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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。' G5 m$ e2 Q3 n' X" U; {& k

8 o* ^' C$ S! S" f; d6 i

; B3 k8 K" O8 q9 G; usocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
: }  a, y& g/ @. t9 q8 g% r
' u. J7 z: E  k/ c" x( z0 O' g

) \: e3 s+ x+ w6 UTCP协议, \, h( b9 l2 n2 m
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- @2 `$ d5 i: ~6 Z7 X

5 Q" N  k) }- A* C7 [% ?
7 g8 v3 I6 u. m/ @
关键词:三次握手,可靠,基于字节流。8 g5 @8 ^: P' y

$ v/ s3 W) x' W

4 ]5 Q2 j  N) v2 K& |" f$ ?可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。& \3 t2 G' l  p, e8 I

) P4 V" E: ]) w2 t4 _TCP服务器端和客户端的运行流程
3 B8 m" E, Q9 j+ D7 j* o3 z# D+ L
) r, {( Z  Q/ {. |+ U9 f$ D& N
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ q4 V, u9 S; H& P+ w9 m: I& m
1 b! P% T0 z% [% m, B6 I
1.创建socket, E9 ^' Y; U) g  |
socket是一个结构体,被创建在内核中
+ a/ |5 L# D- \: x sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
% {! P+ L' n: i8 O/ P% X8 X; v: E& J, m( [7 l( X$ L/ p( F; U% S
: B: _; s, i5 H- B# J5 l
2.调用bind函数3 ~9 f* J- L# x5 N; \
将socket和地址(包括ip、port)绑定。: z% `, G% B  }/ i% G5 l
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
& b( t2 c! H3 i9 b% r: l struct sockaddr_in myaddr; //地址结构体0 A9 o: F' j) z# n& J& p
bind函数% c. T1 D0 u) f: U( V4 `- _
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* s2 G2 y% u2 Z( ^7 E2 W

5 ~. `+ _0 _0 F9 ?1 \8 t
8 n, A  K+ O9 d
3.listen监听,将接收到的客户端连接放入队列- {% V  A8 A: Q0 }
listen(sockfd,8) //第二个参数是队列长度
5 p& n/ o$ z2 U! b
( I% B; r; i3 s1 R1 p
7 k, r& C" w& \/ {! r& s
4.调用accept函数,从队列获取请求,返回socket描 述符
6 g4 `) d  g( K5 C8 A& I  如果无请求,将会阻塞,直到获得连接8 F+ _! p: C4 |+ p- d: V) ]
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
2 \$ K5 A$ V" k9 B! k7 E/ }8 i* Y  R+ ?8 w6 N5 D
% i: N( `$ J: L6 p/ ]
5.调用read/write进行双向通信
* j- L6 T+ j! j; I
# d/ a* k; L0 h8 x6 K; |& q* h
2 o6 P7 Y! d, J: a
6.关闭accept返回的socket
3 b/ P4 j; h  d. n& O% D* e( D  close(scokfd);
8 F* p' c6 y7 R$ @  r
* P, u; V! w1 X$ n8 A) g9 }
$ ?/ d, r5 }" ~8 J( B! ]. U3 ~

