您尚未登录,请登录后浏览更多内容! 登录 | 立即注册

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 18390|回复: 0
打印 上一主题 下一主题

[C] 自己动手用c语言写一个基于服务器和客户端(TCP)

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。. t4 j7 k2 F( ~3 n- _
2 w* k1 \* X0 n! c  `
7 e  f/ T& N; M: h# s, ^" p
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。4 h+ J8 X2 @4 V2 ?7 j5 b& @
1 K& |# A+ W& q4 r* O3 F9 J

& D! j! ]/ H; d3 c! S2 m2 sTCP协议
; B5 j# l- E- z: E2 o$ gTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 w# l/ L5 A4 k1 s, g% C; g! l! U+ f: w
+ U" o- i  V% v( ~$ n3 S5 v
关键词:三次握手,可靠,基于字节流。4 A5 E' q! {, h0 f& g) X

' R/ h3 m' G, J6 P- M5 x) ~4 k  V
/ b( n: j7 |4 C1 J( d
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。5 t: a7 ]& q$ R3 g2 h

$ B6 q0 c) T& d. \. oTCP服务器端和客户端的运行流程
3 P, B/ n4 _! V" }1 }
" n" {) b% k. E8 R) ^

; [. X1 v) e; f& I0 g. w0 R+ X如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
6 I9 B  S. s/ m0 M
2 e: F" |% Z+ j# N
5 G  ?3 h* W; v' ^* Q' A
1.创建socket
5 E" w- B$ n; T3 X! f, q! d" z socket是一个结构体,被创建在内核中! B& I  F2 ^# t, M* {
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& J1 ]" u0 q/ R9 u) l- y5 _# E$ J3 p6 `: f
8 v" y6 a# c, p* r* ^! z
2.调用bind函数% a) I& C% Z" v9 h
将socket和地址(包括ip、port)绑定。/ _3 s1 b3 o; g. J
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
7 x& b( }- O0 }% r' ~( |% [4 l$ n/ D3 o struct sockaddr_in myaddr; //地址结构体$ I+ V. x  L4 ^+ D
bind函数
: K2 s9 q# U- q" L. Q bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))5 S  ~$ V) s" ~% y+ g

- s: C1 @5 a( {$ i2 e% A
: g" @, U2 u8 X
3.listen监听,将接收到的客户端连接放入队列' _. }. c5 ^/ `/ E+ v: g
listen(sockfd,8) //第二个参数是队列长度8 r* K; H# {. ?" }6 D; v
+ C' z' I' z% p* o9 t6 L) u0 @
/ }+ m( v) N! v% q4 I# B
4.调用accept函数,从队列获取请求,返回socket描 述符
" O, L- V2 l, i* A: P  如果无请求,将会阻塞,直到获得连接
9 b+ T$ i3 d) f( l7 X  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 y8 y1 l8 }" z2 K3 I5 k# p1 O

