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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
; A( l5 @+ g, _; R' y) w5 e1 C+ a; G' C7 n- Y5 o/ S
! ]5 g- M4 D/ c
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。; B# b5 ~) ~! _* W6 L% T5 [

# I0 T# |8 `/ ~4 |6 c" F4 Z, R1 e

' U- k. q* l, e5 Q2 W, `' _TCP协议% W" l9 }& S, }3 F
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。, s5 F, Q" u- D8 M
9 }: G3 U; t" ?; M- x
; V, M: U9 w" t# q/ F8 A$ i+ q
关键词:三次握手,可靠,基于字节流。* `9 F; |# y. h
2 i6 e! I9 e) x
! c$ ?. F2 \; [
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
5 Y; S) P$ D8 k6 d6 l* b0 o8 J $ T2 n" c, ~5 r  b- K' I
TCP服务器端和客户端的运行流程5 x* y' F7 S$ t- _! N

7 E# L" H# j4 ?2 [% `9 j, ^; }

: I, K% S# K+ @; Q2 x3 a如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
: J  A" u1 ^9 E$ y9 j+ S! m$ I3 P9 c8 u$ L; g6 ^6 f7 H) _3 C/ h

0 |1 v" j7 \) g/ h1 b1.创建socket: N9 C1 K: d, u
socket是一个结构体,被创建在内核中
7 U' i2 K$ S6 p4 _3 M sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 B/ A# @+ T- w) x$ Y' c* m6 g. H# ?6 ?% w

. l' l  S0 H. m2.调用bind函数
7 k& w  A5 K/ C3 R1 M5 S8 D- D 将socket和地址(包括ip、port)绑定。" |  T0 X, }6 L3 m0 K; K
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
5 x) X3 X( {2 ]5 [7 O' G+ W, s struct sockaddr_in myaddr; //地址结构体
; s6 }- j5 i0 ?! z bind函数9 I+ ^8 G  n& q7 t; n+ B3 }  `
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% t; Z+ R5 j0 \
+ ?6 {+ y, c4 Q
* o& D9 p6 ^& D$ [
3.listen监听,将接收到的客户端连接放入队列
7 n8 L7 g( M, j  r4 | listen(sockfd,8) //第二个参数是队列长度
4 M. P. }4 U: O  b' U: u9 h6 R% I4 R  a2 ?3 q1 R
& o6 F) C% v* S4 p% l4 T
4.调用accept函数,从队列获取请求,返回socket描 述符; W9 `5 k# ?$ \+ `$ J
  如果无请求,将会阻塞,直到获得连接