6 G) b. X& I8 _& M/ b+ ]
* M" v9 H/ {% S& e1 c% ~
下面放出完整代码
0 ~4 J) Z8 u- j
/ z+ s, p7 v5 f' t3 s/ K
  1. /*服务器*/
    ! b0 G+ u* G! L7 ]! J) S
  2. #include <stdio.h>
    & C- l; Y- \# w+ b- m, r
  3. #include <string.h>' E/ [: r; n( C* P& l# U2 v1 h
  4. #include <stdlib.h>
    ; a1 E/ u- T* ?
  5. #include <strings.h>1 l3 ]4 [9 p; @6 k/ v& K" \
  6. #include <sys/types.h>! R9 v( {! `) V( l  s7 @
  7. #include <sys/socket.h>
    # O! j8 `+ O. _) d- F% d& B% n
  8. #include <arpa/inet.h>
    8 x6 C0 ?1 O1 U
  9. #include <netinet/in.h>
    * Z" u1 \" o  b
  10. int main()1 b7 f4 ~: O! X2 H5 G1 e) ~
  11. {3 ^- T/ Y$ x0 d" u, t( C/ {1 x9 P
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    * L4 a- L2 `! `  P3 Z7 c* W" \
  13.         if (sockfd < 0). Z/ l2 n* E: D! _
  14.         {: ?5 p2 U6 }3 _6 K
  15.                 perror("socket");' w+ m3 t0 E" o3 X/ E
  16.                 return -1;' \" N3 W( L' P. {8 N. p, |4 a8 b
  17.         } //创建失败的错误处理
    ( D& E/ [* y: Z. I
  18.          printf("socket..............0 i: L1 N; L8 j/ J  V( n4 ?, u
  19. "); //成功则打印“socket。。。。”
    2 \4 f) x3 u- ~' o6 Z) C' {: a
  20.          . A% C/ n5 T0 h
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体- }& M% [$ Z  D% |8 w& f
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    5 g' ~8 ]+ A% C# ^) R$ X
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    9 I, }7 A9 b! ?
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ; M" u& ^; I3 W4 o1 x  l  F
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    . a) L2 Z# l8 [$ p

  26. - ?( k( o- z& W, \+ Q2 R' K2 C" G
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    - ]" A& F. e" c# V7 l/ k' B2 n
  28.          {9 y6 E1 W+ W3 \
  29.                  perror("bind");8 Z% m: Q) t  S
  30.                  return -1;
    ) [% k/ {# t5 @9 E8 v2 t
  31.          }- M9 d) i$ S5 u* x
  32.          printf("bind..........
    ! R7 c; @/ _& H+ T
  33. ");# l  p- s" r/ X) w: m! q

  34. 0 A# w4 d0 ?- J% i/ p
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    6 [/ O) A' k' L  g5 \$ s! f
  36.          {
    . `) S. C! d: U" E4 _' }8 C
  37.                  perror("listen");! ^+ w# k; X- u. {
  38.                  return -1;
    # E8 v/ I  A8 P& p3 T
  39.          }' Y" p' @, |" {( j7 G: O* V
  40.          printf("listen............9 H+ ?' t; m- t: L+ H" m- v+ m6 p, B
  41. ");- I2 U& ]- ^  `. S0 c( c
  42.          , n, t, T: k1 E" y
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    / v' l2 {# {1 j' I7 j/ z, Q
  44.         if (connfd < 0)
    4 m+ ]0 Q3 K" b# ~
  45.         {
    5 G) V# _8 d. B! ?- U) y
  46.                 perror("accept");
    : o" [7 I  L5 q' U/ u. [
  47.                 return -1;
    4 c5 }9 V. v. _  G# i
  48.         }& B% m$ H: E7 a% n8 ]5 L
  49.         printf("accept..............
    % t1 e0 q& S% R# O& r
  50. ");" n/ ~& b* B- P# ~8 a5 R) p* z! i
  51.         char buf[100];//定义一个数组用来存储接收到的数据# w+ k3 n5 r( h
  52.         int ret;
    ) N9 w" f5 ?9 U8 G
  53.         while (1)7 H) Q7 N, b4 f4 n
  54.         {( c' I5 y7 g0 x6 \+ x4 j9 T9 z
  55.                 memset(buf, 0, sizeof(buf));& V+ I- u2 @! a! \; l4 @
  56.                 ret = read(connfd, buf, sizeof(buf));
    ( i+ R- ?5 l5 d3 \' n8 f8 g
  57.                 if (0 > ret)0 z0 Q( O! L  H" w7 H
  58.                 {7 C- o$ P( S: E" ?
  59.                         perror("read");8 O6 P- ~0 b* E$ ?- F2 T) d  y
  60.                         break;; o+ o) e) y* ?; h) B3 E7 H
  61.                 }//执行while循环读取数据,当& `. }5 H' ^; M: I" G. L" Z0 b
  62.                 else if (0 == ret)
    4 u% d, p  W, [, ^
  63.                 {) ~" r7 I+ U( S& Q9 ?" b
  64.                         printf("write close!
    " [! p  W$ |/ Z, o/ e  }! I; W7 B( h
  65. ");
    - n# ^) e( ~7 Z. r) d
  66.                         break;
    ) f5 A7 b% u6 W. W5 t
  67.                 }
    9 V9 j) o7 X9 N
  68.                 printf("recv: ");
    : \/ g% {& u& _1 z6 Z
  69.                 fputs(buf, stdout);//打印接收到的数据
    8 ~" Q  c2 \% R
  70.         }+ V2 c6 e% j4 P* T  v# s. y
  71.         close(sockfd);//关闭套接字- B- y  s/ L: o1 ^3 H( [) S
  72.         close(connfd);//断开连接- R0 p3 w2 j5 S& N8 ~. Y* h
  73.         return 0;( o5 E. }- I( G' P! }
  74. }
