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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
9 @4 {- j8 X5 ~7 K6 \5 s' s% M$ ^) p$ y% F5 R/ f

% S6 t: c9 K: w7 Ssocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。' }: h/ t& P( @) k9 G) u. h8 |* Z
: r1 Y7 S0 z7 b6 u3 Z2 d2 N
- o( k* o) [- F4 d
TCP协议
1 l3 |& v& w0 D8 t# r2 _  l# pTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。" E* d, L9 j# z/ _6 \

( W7 n# Q4 I' b

9 d- W" E" z+ o  d# t& `7 V, O关键词:三次握手,可靠,基于字节流。
& N: G5 _8 k0 F& W8 d+ Z# C1 G: Q5 N' @/ D, ]# e! j
" o8 ^$ y5 }3 ^" U9 p9 W
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
0 J7 f* j/ D, \ ' ]4 ~# ~- R$ y  x/ Q6 U3 t  f
TCP服务器端和客户端的运行流程! s  J; D1 i5 a8 L3 h
4 g9 `$ A) G1 P0 G

1 v+ }- u/ q' o" d- s如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?5 }) C( g: R+ p

$ l8 d4 x0 g/ x6 @8 h1 U& t
' a" }- C" P- X' w5 |! x( M0 b
1.创建socket6 K7 h& u( w- X/ X& ]& |6 d* ]7 p+ y
socket是一个结构体,被创建在内核中
! t% `8 ]: d6 _2 ]: \9 z( W/ g( G sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
" v1 E  t; |5 x
. k# _, X, S  E- `/ [+ t* k

0 |9 k, X9 ^" u2.调用bind函数. Q- l$ w/ W  U8 P  D) l5 o1 i2 \6 {5 J
将socket和地址(包括ip、port)绑定。0 u/ S+ m, q+ r# l: j
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序2 r% {+ Y6 ?' h' [; k, f  k
struct sockaddr_in myaddr; //地址结构体- q4 L" H  v4 c
bind函数
2 ^) k) H2 M4 d0 F- P' a bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))' X- O4 x: k7 z# Q6 H  {( W

' b2 ?. i9 M# n2 s0 R: V0 S0 \! {
0 E; ^; L# }. G, |; L& q% v' p0 t; [- Q
3.listen监听,将接收到的客户端连接放入队列4 z" e  a) ]7 i  P' z
listen(sockfd,8) //第二个参数是队列长度
# j4 ^& N5 k' [0 B. G! H
& R5 W' Y) w+ |7 r) ?: q7 l( i2 r
) @$ y* K  P4 q+ `" h
4.调用accept函数,从队列获取请求,返回socket描 述符. z6 l, B$ c9 P- `+ t0 Q
  如果无请求,将会阻塞,直到获得连接
2 F* }9 w- O' U4 M  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
* K, M( `4 _) X% d- D& O& X5 A" B( l- c+ Y1 _
$ r) e" l& p: A+ o
5.调用read/write进行双向通信
, }( r2 l- @; a4 |' l. J. F, L( ~3 \$ g+ T$ e. M7 u
* ?# \* ]4 s! d! O6 h
6.关闭accept返回的socket
# s' s# [1 j% W  e  close(scokfd);
- g6 v& [( U) v- J: i0 I9 T% y) E
: S! y( W; c3 K# H" H
; a5 D  j4 |( e" c
3 c6 J2 R# ~4 p( X
# R  R. o, ?  E( N4 b. o9 i
下面放出完整代码
2 N( }' V0 U# X
+ i" t" H" Z8 E8 N
  1. /*服务器*/! U+ o2 k; u0 [; w  c. V+ \
  2. #include <stdio.h>
    / }2 Y$ {( @) u
  3. #include <string.h>
      Q% _* L" L7 i! H
  4. #include <stdlib.h>
    1 V+ d5 O4 Z, O# A$ i
  5. #include <strings.h>9 G8 {. c3 c" o5 i
  6. #include <sys/types.h>: h$ A% i+ ~2 f- R9 @. X6 r+ H3 {) D
  7. #include <sys/socket.h>
    1 s6 u. B3 j& X
  8. #include <arpa/inet.h>7 b5 c' q8 N( z9 @
  9. #include <netinet/in.h>
    - _1 L3 D$ {# o) b7 ~+ g
  10. int main()
    + ~6 B! w  i9 W0 i! ~+ i* f% i: n
  11. {* O8 H# M' |( Q+ {" K. K# r* p
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- H4 r& j& T1 E$ X
  13.         if (sockfd < 0)* L& ^4 W/ `+ [4 v
  14.         {
    & S! E4 H  p; e$ }, c: B
  15.                 perror("socket");1 U! k9 b- ?$ F4 ~+ O) l
  16.                 return -1;  {2 R& C4 [$ N4 q$ d
  17.         } //创建失败的错误处理
    " U0 Q: P" T, j/ Y/ _1 S6 J' G3 g
  18.          printf("socket..............
    & S2 G: E: E) z. w6 Q- _
  19. "); //成功则打印“socket。。。。”
    # c, q5 x7 x4 K0 ?. J- X6 y
  20.          ; V4 N( @  P& U4 }
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ! @& J$ `$ @+ z4 a
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    * l, s$ I. z# Y; l
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型8 \1 k+ M3 k8 w) `& ~) L
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    % B, X$ V( a" Q" _. i* n/ B
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址/ k. n; }/ k  R. V# y! @+ R) p
  26. ( G# r& Y" x* [+ W, C
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    # H2 H$ F' i5 W( J* D6 R
  28.          {7 q4 t: j1 H; d) _0 b
  29.                  perror("bind");4 F0 w$ p: F, y; `* t7 `, E; o) M" b
  30.                  return -1;" g5 {& m. l$ R+ D3 Z
  31.          }
    1 L9 ~& r1 [3 z/ T2 r2 C
  32.          printf("bind..........8 S1 p8 t1 w, r# U
  33. ");& W3 |6 m* p& M- _" q7 `# R
  34. ! M' _, ^8 ]4 l& u/ m5 v# ^
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听; d# T% w$ ?$ P, A) V( G
  36.          {
    + r/ f/ R# }# A1 A7 ~
  37.                  perror("listen");* f% V. l, v; M1 g
  38.                  return -1;0 j3 V3 j0 @4 B+ H6 s7 g; E
  39.          }) s  l$ \, G& z# T+ g  p! V
  40.          printf("listen............4 M1 k' v: I: d( D5 Z  L
  41. ");
    9 F2 u& t) E6 v- [1 ^
  42.          
    $ o( a" M  s9 w- g
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    8 E# Y  d# o" m, F9 X; P% g
  44.         if (connfd < 0)
    $ i+ |4 ^/ b9 I% h9 q1 F, |" Y
  45.         {
    5 Z4 ]( n6 W( ?; b' i0 a* O
  46.                 perror("accept");
    # m- q2 x3 T3 h% c& _
  47.                 return -1;
    8 E: Z: r0 N* E
  48.         }
    . r' U1 |' L4 L3 Q: }: X" P. C
  49.         printf("accept..............2 ?: V4 \* ]7 m9 A' O7 O
  50. ");
    3 y5 l$ E3 N/ z2 C6 y. {# N
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    + w( X+ h- m! D
  52.         int ret;  J) `" D/ e9 O) }
  53.         while (1)5 H1 [5 o. u) |) @
  54.         {2 f; @; c; p' @( ]! O) ?1 g$ p) J
  55.                 memset(buf, 0, sizeof(buf));/ }/ ?* }# U( q% C6 ~
  56.                 ret = read(connfd, buf, sizeof(buf));1 C8 {/ Z3 [4 v8 Z$ c" T
  57.                 if (0 > ret)+ D  s3 Z& u: L  G9 t- ^9 G# p# g- n8 p
  58.                 {: K4 X( u2 Z$ Q6 p
  59.                         perror("read");& T$ I# ], Z3 b2 u" u. Y8 V* B
  60.                         break;# L. B" x" [. o/ I- t
  61.                 }//执行while循环读取数据,当6 ]. f. W/ r; T7 ?7 Y
  62.                 else if (0 == ret)
    1 G5 R# _, B! b( ]) d  y
  63.                 {: A" F7 x3 M$ T" v  v
  64.                         printf("write close!5 ]* b( Z1 }5 X- Q' s; m) H
  65. ");+ `. Z3 }5 b0 i6 {
  66.                         break;
    - W) l. B; f; K& ?0 H7 Z
  67.                 }, F3 P' d( L+ \( a8 A  v6 p
  68.                 printf("recv: ");. e! w% ~& K! A7 w$ x- s9 d
  69.                 fputs(buf, stdout);//打印接收到的数据, p7 ?1 C% v8 u! a
  70.         }# G! y- a* \: M  V
  71.         close(sockfd);//关闭套接字& K* S: g  h' y4 M. A. h% u6 k
  72.         close(connfd);//断开连接5 \: c  q5 l1 z0 p1 F9 p
  73.         return 0;  B/ ]  y: r, i) K& E4 y( P' r; e
  74. }
复制代码
* _5 S' V  ~( b3 w

) m! U! A9 r8 ~# p
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    8 K/ c# w' }6 }7 T# v3 N" V
  2. #include <stdio.h>) l* `3 d+ D' c' x* s# n2 b& |. D
  3. #include <string.h>% {4 e9 ~! U' V
  4. #include <stdlib.h>
    / ]3 |. U6 p9 P  g% ~' \
  5. #include <strings.h>5 e: `5 l2 V& A- d1 F  i
  6. #include <sys/types.h>
    1 h% ^% l3 P% `$ Y* P
  7. #include <sys/socket.h>
    ' a# t1 o7 D6 A$ ~: [$ Y
  8. #include <netinet/in.h>( s/ ^+ A5 U  e0 H) z3 v
  9. #include <arpa/inet.h>
    3 T! s5 Z3 e" {! G
  10. int main()
    1 b0 f3 C# ]# F/ D
  11. {
    1 B, A5 \0 x+ V, \% {
  12. int sockfd;; T! R' U! ~7 h! e' j& ]: G
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))6 t: ^& N1 k) l5 `9 t2 c
  14.         {( U4 \5 N9 s* [( @! A7 {, l
  15.                 perror("socket");
    4 o& M/ W( n2 m, C$ i
  16.                 return -1;7 ~5 m3 A) r$ e: z% c( \# ]
  17.         }
    $ ]' b0 _/ I2 [! i
  18.         printf("socket.........../ x; n! l& ^7 [% i
  19. ");; G  N5 [4 M3 @* \# J8 x
  20.         
    2 K9 T" r  w4 _- D
  21.         struct sockaddr_in srv_addr;
    % z* m+ n% i4 u. F# }
  22.         memset(&srv_addr, 0, sizeof(srv_addr));$ v( N$ q6 @3 R/ |# s+ }$ `
  23.         srv_addr.sin_family                 = AF_INET;, z7 B% m" y, J* @/ s
  24.         srv_addr.sin_port                         = htons(8888);
    2 r; R6 p' r9 t/ x
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");8 c6 R- H+ b1 Z7 U- }/ M
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ' O& j+ l# F2 H+ f
  27.         {9 Q  Q) t8 N) m% k9 W  @
  28.                 perror("connect");2 K. h# I$ n8 T- `5 }5 ^* [; V
  29.                 return -1; //exit //pthread_exit; e! C3 ^/ c- {& H$ [& f
  30.         }
    6 h6 Q- y) x$ n3 D- E
  31.         printf("connect..............
    1 g) P: p5 [  S" w
  32. ");! ]$ J0 ]3 D) f3 |- K6 v
  33.         char buf[100];
    $ F- X2 K. ~* q& N+ Y4 u1 x
  34.         int ret;
    7 A- q! B& D, ~0 [
  35.         while (1)
    # h5 D- z" w3 q6 ~2 M
  36.         {
      I" i& r& p0 F
  37.                 printf("send: ");
    5 m  @: G. p3 ^6 j
  38.                 fgets(buf, sizeof(buf), stdin);
    * s$ r2 x/ U' X' i
  39.                 ret = write(sockfd, buf, sizeof(buf));( p' O  d! ^% f  r$ l+ D% r
  40.                 if (ret < 0)4 p. i! b5 S! T- J# d8 ?5 G" H% g
  41.                 {
    3 r  g6 z: M, T
  42.                         perror("write");( m* A. @& x+ _
  43.                         break;5 R9 S+ b" C0 k9 C4 ]. X
  44.                 }- O- G$ G1 q4 T
  45.                 if (strncmp(buf, "quit", 4) == 0)
    $ d; S. `" V' ?5 v& t' b$ k& a
  46.                         break;
    ! j- g' c5 E3 s' T# ^/ J
  47.         }
    9 d; l7 ?9 f4 O/ z' }
  48.         close(sockfd);
    2 O7 ^# N' y2 ^4 ~4 l/ {2 S
  49.         return 0;
      A' t9 ^" w& D) y8 H
  50. }
复制代码
7 N& X( |7 A, w3 a6 a
5 H4 T; J6 R5 }% L+ k% z0 t7 }  [
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-6-19 06:24 , Processed in 0.122250 second(s), 23 queries .

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