" U# t% a" z! u( v) E( @, B1 i  K6 D  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数2 M$ D# T) {8 g, _% q5 e- \

  F$ e, E! {# U; L. n6 U/ X9 V! p
  g7 v! c& N8 P2 N. v$ O9 L
5.调用read/write进行双向通信
) C6 s& x: @  E5 P" V/ z' m' Q0 @3 ]& Z# q: h& I

+ \' g0 T2 m1 G; `& L6.关闭accept返回的socket
0 l( k. ?; D, \1 P5 z  close(scokfd);
4 f6 @6 ?9 o5 b- {5 c9 q, J  b' H, C, ^
4 ]% t2 B* K% l6 s- F5 {
" i  f7 g* d9 O1 N

* C' `, Q2 \  _0 w$ H& [3 ~. P4 N下面放出完整代码# T! W, `+ K8 |& ^* A  `

6 E! L* M4 @5 c/ o$ D
  1. /*服务器*/
    6 q6 ~  f5 g/ c% Z
  2. #include <stdio.h>
    * E% y% ?/ s! @" Q0 M. V
  3. #include <string.h>' E/ \5 h% N# f6 Y5 q
  4. #include <stdlib.h>
    4 _/ K7 a! v% }  e
  5. #include <strings.h>
    $ l, g$ R2 J5 T/ c7 q+ H
  6. #include <sys/types.h>$ O  ]: Y- e5 N) u) G  X
  7. #include <sys/socket.h>
    ) v9 Z, M3 }9 D* v, o" t
  8. #include <arpa/inet.h>+ o+ d% o8 A* w0 G) d  z$ r
  9. #include <netinet/in.h>
    ' y" B: T8 P+ x1 u8 D; W
  10. int main()
    1 ~0 O' L; [  F9 m% X  I0 T
  11. {
    7 J0 O2 j) O- s" k+ U+ A  S- t
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- ]9 y2 g  m  F
  13.         if (sockfd < 0)
    & x+ }4 b5 T# p5 j
  14.         {0 ^) W& S4 W5 E( v6 u
  15.                 perror("socket");- C6 x, L' c4 V  U$ M( G0 v
  16.                 return -1;2 T" C% A* u1 s+ Q5 {( o
  17.         } //创建失败的错误处理3 [0 L9 s7 J* n
  18.          printf("socket..............3 k: ~+ W" x7 R$ M6 Q6 i
  19. "); //成功则打印“socket。。。。”2 q/ @( \* }$ D' t
  20.          1 E/ j- x; y5 k
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    2 Y" H0 H) i# \+ H
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ( h1 F8 J8 B( c
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    " w- L$ z' o, U2 L
  24.          myaddr.sin_port                 = htons(8888); //选择端口号; `8 K3 K, m6 p8 H8 Z% b" l: Y
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址& i3 u4 @, p8 S
  26. / S0 |' L% N% D
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    9 S# M. ^# ?/ e6 c6 v% o) G! v
  28.          {4 {  I7 x- p4 s% E( M
  29.                  perror("bind");
    9 K* W/ ]: W+ C, m, [
  30.                  return -1;
    / l; L7 u) ^0 C! I/ c
  31.          }
    ( u/ ^. b, L# V
  32.          printf("bind..........  D6 e2 ^0 ~- W0 r0 a; [# g0 c
  33. ");
    5 H; ]4 y' V7 d( W
  34. : W% A# T6 g# O3 t, Z
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ' j% w! [: d0 e
  36.          {
    + k% z: e* S2 r3 J6 Q  T% E2 s7 n
  37.                  perror("listen");. X1 D5 r, g' [' F
  38.                  return -1;" \1 `8 i2 E8 s) c# m. e) G0 P  ]
  39.          }: x& w+ T. w8 ?- ]' u5 Q+ i' u
  40.          printf("listen............4 S8 n  u3 z7 k  U; j7 r4 V
  41. ");9 w8 ?: [- i: N: m
  42.          , z) r8 l1 A: W
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    2 C" R, P1 s  q$ ]" \& s4 E! K
  44.         if (connfd < 0)
    ( F% ]* s  t( b# |8 s( X
  45.         {
    ( v  j# K$ L7 d8 ]
  46.                 perror("accept");
    1 R: {) Q. ]! p& H. ?$ v
  47.                 return -1;
    , |0 W7 B% f* C( o, K  @9 a* A2 [
  48.         }
    ) q9 U! q: M9 R  W
  49.         printf("accept..............
    / ~* Z& L" z" x0 B  m% E7 [( t9 H
  50. ");
    , M8 F- x% U' @" {6 J$ T1 M* v
  51.         char buf[100];//定义一个数组用来存储接收到的数据  U# b+ a# X- \' Z9 v- Y+ B
  52.         int ret;
    ' ~! F* e. `( O  S
  53.         while (1)/ z& @9 E# [1 L! q4 T3 K
  54.         {* ]- _5 H- m* K9 t( w' f' Y
  55.                 memset(buf, 0, sizeof(buf));6 ?, C4 z% y2 n* j9 g9 A1 N
  56.                 ret = read(connfd, buf, sizeof(buf));
    - K9 e6 |! d0 \: q* Z- l8 I+ s, |
  57.                 if (0 > ret)
    * K5 J5 S! G. \9 Z" s7 l7 u
  58.                 {
    " f7 L* g$ Q  }& \
  59.                         perror("read");" G4 {+ n+ r0 Y$ e
  60.                         break;" ^) O8 P; x) r/ U: E: y$ H, u7 @8 _
  61.                 }//执行while循环读取数据,当
    2 V( e% ^, N- D  p3 [, x9 ]
  62.                 else if (0 == ret)0 f' P8 u9 L$ M5 g. ~$ |
  63.                 {
    ! `6 M* ?* _. G/ [/ k9 u8 i+ d
  64.                         printf("write close!
    / r& ^( N, D4 b, L7 K5 f2 o
  65. ");
    4 @/ F( t$ w+ R7 U
  66.                         break;
    - Q/ l) |! k" c" i4 [3 `0 u
  67.                 }
    4 }+ z( n6 _. I+ S' q( e2 {  S
  68.                 printf("recv: ");
    . h5 z3 t4 h: \9 Y' q3 x
  69.                 fputs(buf, stdout);//打印接收到的数据* h  S# g: B- F
  70.         }& H6 t: y  F/ t, K  A  f" `
  71.         close(sockfd);//关闭套接字
    * e+ v& n1 i; p
  72.         close(connfd);//断开连接: P# }4 D; i; f. `4 d) C& h8 K
  73.         return 0;
    $ R) W" ^6 [/ L; O" A4 D
  74. }
复制代码

& R7 @0 S; z1 o- H) |, h0 w* i6 D8 p8 \( l! _/ R: R6 \
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释). S2 T4 B" \- q: \; A4 o
  2. #include <stdio.h>. d  K6 v8 z  u  [8 |
  3. #include <string.h>
    $ p6 Z  J0 e- P# y
  4. #include <stdlib.h>
    - ?" r( s5 a& w) ]0 A  }
  5. #include <strings.h>
    - e1 X) r2 x# g6 J/ T, c- {  j
  6. #include <sys/types.h>
    ( `- j  @6 e% v5 p! X( b
  7. #include <sys/socket.h>. E1 {4 Z( H3 p4 P' q3 ~8 F- I# D
  8. #include <netinet/in.h>
    + a5 k1 q) S' v; a& X1 v+ \
  9. #include <arpa/inet.h>
    8 o, Z' S! I4 Q1 C0 W! V0 x9 |
  10. int main()+ {  n6 b& Y8 v1 h, U( e- p; W6 ^( ]
  11. {3 |, w) v4 l5 y& ]
  12. int sockfd;
    % \) C( s7 G: E7 u3 T
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    . b) R, E- u5 u6 e* x# E! m
  14.         {
    ! m$ F4 t- f/ H' M4 T* M6 e
  15.                 perror("socket");
    : G. i+ F  v6 x- S# O2 x, q
  16.                 return -1;
      \. ?# q1 M( P) l5 P- D3 S' H
  17.         }1 x6 v# j# v& B6 N* M3 }$ T5 E
  18.         printf("socket...........
    $ z2 ]9 d: K/ z, u
  19. ");
    3 g2 l. ~6 H: ]; e; U
  20.         
    ; v8 \1 M! o3 X
  21.         struct sockaddr_in srv_addr;
    / F: k6 Y/ P, H  \% p$ F2 r
  22.         memset(&srv_addr, 0, sizeof(srv_addr));+ x' R5 S9 V  r9 n" c" ~; t2 v
  23.         srv_addr.sin_family                 = AF_INET;
    0 W  `0 m+ J$ G$ O
  24.         srv_addr.sin_port                         = htons(8888);  J- A8 t" }1 U, O& w
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");% V6 W. w" U' j- V; e+ Y! f6 U4 j8 h
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))), f" _  P% N/ u. k' M3 I+ D; z
  27.         {9 c2 w# F; o# |2 C
  28.                 perror("connect");
    # L* j) m# X7 ?' q" A+ ^0 A
  29.                 return -1; //exit //pthread_exit2 l! S: C4 J3 U6 |# J" Q1 R
  30.         }
    # a5 L  r  S8 u% g5 c
  31.         printf("connect..............
    " x' w# d3 ]8 A& {3 P
  32. ");
    1 `2 g3 V/ s1 y+ b0 S7 K) Y
  33.         char buf[100];
    1 ?) T7 p9 J  G; c
  34.         int ret;6 ^; o: |# Z3 s8 p2 C
  35.         while (1)( G& h5 v  G( D6 u0 u% G! R
  36.         {' [: }( l6 ?4 d5 V: u' c
  37.                 printf("send: ");$ n, a/ Y: A' X; ?/ v0 k
  38.                 fgets(buf, sizeof(buf), stdin);: h3 ], k) K2 `
  39.                 ret = write(sockfd, buf, sizeof(buf));
    3 M, {8 s. V2 P0 p  l
  40.                 if (ret < 0)9 Q: _1 f+ y& i. X. u$ S6 j' o( m: z
  41.                 {
    9 G& d4 |6 [3 W& \
  42.                         perror("write");+ H' f9 b, K& h, |3 c
  43.                         break;1 E% R/ P" c0 E! y# v
  44.                 }
    9 U5 T" X1 s( D% ?. t
  45.                 if (strncmp(buf, "quit", 4) == 0)# ]9 a/ h4 y% s
  46.                         break;
    ; h6 V+ O/ Q# [" ^* n. B2 a6 r
  47.         }4 O. \- R& N/ Y  N" q% n
  48.         close(sockfd);
    ' r+ P6 s4 m! p) G) F
  49.         return 0;
    0 q' S8 G. j- u' B) k1 v0 d
  50. }
复制代码

' `) n2 N, S5 i. E+ d* g' H* }& S" {% I3 |$ v5 Q, q5 W
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-4-30 22:49 , Processed in 0.056970 second(s), 22 queries .

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