复制代码
8 O1 a" A* Q1 H  n- x# d
, k$ l0 [9 p* }) N# r, ~( e
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    + V: {) ?/ a) V0 D! ~6 x
  2. #include <stdio.h>
    / o- T( o1 ?, G$ T1 z$ j% |
  3. #include <string.h>: t. a! S* V6 M% X+ G
  4. #include <stdlib.h>
    8 z5 k; y; R0 L) G' q1 q, @
  5. #include <strings.h>
    . t" u* B! u; ]5 ^" b% K
  6. #include <sys/types.h>
    , L- t+ W  E+ q
  7. #include <sys/socket.h>1 @; W0 X* v5 J: |
  8. #include <netinet/in.h>
    " M5 `' c  V. j: R2 E
  9. #include <arpa/inet.h>
    7 _% r. g2 h/ \. V8 O3 L8 I" F
  10. int main()9 g% x/ @1 r, |
  11. {! K, `6 i' n  O& D; K' I# h: b% B
  12. int sockfd;
    8 W* z/ L1 t* E2 f8 w+ s& _1 X
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    / J/ b$ u5 m7 p7 u5 b
  14.         {  l% h% k5 ^* G1 R8 w0 L
  15.                 perror("socket");6 y/ Q: x: v# D/ V; q8 ]6 c
  16.                 return -1;" M4 U5 a* Q: x( o+ T* S" G
  17.         }# g$ o5 Y- o# G, E, f# x
  18.         printf("socket............ u" O) C) J; w# s
  19. ");
    $ ^" u- V4 V$ {- s$ k
  20.         
    " L+ x2 c) c. }8 H$ e' b: M* _
  21.         struct sockaddr_in srv_addr;; `" n- [( f1 D; K8 ~( f1 @
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    5 t; p! l" F/ _& s$ ]
  23.         srv_addr.sin_family                 = AF_INET;# k, v; B/ o$ g8 h
  24.         srv_addr.sin_port                         = htons(8888);
    ( c6 ^0 `9 z% L* P/ U
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");1 n9 P. Y) B5 l8 {
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))& o3 ~  i; @' ?; f: c' @
  27.         {
    5 _: ^; e% ]  P2 _9 ^& y
  28.                 perror("connect");; ^; {5 ~$ ]4 v1 i& M
  29.                 return -1; //exit //pthread_exit
    4 d+ N3 ~% h5 Z
  30.         }4 W$ |0 I  t* v! D
  31.         printf("connect..............
    7 i* X; g6 C6 O0 B
  32. ");: k1 L9 }8 C* {7 ?7 M
  33.         char buf[100];
    / Y) w9 j  D! E
  34.         int ret;
    + G' x, P- r2 v5 Z8 Z& j- U, r$ k
  35.         while (1)
    + \5 l/ F/ y7 G% h; R  G
  36.         {# Z: y0 E8 P2 ?8 f. l) `
  37.                 printf("send: ");& `& P3 \% x5 F% w
  38.                 fgets(buf, sizeof(buf), stdin);9 S: Z( z0 {5 L4 Z4 e
  39.                 ret = write(sockfd, buf, sizeof(buf));
    7 F" s; d  n5 G" s2 \& H
  40.                 if (ret < 0)- W8 g) X; c% e! G+ n' \) n
  41.                 {
    1 h. {" H9 t2 ~
  42.                         perror("write");
    5 O3 n3 q; M$ A8 x. n9 Y
  43.                         break;
    # Q* |6 B0 D, h9 C7 u. y1 ~
  44.                 }; t2 I2 S0 z- h6 L" h
  45.                 if (strncmp(buf, "quit", 4) == 0)
    ' d/ s6 t) Z+ ]5 \
  46.                         break;
      o* l2 \. m' x" Y
  47.         }
    ' k& o1 `; i; {2 \, `
  48.         close(sockfd);$ w4 N' |0 Q4 H! D/ V: E3 B& F4 P( [/ [
  49.         return 0;
    4 W- S9 t/ t2 @
  50. }
复制代码
$ N0 c$ J2 h  m2 f
) n, T; T* R6 I2 `7 R) [' F
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 09:49 , Processed in 0.125679 second(s), 22 queries .

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