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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
% p& D0 w. x5 N# {- @0 a( Q" u, \% r- M* {1 {1 N. @+ b
' E6 w. m/ O$ R- \3 O5 |
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
4 e8 C# P: f/ [# [+ z2 \: f* a( S$ d* T0 W# u; {. \
4 H, G0 E9 N4 O6 L  J0 h( Z2 x6 K2 J8 T- B
TCP协议# n/ C- ^2 [) y2 X, L  w" }( N
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。: W8 T& J: L) ~) r9 \+ U3 {

; F# T# w$ W( j9 x, Q

! a& [+ |0 I' A; s关键词:三次握手,可靠,基于字节流。
% B: V7 {* j6 u; O" W/ j! U/ L: b, r5 H$ s/ J: F* F

* L0 P1 f  G4 B( y. \" x: k' Y* x可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。- {0 q1 G) V; R; g5 {
" _' Q+ l' g9 q& ~
TCP服务器端和客户端的运行流程/ c6 O9 K/ B) }. I9 G! u9 Y

, g* o% P/ O) m5 m) x
2 p( ?% c/ l2 o: v1 ~
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?0 c7 Y1 r6 w& P  Z: P7 P6 Z2 z

& h# N$ f! o# z' w& R8 ^

# l/ w$ d  [3 _% u1 e5 D" B: C1.创建socket# v" d- [  B* `
socket是一个结构体,被创建在内核中! k: x; g2 I! J4 b
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ E. m1 l8 r+ q/ w4 r" s

7 l. G/ v( j7 D

$ Z4 G3 M7 ^1 O: `2 ?! }" q" \$ `2.调用bind函数
9 s; u  u8 J' L 将socket和地址(包括ip、port)绑定。6 _, T: s# k, E% R
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) @* E+ f4 W0 X! e3 N
struct sockaddr_in myaddr; //地址结构体
9 z# K* d! h% u$ B+ i. S bind函数: [0 ~  O/ j: X0 H; u4 n( p
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
. v! [1 V4 I& g& T8 Y/ a2 v# d( n4 f" F/ I

/ f# A8 P8 D1 m# l' |1 Z3.listen监听,将接收到的客户端连接放入队列
" D! E2 e" N/ i2 P% o+ E( W$ @ listen(sockfd,8) //第二个参数是队列长度
0 _/ @' [9 C& {$ X7 `3 j
0 R0 e5 j0 d3 X& s0 l* W" t
( n  g2 G) o# ]( x& ]/ g3 o
4.调用accept函数,从队列获取请求,返回socket描 述符3 z/ V3 R! ~# O; e. ~/ T, `
  如果无请求,将会阻塞,直到获得连接
' {- A: B! h2 d9 @" f9 h  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
' d) Y  e' O; |4 m; P
. d9 M% P+ G. A/ V2 z# L* F# E( i% ~9 T
+ @. M7 g- A9 D# ^/ D7 w+ R. _
5.调用read/write进行双向通信
& Q' J( _! W' h8 q6 d1 W0 ~" w! M4 R% M$ Z# P- m& Q

1 H0 ?; P# h' Z8 N6.关闭accept返回的socket3 h  `4 t( ^& L1 i; s! {6 k' j( {+ q/ X
  close(scokfd);7 o8 G5 \" t- R) C7 ]

; C+ g% |! R' @  ]; l+ m
7 r/ j, e3 ~% I, S+ L

* L& W; @3 g$ x  Z, z, ]! h. k
2 t: u/ I* @0 F6 ^" [$ ]
下面放出完整代码
: I7 g; x5 a; A9 }4 o) V6 L) L6 Z
6 b$ @9 i! f. X8 x2 H: r
  1. /*服务器*/0 R# A0 f) V- [9 d1 t
  2. #include <stdio.h>8 t% W: Z) c5 a9 ^/ l, H
  3. #include <string.h>
    / E- M3 u) {& ]) P0 w0 Q8 \& x
  4. #include <stdlib.h>) |2 S4 J' B! o7 [/ z4 m
  5. #include <strings.h>
    # u- c: m8 Z% D  S
  6. #include <sys/types.h>' h5 r2 s8 |: i- w4 D
  7. #include <sys/socket.h>7 N" G/ N& ]) L+ Z$ l7 d0 u5 S
  8. #include <arpa/inet.h>
    9 f0 B! {/ Z0 s( @# y1 i; f- t
  9. #include <netinet/in.h>
    9 c9 n) D3 {( b& _4 _
  10. int main()# Q. }  ^3 L  m6 ]6 O: h
  11. {
    * ?! R! C4 V& f  K9 {
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字8 u* W+ s+ X' }6 V( i3 Y
  13.         if (sockfd < 0)1 i3 i2 J* }9 ?$ p7 R
  14.         {) \3 z* c( f1 ?. T
  15.                 perror("socket");( q  k- q! S' }. ?: Y3 g% U: E
  16.                 return -1;6 b1 u8 i1 R" ]1 J1 D, e8 K
  17.         } //创建失败的错误处理
    $ S% A9 \2 ?& X. w; j
  18.          printf("socket..............# B% y+ Q' z! R; l+ z* I
  19. "); //成功则打印“socket。。。。”. M- E% |1 ]! q2 W: F% j" X) L1 Y
  20.          : L0 s; Z8 i8 Y. `
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    " T1 \4 }0 {2 n: |
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)! u# F7 ]% K. z/ u- L
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型  u5 K( b# l0 O
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    5 l& ]  g  C$ }7 n# z. c1 m7 Q
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    5 J4 y. V2 z$ L0 \0 x, g3 L8 T' W

  26. ; p' M5 h0 F, Z6 U2 Z$ C8 `% E
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ( c8 h, b" l0 z7 ~& F6 q; R5 h
  28.          {
    ! n6 ^, C! y5 Y: q2 y6 f
  29.                  perror("bind");$ p' H+ t- k' j+ V, E
  30.                  return -1;+ V" h( K' i1 e
  31.          }
    6 e2 n  z& t7 t0 P+ f, }
  32.          printf("bind........../ {6 @0 j- [0 U# Q' z
  33. ");
    % {4 `! |; ?: V& M' I# H' U) T9 e

  34. # w, B; u7 h  A2 w( O: P- R
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听# X1 y( F' h" u4 k- Z5 m
  36.          {
    3 Y4 k5 O4 c5 x6 H
  37.                  perror("listen");
    8 N. m2 I. k- D- Z
  38.                  return -1;6 O7 d2 N: @  `& v: O
  39.          }+ M7 E) M- N& e* g
  40.          printf("listen............
    + W- G6 O2 {" T: g5 V, i. |! w: W6 }' u
  41. ");8 |* V1 f. l; `
  42.          
    " [9 w! V' i/ t; b4 E: i( A) ?
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    # V2 u3 U+ S8 I) B* j7 W
  44.         if (connfd < 0)6 K/ m  }$ e8 B+ ]! w0 {
  45.         {
    ( h9 O: D5 q, v8 |' n
  46.                 perror("accept");
    6 y4 f4 Z) \  z5 X
  47.                 return -1;" C2 ?  g- `1 ]5 @. `, Y: [- A! i( ]
  48.         }1 h4 V6 w% z* @- X- q
  49.         printf("accept..............5 [% f; Q# Z+ |: Y5 }
  50. ");
    ; X* c6 I( E. Z* p# j; ?) g
  51.         char buf[100];//定义一个数组用来存储接收到的数据* a0 C) \0 S9 \3 \4 B3 R: U
  52.         int ret;5 m% k' @4 }' q7 x- E/ r6 ^
  53.         while (1)
      H6 p6 h& l5 z0 a6 b
  54.         {
    ; F$ `- ~+ E3 W" N5 J
  55.                 memset(buf, 0, sizeof(buf));
    ( f& N1 w# O: @8 z
  56.                 ret = read(connfd, buf, sizeof(buf));
    - _" E  b' W  p
  57.                 if (0 > ret). @' P- v% c, ?) h. D" [2 Q9 ^
  58.                 {9 P' ~, P4 `2 Q5 M0 u% h
  59.                         perror("read");/ W+ K: C" c* x% C& {
  60.                         break;
    % P7 G' A; g& X% M& i. x
  61.                 }//执行while循环读取数据,当$ J; E, D' }  p
  62.                 else if (0 == ret)
    5 }, r" |, R5 ]" M" j. C6 i
  63.                 {
    " r4 m, Q. _1 U$ ^8 t+ F8 w$ ~% }
  64.                         printf("write close!& X" N9 w+ s$ D# O' H. q1 B: P
  65. ");
    , B( y% u) E/ J+ w+ I
  66.                         break;
    ' ]8 p& j! r* ]% K
  67.                 }& t+ E  l5 A/ ]' d; o. Q( I
  68.                 printf("recv: ");8 L3 a8 l7 `9 q) o5 J
  69.                 fputs(buf, stdout);//打印接收到的数据
    # J7 l- D0 H# N& `' ?7 T* l6 N
  70.         }
    7 ?5 D3 O) ~/ F
  71.         close(sockfd);//关闭套接字
    * W! W0 @! R9 }" n6 k* W
  72.         close(connfd);//断开连接
    , P& k, j/ h. }7 ?7 x
  73.         return 0;4 I1 B3 P! p. A7 E9 {: J* F1 M
  74. }
复制代码

2 w# f3 q) Z+ O$ ^- _3 ]
8 L5 c7 r) A2 D' r  N' i1 N
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    * g! A' i& ~6 [% M9 T. k
  2. #include <stdio.h>
    ) h1 |3 X7 ^  ~9 {. ?! o
  3. #include <string.h>3 d" g% O. [0 U" y& p
  4. #include <stdlib.h>
    $ j: Q0 m' `, w7 D: b- T/ @. v
  5. #include <strings.h>
      B: _3 S0 l% M. G* o, o* O. R
  6. #include <sys/types.h>
    1 l7 H4 D" H+ g! R( @* r! |* ^5 `+ w
  7. #include <sys/socket.h>, f; s1 F! }2 C+ ~+ J) X  r
  8. #include <netinet/in.h>
      D* N7 d$ Z5 E
  9. #include <arpa/inet.h>' f8 @; G/ M8 t. Y. Z
  10. int main()7 C) g: f8 B' ]& E, h+ O4 U
  11. {" C  P5 H! R9 W; g2 c3 Q8 x' p
  12. int sockfd;
    / f' D& C$ B9 U; S5 q" o8 R
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))$ ^/ q( o* J$ V0 R
  14.         {
    ! `- ?* @! @: u' o
  15.                 perror("socket");" M8 \' m. ?. G
  16.                 return -1;. t" B8 f9 Z: |& j. I
  17.         }
    : v' `7 D& |; P
  18.         printf("socket..........., H$ u1 b2 q' K% M+ B4 T4 c1 x
  19. ");# Q+ E2 x' s, z: D$ |) }+ J, F  a! t
  20.         
    # X$ ^" @" b5 R* |
  21.         struct sockaddr_in srv_addr;  |" [( c# ?- d
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    - `! i8 ~& v9 W$ g1 u0 n
  23.         srv_addr.sin_family                 = AF_INET;  z, B  z0 j* G! E0 @
  24.         srv_addr.sin_port                         = htons(8888);5 x! X9 A* S) A, D/ i4 J; w
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    + r8 n3 {" X8 R: Y. D  c
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))9 m: I3 F4 I4 M( J' l: _8 ]
  27.         {% }" W7 o- m- R1 T- u# h3 h, E
  28.                 perror("connect");. _6 W' g" y$ }8 r1 p) H( c
  29.                 return -1; //exit //pthread_exit
    7 j1 S9 I2 Q0 z( Z
  30.         }0 t- e/ {% M/ w/ N: Z1 d
  31.         printf("connect..............
    # Q! P% l  I( P* W6 M' ?
  32. ");  @" H+ _( r3 D
  33.         char buf[100];" C5 o4 f. T+ ?5 z  L. a* r
  34.         int ret;1 K, Y$ b8 a* Q) w& e4 I
  35.         while (1)
    2 C- ]- e# E  ~4 r
  36.         {& i' U# Z: S- E
  37.                 printf("send: ");
    4 A; P% z& a4 }
  38.                 fgets(buf, sizeof(buf), stdin);/ I( y* _- |6 i. R& r4 H( y
  39.                 ret = write(sockfd, buf, sizeof(buf));
    3 l3 b1 d& L* u* l1 N
  40.                 if (ret < 0)
    + K5 x- E- H  S+ x3 }* G$ ^) I
  41.                 {+ T; |7 A1 o: |; c/ X
  42.                         perror("write");
    % e5 j8 |3 t3 M3 S: H9 S$ G
  43.                         break;
    / |. S: r7 b1 m1 t: P; h. {( x
  44.                 }
    . Y( D0 N2 Q, H# w, K- N
  45.                 if (strncmp(buf, "quit", 4) == 0)0 ~% u, D6 }  {7 `1 O; p1 R
  46.                         break;6 j  n4 V: y' |+ e6 x# ^
  47.         }
    0 @* K( {3 H. K% L6 n+ z8 V: w
  48.         close(sockfd);
    9 v4 h, h1 [( ~
  49.         return 0;; b: o' ?  b, b& T
  50. }
复制代码
3 I& E8 ?; t1 N4 `

. m$ v6 J1 v- o
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-20 00:14 , Processed in 0.055843 second(s), 22 queries .

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