+ K7 r  |9 V3 A1 Q& g$ e1 S
: }4 N" W* c& _' Q9 v! g
5.调用read/write进行双向通信" q  K# c$ r4 C6 H' z
0 ?5 M7 p# Q* d$ {. d& C
8 [/ F5 f/ p+ r9 J
6.关闭accept返回的socket2 i& ]/ t* @9 R( j: {/ \# O3 T
  close(scokfd);+ M8 o4 F4 [/ X5 W
) x* F: x# K+ _% u2 W' S0 g

4 j/ g3 F' V1 N* J
% Y2 Q/ p9 L, D- ]! v
$ x8 ^8 [0 C, h+ ]5 Y
下面放出完整代码
4 l3 O# y' p' b9 C9 ]; Y# b) c( k6 G% R. F+ k
  1. /*服务器*/
    - z+ f# M) c6 m' X4 F" B
  2. #include <stdio.h>; S3 S& K& r5 W1 @
  3. #include <string.h>% i% B% j' n3 e6 r  ^7 L# E
  4. #include <stdlib.h>  B* R+ i8 P% u- o
  5. #include <strings.h>
    * ~% Q. ?" [& D3 ?
  6. #include <sys/types.h>
    / L! x  x% v5 _3 q' ]* I2 b" n
  7. #include <sys/socket.h>
    / v0 A, H+ J" v. S6 }1 _/ a  N
  8. #include <arpa/inet.h>% n( \0 o, ?; k' w: ?0 t# F. j& p
  9. #include <netinet/in.h>" ?2 z+ |- @! e! `
  10. int main()
    1 p3 L0 j+ F, v( w) N" s
  11. {. H/ f2 Z4 M1 U: V! U' R2 B
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字% M; V7 i8 ^. [' b5 n
  13.         if (sockfd < 0)" H1 C8 t4 g6 N$ j1 a0 s
  14.         {
    : c1 |) ?# g& }1 S7 K  q
  15.                 perror("socket");
    9 v% D" ?; l/ X  c- G) s" {
  16.                 return -1;& N5 s9 n* p* C5 |' M
  17.         } //创建失败的错误处理% O1 }% }! n  D3 G2 _- W- [' V* t
  18.          printf("socket..............: ?! Z& C: g: l  G: X
  19. "); //成功则打印“socket。。。。”( o: C( `2 D* M% o
  20.          - w7 \2 {1 }' v' ]
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体* B+ z' W: H7 M- E
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)6 u. F5 j3 E+ i2 f- n
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型9 j6 O1 G! \: q* E! _
  24.          myaddr.sin_port                 = htons(8888); //选择端口号+ O* z3 q! I/ Z* g, P% `
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址5 E' w3 W+ c. ?0 |$ y
  26. ( {* w/ l8 O+ b: z2 v& s
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    + [  A7 w, I5 H% _6 z9 \0 l& I
  28.          {& L- r$ a. E& `$ h
  29.                  perror("bind");
    + \9 n8 H* C7 W. y; `$ y1 N& S
  30.                  return -1;
    % P# z+ v2 _, o  J% E! K$ |
  31.          }
    2 v( @: j3 B" ^
  32.          printf("bind..........3 m' B0 }: I! l6 }# B5 D
  33. ");
    6 ?% |' i$ l/ ?
  34. 5 v% A" k# i" d( v5 j0 T1 h) J
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听* [; f/ H+ {7 _: P; o
  36.          {* G" n% a) ]% J/ i( y) {( a
  37.                  perror("listen");3 D: c) E5 H( a
  38.                  return -1;/ e4 u5 l! D" @& m% W
  39.          }  L6 q, O  t) L# _# `/ n; w/ C
  40.          printf("listen............
    - Q0 h( D1 M8 N) C) x' M0 `
  41. ");( C, g" o+ |. }7 o
  42.          
    - F  ?: A  T2 I0 {; f$ X
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求- E/ _7 l8 b7 A
  44.         if (connfd < 0)
    - c2 U  v; r/ O* G7 ^1 c  t
  45.         {
    2 y5 Z3 y1 j$ U+ H% @
  46.                 perror("accept");
    ( H4 L5 R8 D" H( v
  47.                 return -1;
    ; N! T  q7 Q. s6 g  }/ S  O
  48.         }
    , ]( Z! ?; l2 n4 p- _# L9 h
  49.         printf("accept..............
    0 T$ B! a! T1 A& R
  50. ");
    1 `- }0 u$ `/ ~7 E: {6 I' `
  51.         char buf[100];//定义一个数组用来存储接收到的数据
      j8 z* T7 D, o
  52.         int ret;
    4 p4 M$ e: v+ h  {7 S2 l: ?% @
  53.         while (1)4 S: y3 l& |9 m( a1 b1 x$ d  ^
  54.         {
    % r2 t0 F- c" V8 E5 |+ N
  55.                 memset(buf, 0, sizeof(buf));- w  \$ m8 C: t, v  I
  56.                 ret = read(connfd, buf, sizeof(buf));. U+ A  m4 a, ~$ C, Q/ O+ d
  57.                 if (0 > ret)- i; K( Q) Y% a/ A  X8 b
  58.                 {* g! C) W% T5 Q
  59.                         perror("read");9 W  e7 l- R1 [8 ]- K
  60.                         break;. F( V5 ]3 D: u: c$ u
  61.                 }//执行while循环读取数据,当. Q. P. L/ j, P' J
  62.                 else if (0 == ret)
      j5 N9 a2 R2 K; O$ B
  63.                 {  c) _" e" s' |0 S' i: O
  64.                         printf("write close!
    ' ~2 W2 B8 @) f
  65. ");% n7 l' k; w$ Q, g
  66.                         break;) L9 a: E: M. ?9 {9 v) L
  67.                 }% w) d* v  p5 D. l0 s
  68.                 printf("recv: ");3 ]& R* }( W" Y; I, j: B
  69.                 fputs(buf, stdout);//打印接收到的数据
    - h. C% N, M; i/ ~& ~
  70.         }
    ' U# {/ ?: B5 V+ R; F
  71.         close(sockfd);//关闭套接字+ J. w1 O3 K1 r1 Y" s3 \6 B! R
  72.         close(connfd);//断开连接! b" H1 U3 t: X& S
  73.         return 0;
    7 |' c  [# i) y+ o/ E7 K( m
  74. }
复制代码
  X% s/ S7 P: T, H' x
6 Z* Y& d) X6 {, |8 ~) x
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)0 C. l" a' o0 \
  2. #include <stdio.h>
    & u; p  K2 V/ R8 C
  3. #include <string.h>
    , F( _% T% Q0 g  l+ C
  4. #include <stdlib.h>2 M" [% {. _# u3 W9 W! N4 }# L* ]; T
  5. #include <strings.h>* p6 T& b  u9 z4 j  i
  6. #include <sys/types.h>- V+ L; q: B4 i" P
  7. #include <sys/socket.h>
    : D1 c$ k3 |1 ^1 x
  8. #include <netinet/in.h>
    " f6 e) h# `; J7 u* `0 {2 Z
  9. #include <arpa/inet.h>
    ( A; x) D) A) `: v
  10. int main()6 r; D* T' Z7 C8 F7 A/ D9 G
  11. {8 y' u  @- f7 Y! o5 l/ j; F7 ?8 ]( ~
  12. int sockfd;
    5 d  N( {0 d' a8 t9 v, i  H( [
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))+ x! I) J8 k( E
  14.         {
    ! V1 l6 z2 W1 p8 _
  15.                 perror("socket");3 }. c) y; p% B2 [2 E, d
  16.                 return -1;
    7 d# I7 n+ K. M, Q" W
  17.         }$ x5 P- |, Y6 g8 L/ _, w
  18.         printf("socket...........
    0 s; |8 {- _8 Y  j+ e
  19. ");7 E, g) V5 S1 {  H& Z2 I
  20.         $ o; e7 H' |" T
  21.         struct sockaddr_in srv_addr;% t4 y! h; M( b6 e' D% S
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    2 j* E. t: B7 f1 _
  23.         srv_addr.sin_family                 = AF_INET;; Y  @4 T5 A! R+ T, g
  24.         srv_addr.sin_port                         = htons(8888);
    , g5 v0 d( e6 ]- E. D% V4 ^
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ! q! V/ {6 K9 @* g
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    0 N( p" i  U, T" i
  27.         {
    6 [! D: U6 \6 U; m! L
  28.                 perror("connect");6 H- Z# i$ M5 \$ J0 j1 G9 [
  29.                 return -1; //exit //pthread_exit7 v+ B" e9 Q  j, _+ D
  30.         }2 R  E3 ^0 a, c! {
  31.         printf("connect............... o8 W9 [- t' R2 I
  32. ");
    ) Q, n" i8 L+ f. F# `
  33.         char buf[100];7 _% _. e9 _, \  w
  34.         int ret;* Y/ ^) d9 z4 F, B; u( y
  35.         while (1)
    - D# i- O9 l3 }3 }
  36.         {5 G) R& e- {9 g) k6 q! z2 O; [
  37.                 printf("send: ");- T5 f1 g, U  W: z8 g* H
  38.                 fgets(buf, sizeof(buf), stdin);
    , u8 n& J" A7 G/ |; `# I, |
  39.                 ret = write(sockfd, buf, sizeof(buf));
    : Z) }- e/ e/ l" ~" p
  40.                 if (ret < 0)
    + g* b, [1 K: P
  41.                 {
    * F* q3 j) o7 k+ Z
  42.                         perror("write");4 \& Z. C  u- v" l- B, ]) m
  43.                         break;
    " r5 q: d. w4 e
  44.                 }
    * D2 {: K5 r9 H7 ^1 i
  45.                 if (strncmp(buf, "quit", 4) == 0)$ c% E8 r$ U$ z7 H
  46.                         break;
    6 h. A4 k" a+ R0 W, f8 x4 o2 c+ |# w
  47.         }5 Z1 a! F% x" M+ W7 l- U- ?
  48.         close(sockfd);0 w  w9 d9 X9 f
  49.         return 0;
    ( b7 }) e6 z: r- c- E
  50. }
复制代码
! G" h6 r# n1 ]( l" i3 E) X! d( a
$ h+ J/ |" M' e  N
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 03:08 , Processed in 0.063269 second(s), 22 queries .

Copyright © 2001-2026 Powered by cncml! X3.2. Theme By cncml!