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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 2526|回复: 0

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

[复制链接]
发表于 2020-5-9 02:09:24 | 显示全部楼层 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
! s# J1 A: N* M3 o3 ]2 S
) f) X8 r5 }  b+ O" `% s

9 J. n# ^: x) T0 |; I1 hsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。' Z( i5 G& y+ }

- y# h) X& G. M( l) N7 Q, k# C/ n- N
" S2 x# Z( g. \, s) ]* z
TCP协议6 d1 X: h, R  k
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
. o  ~2 o: h3 D* W1 Z5 N" w' V3 ^  x0 ?
" ]! e9 H9 S, f
关键词:三次握手,可靠,基于字节流。
( T8 z+ }: W! }3 J" z  K8 r7 b, i2 i% _# X% V& V/ N* F3 @
" c& j1 Y- Y4 g: |
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
2 x  ~6 H& x. w9 n 微信截图_20200509015654.png 4 S8 d  M' i! O' N+ o/ T  b4 t
TCP服务器端和客户端的运行流程
5 J* t0 A% C9 v% S
7 q& k+ O! a3 [! n" I' P' e, D
/ d9 U' k" x" N9 J
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?* z  {% {* n0 M! p5 P
! ^/ y& w# f0 a3 m' e# N" N2 h
9 x# j( X) f- D( V& w' N
1.创建socket6 Z. g* Z' f  n( H9 M" b
socket是一个结构体,被创建在内核中0 t+ O) e/ d& o3 j. \
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
, n7 d) k! p) Q2 R& g$ k
$ V& ]- A- H6 [0 f, a$ l7 A5 R

  M; |7 I6 @) |) N, Q" v7 y2.调用bind函数, ~& S8 A% V/ r7 u
将socket和地址(包括ip、port)绑定。- p6 R% C% ^, T
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序; q$ Z, Z' e/ {  C
struct sockaddr_in myaddr; //地址结构体5 y& t) Z. ?# E( o  W
bind函数
* J! Z( g( |* d) p  q bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))0 _7 ^# S& g6 |+ y9 n+ v  T% ~3 ]

  K( o/ J+ W' M9 K1 e$ F1 [

$ l; |; ~+ s( H8 A2 o" Q( D3.listen监听,将接收到的客户端连接放入队列) r" h) R( Q. ]' F- x: g$ G' \( D
listen(sockfd,8) //第二个参数是队列长度
( A- l4 `3 P9 f0 ~. t! E+ Q3 a
4 C7 i; f6 ]4 B, T
4.调用accept函数,从队列获取请求,返回socket描 述符
8 e% L; n! {1 Z9 E  如果无请求,将会阻塞,直到获得连接& ~! w: s* a- i% p; [, u! {: m9 |0 O
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
: _# A8 V, L8 y. a: V6 h# Q2 R6 o9 D6 G  _% C

) B0 d! X6 {8 N, y9 s' r/ _9 S* t5.调用read/write进行双向通信
) ~# P5 I+ A# Q7 Y; I
7 C0 S/ S9 u8 c8 o+ E' @; }2 q
8 U) U4 |$ g- C$ P4 _
6.关闭accept返回的socket; K3 t# y0 ^, e" y  ]
  close(scokfd);
0 T4 K$ V& e' t) @
6 ~- C3 W1 S- c

3 M5 V5 ]/ y9 x/ d
$ F" T2 q' k* Y

. o" l9 B7 Q! o5 w下面放出完整代码4 n6 g3 d! M5 a$ V) H- ]+ \: c

' r. C6 h2 u! ]8 i& N7 [
  1. /*服务器*/$ s7 o/ d' [) U2 M1 i, S
  2. #include <stdio.h>
    & e/ V8 J4 ?+ k% @
  3. #include <string.h>
    6 J1 j/ T  L" p
  4. #include <stdlib.h>' p9 B. g8 F' A7 m5 i
  5. #include <strings.h>
    1 v* \4 y; Q7 q/ i, p' F, w* B
  6. #include <sys/types.h>5 M& o- d7 b+ X" ]8 d- G* O
  7. #include <sys/socket.h>. v. w. s* i1 s5 x
  8. #include <arpa/inet.h>
    . p3 A! i9 n4 f! N. E$ r5 q2 b
  9. #include <netinet/in.h>
    * w$ @3 ^0 h+ y* `& }! Q, A
  10. int main()
    - |5 |/ t6 [/ @3 d
  11. {
    - i6 X4 H$ V8 {0 q; `$ f3 Y
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ' x( g/ y) N) G  T5 V
  13.         if (sockfd < 0)4 V" a; u  Y7 Y, T1 M) ?; u
  14.         {8 H0 ^8 a% B$ ~# p$ }
  15.                 perror("socket");! z! A: f" j+ Z
  16.                 return -1;' O! e: Q7 C1 n4 Z5 n% q1 y
  17.         } //创建失败的错误处理- ?. X7 m! ~& `: w
  18.          printf("socket..............
    3 I" g! c' e$ l" T
  19. "); //成功则打印“socket。。。。”+ q2 O+ Z* O) x* p1 V
  20.          0 v8 c1 d1 l8 F6 ?5 k# ^# u4 R0 {  A& I  L
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体: u2 h0 |& J8 z
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)- {) B+ k. m! ?8 v* v
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型' |0 V' n! n9 r* v- v& [
  24.          myaddr.sin_port                 = htons(8888); //选择端口号/ n4 w1 r( o  ~1 z0 [4 c
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址& p. d' V3 X3 T; f$ O5 |" F5 M

  26. 8 m; j6 }9 x! z0 b
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字- b0 e4 U$ z0 D* A/ A5 `" I: i/ n& a
  28.          {1 v& I9 Y( x1 {* y
  29.                  perror("bind");! v0 ~9 N6 l2 \+ D
  30.                  return -1;
    0 }5 |8 `6 B! U8 p; c
  31.          }: ^% @) ^5 K4 J) [/ [& \
  32.          printf("bind..........8 w; j8 o+ n1 B) t0 V  B: v
  33. ");5 H8 j* `; C4 b' M9 c
  34. * Y' D! W1 U0 n) o
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听! J9 @! h" _6 G
  36.          {: z" N% H: J4 a+ g4 O' X
  37.                  perror("listen");
    # }+ n5 E7 N% Z
  38.                  return -1;
    * {0 V- N  v3 r/ R8 x
  39.          }
    6 a' Q+ O( `3 F+ z# R
  40.          printf("listen............
    ) ^2 {$ T6 e; |0 s. k! c6 v6 m3 h
  41. ");
    % U% ~8 A; B; p" m
  42.          
    7 e: o$ C0 {: j# t, H
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    4 }0 W7 D. P6 |+ z* g1 p$ j
  44.         if (connfd < 0)
    $ x4 F3 u- X% o$ ]  ~
  45.         {
    + |' q: _$ Q* J2 E* e3 S5 f: x
  46.                 perror("accept");$ f4 C. `1 q+ \5 Z0 v) L7 w2 w
  47.                 return -1;
    0 {; T' G3 F  u1 X  }: N. g$ I
  48.         }
    $ T. _! ~9 C( q, M0 D; W7 p1 g( o6 `1 o
  49.         printf("accept..............
    , M/ R, {+ X- M( Y' c: @. J
  50. ");
    4 i$ ~3 B4 v  u, b
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    : x% v2 d5 {7 a2 Q/ F2 z- R
  52.         int ret;
    & ?, N' T% l" I* H
  53.         while (1)) V9 @4 P' t2 N  M6 B$ U, W" }% s
  54.         {
    ; g9 A, S7 L/ H) @1 o( w$ z
  55.                 memset(buf, 0, sizeof(buf));4 t: {2 g2 r' I
  56.                 ret = read(connfd, buf, sizeof(buf));: a; h$ f- s/ N# t
  57.                 if (0 > ret)# [' U9 |. Q2 d6 L
  58.                 {4 I2 x3 ~" n( _' V, t
  59.                         perror("read");
    $ h5 i7 Y/ C$ A8 [' c' G6 r
  60.                         break;
    * e3 X3 c* d: ?
  61.                 }//执行while循环读取数据,当- a" y+ C) K, V2 e. q3 [3 ]
  62.                 else if (0 == ret)" i& Y& s) @6 _7 a! @: H
  63.                 {
    " j+ a! P8 ]- G& _, a1 {& y
  64.                         printf("write close!
    $ J6 q7 ^  Z/ A) C1 C2 u
  65. ");
    # o4 `! [, A2 R* W1 I0 Y
  66.                         break;
    4 O5 e* p1 N$ N& a' ^
  67.                 }  d0 K3 {6 k% h  T4 S* C9 G/ `
  68.                 printf("recv: ");$ _# X9 p9 S9 ?# H* z8 P
  69.                 fputs(buf, stdout);//打印接收到的数据
    ( B5 {1 o% Z/ K7 q( f5 Q
  70.         }
    1 G  n% n2 {6 O3 T( ?
  71.         close(sockfd);//关闭套接字
    . S! }3 ~3 _. o# B$ W
  72.         close(connfd);//断开连接) R0 G0 @( ^$ u- W
  73.         return 0;9 k+ w$ K" N4 X3 `+ U
  74. }
复制代码

3 k: y+ @4 r( r' i0 @- I. _' m  W+ `
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)3 |1 _! q7 Q+ W& C7 M; e
  2. #include <stdio.h>, T6 \+ n/ C: n9 r& e, [4 }
  3. #include <string.h>
    9 P% o* ^) G/ A3 v+ N
  4. #include <stdlib.h>2 ~  v3 r0 R8 ~$ _. T  g% p+ q2 ~
  5. #include <strings.h>7 d' i8 ~1 R5 ~0 v% M' |
  6. #include <sys/types.h>
    ; }5 d# Z  G6 q3 Y# M) ?
  7. #include <sys/socket.h>
    & J7 w+ s+ c3 W! m  B
  8. #include <netinet/in.h>
    ! e) K5 O+ M, u  k' F: M
  9. #include <arpa/inet.h>
    1 [- ?& U- s1 `" o4 p0 _* S7 R- E0 E5 ^8 `
  10. int main()
    ! U! g  O7 G  }) K+ p4 R
  11. {: T) z7 W9 ^3 X' p7 F
  12. int sockfd;  j, t3 ]' Z, A$ J/ K
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))# z4 Q( l# S' C' G. ~6 k
  14.         {0 C( g) V% T+ V0 i9 C+ G6 k
  15.                 perror("socket");" ?& t  X8 D9 _/ s4 W
  16.                 return -1;
    % C9 }  `! R3 H) ^; H* t
  17.         }" k- H8 Y+ t& D) P
  18.         printf("socket...........
    ! F/ \' K  s" l! |
  19. ");
    $ l& L( u4 }# h; f  |
  20.         
    * _8 b6 I+ D3 s0 J
  21.         struct sockaddr_in srv_addr;# f! D. |& ]3 \; [/ F- \
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    ; C+ T1 n7 }2 A' ?" I# E( {
  23.         srv_addr.sin_family                 = AF_INET;
    6 D0 H0 @8 O0 H1 y
  24.         srv_addr.sin_port                         = htons(8888);: u) P8 d9 t. @4 ?) m$ S
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    7 ~; H8 @/ s; h; j
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    $ f/ g  g+ v1 b" z" l
  27.         {6 E8 T' Q2 C( u. V- |, Q1 u
  28.                 perror("connect");& V3 k! w1 a7 `5 q) n
  29.                 return -1; //exit //pthread_exit
    4 X" Z  q' Z  @3 ~) k
  30.         }
    0 A/ G' k0 Z" M, L
  31.         printf("connect.............., f+ k% G3 ]& \- ~, ?  b( h6 i2 J5 H
  32. ");
    & G1 `# g" X' z6 S
  33.         char buf[100];* s' ?9 G! Y7 z; Z% ?
  34.         int ret;
    6 j  N- d  a; t5 u9 N# m7 A2 C. z
  35.         while (1)
    - @! Y+ i4 {: x/ z
  36.         {
    ) j5 [. `% [( `) n) g" J  D7 j: y
  37.                 printf("send: ");' z  o; v3 L& P8 `
  38.                 fgets(buf, sizeof(buf), stdin);/ Y3 ^" f' P' f* T$ Y
  39.                 ret = write(sockfd, buf, sizeof(buf));  M/ Y  w2 F+ a3 X# U
  40.                 if (ret < 0)* Y; j& `* [6 [- b+ t) T  C8 _
  41.                 {
    ( E+ y4 Y% o: t$ N+ R
  42.                         perror("write");
    * s2 }) R* w" d0 ^9 [2 {
  43.                         break;
    + C+ B- v; i! m3 o4 m0 E
  44.                 }
    ! m* m$ H% i' M' T8 A; C( z
  45.                 if (strncmp(buf, "quit", 4) == 0)- u0 F, a9 G; c4 D' o3 |; K
  46.                         break;
    7 U. A7 T5 B* F; s4 i  L. J0 B" o
  47.         }" `( I/ |6 o! r7 v) O6 ^- }: v
  48.         close(sockfd);* a" @% c) A5 P/ ^7 D8 q
  49.         return 0;
    - o5 q! D: m6 X! g5 c( H
  50. }
复制代码

/ L. U1 o: Z3 Z
+ v! O# D" `, I; u* R- `* Q3 n, |! L
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2021-5-6 14:52 , Processed in 0.190872 second(s), 25 queries .

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