cncml手绘网

标题: 自己动手用c语言写一个基于服务器和客户端(TCP) [打印本页]

作者: admin    时间: 2020-5-9 02:09
标题: 自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。, k9 C8 h2 A5 H! d! a+ M8 }. T: q
7 P2 K- x% t3 A7 Y/ a

1 Q! y- F" i8 O( s* usocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。7 H7 h- Z; S6 ?# M$ H# U
5 C8 g2 W: ~0 c# Y+ J% E
% h. L3 n7 i2 @% {
TCP协议
6 v0 X& Q7 ]- K. D- ~7 P+ ^TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
' g# o5 W+ x  A: b. k7 t' D  Q) o; C% C

: H& s& M- \9 D关键词:三次握手,可靠,基于字节流。
2 h0 ?! D4 K% J2 O2 J' O' Q5 p6 a& F# o  W
2 b5 K3 e" G# J2 X
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。- {6 ]% W- S4 X" s5 g
' X8 R7 B( P" z2 E# W4 w- s: F
TCP服务器端和客户端的运行流程
7 K; ~, w3 `! k' J% D3 `* ~+ K' y7 @& C5 a: C5 _

! h8 N  ]% s5 `如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
5 e3 I: w7 |9 }, E; X! z7 p4 E. T& [' m+ g( C
5 \8 Y1 {+ s" \, C- @8 x# F4 W
1.创建socket
3 I" N2 C# ~0 s socket是一个结构体,被创建在内核中9 D% z5 ?8 h! G" V( \" q
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议& f# r, J0 @/ `; E/ l$ ^5 ~

7 {2 H: w3 B; ^" ^! d

! E7 a( y% n7 v: {  v2.调用bind函数3 Z2 u$ D0 o& `8 R, ]* R
将socket和地址(包括ip、port)绑定。
7 E6 h$ M' ~: D, U5 X1 X4 j 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
0 l" }8 l  E) M2 Q# i9 ] struct sockaddr_in myaddr; //地址结构体
% j% q0 ~3 d5 d bind函数
8 D; o+ l; g0 G bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% V9 a; k) E" Z" I
, o. ^* s# L! D# Z& m( S' H- s* J0 Q/ e

4 G9 m' x! x  C. E- |9 q3.listen监听,将接收到的客户端连接放入队列
$ ?) `# o% W$ O5 s listen(sockfd,8) //第二个参数是队列长度
* E  z; ]" x0 K4 F! ]5 R+ _, F, F# h

' n% K" D: z; \, F! `6 E* G1 b6 I4.调用accept函数,从队列获取请求,返回socket描 述符, S4 S1 m& S" }, q) X0 G* c
  如果无请求,将会阻塞,直到获得连接& a% C; g& f8 d0 ?
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
4 }+ u4 I3 M6 l4 \% H/ ?5 Q) i* S2 \1 F8 u- D$ w

8 k: F  y: y1 Q! h- C" A5.调用read/write进行双向通信
  A4 c; D! M! k3 ?3 S# m  o3 V; `
1 ]8 a- c3 @. q; I  K7 j
' f, h% R" t6 c) d2 y. s+ |
6.关闭accept返回的socket& Q' ]+ z  A2 B! w8 a
  close(scokfd);
, M: R: w! O) f' E* j6 c! b9 u4 M- C& Q9 b' ?: I

) y2 c: ], ?( P1 B+ T% I+ q/ _% Q) A: F, Z$ r7 V
1 @% y% Q3 g, T8 O  R" @# p
下面放出完整代码
: O1 G1 O. ~/ B9 g" `( l
6 V1 ]9 L9 M+ B5 m/ l) J
  1. /*服务器*/" n3 C( E& R/ n0 n
  2. #include <stdio.h>
    3 B, m8 E! S1 }( ?$ k) @
  3. #include <string.h>
    ! a) Z5 N; {4 S% w
  4. #include <stdlib.h>4 v% R, x3 ^0 {" [# W
  5. #include <strings.h>
    ( E) l- T1 ^9 o$ D. i( G1 r; E% w
  6. #include <sys/types.h>% V/ R% G+ y. Y% P8 m3 n- @( J
  7. #include <sys/socket.h>
    , q3 N$ W4 O# X) P5 O( s0 G
  8. #include <arpa/inet.h>8 e: L3 }" \* F. K
  9. #include <netinet/in.h>
    7 i) ^: A  R, [
  10. int main()$ c0 X; ~: _0 w6 y: h
  11. {2 Z; O1 W3 y" u9 t/ B$ N
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字3 {5 X3 ^7 l& g
  13.         if (sockfd < 0)/ |* r- I7 j5 y, D9 |; u  l- ~
  14.         {
    2 \8 c, a5 }; ~9 q% p+ [
  15.                 perror("socket");% G" j/ B, S- f; f9 \3 M3 B
  16.                 return -1;
    : Z$ t5 d3 X( l8 s! N
  17.         } //创建失败的错误处理) L# H4 \9 P3 U1 ]! M/ h( x6 ]* C
  18.          printf("socket..............2 s0 h  g2 X: {7 ~4 m
  19. "); //成功则打印“socket。。。。”
    " S* m, Z0 U9 I
  20.          8 ^- F# B3 T3 S8 D) l
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体7 G" i4 u; T! e  f
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ; d; a7 X* L5 a/ j
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型' h- u9 q8 Z0 r
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    / q9 [. o9 s# W: T0 S' Y
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址8 [  a" I% w+ ^/ ?! A

  26. 5 h, O: F$ {+ ]% z
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字* j' J% I2 k! e0 z  L3 X
  28.          {& I4 b9 L5 i/ U5 O3 M
  29.                  perror("bind");
    - X2 T! B. Y9 c. h$ S  V' F2 a
  30.                  return -1;
    / y5 q( F+ l! n$ @5 a9 ]2 F3 @
  31.          }" K5 `  _& b* w- z
  32.          printf("bind..........; d2 k: Y. m. B# J
  33. ");7 U  q% ~7 @; ~5 m2 \+ Y; k
  34. ' E, B$ `3 F, Z
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听9 ~' D! r1 K  A+ B2 m( Y* J. E$ _- m
  36.          {1 f% `8 l5 }4 f; T  n4 I, j
  37.                  perror("listen");
      O$ l. D3 Z4 J7 f
  38.                  return -1;. @1 N, ?& ~1 B% o' r) p- X
  39.          }. A$ E- ^; z2 |  Y$ q
  40.          printf("listen............* X. f4 h4 ]' x$ |. E: P2 Z* N
  41. ");- P. n' H5 z5 m. X- |
  42.          * {' _  |! R' e! |
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求6 q3 u, Q$ n3 N  l) {# h
  44.         if (connfd < 0)
    , X7 K( d9 m9 k! B/ W" V5 V
  45.         {
    9 m3 Q$ V0 ]+ f6 s4 T
  46.                 perror("accept");
    " [# E; G" H4 D$ S! f
  47.                 return -1;( }4 x; j- ^) l8 d9 F0 v( A
  48.         }
    # D8 x* J& O* g( [! X
  49.         printf("accept..............5 @: [6 t/ W7 b7 U( b( j1 D2 G
  50. ");
    1 m4 X& `: F( ~5 f
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    - q' Q% U# r' X% A! G
  52.         int ret;* f4 @! H+ ]* {, Q
  53.         while (1)
    4 I- w0 b. \. v0 o' R
  54.         {
    ! u' T, }% Z" E4 V! R7 q
  55.                 memset(buf, 0, sizeof(buf));5 m5 W3 P. c: P0 O9 ?
  56.                 ret = read(connfd, buf, sizeof(buf));
    9 q$ e1 P. o7 K; T0 z. k% c6 V5 O
  57.                 if (0 > ret)
    ; K7 i% [; c8 b
  58.                 {
    ) ~$ a' L- }( }
  59.                         perror("read");
    & o, ~0 ?: z% d, S
  60.                         break;& P, V0 A7 ^# v. R, O0 X
  61.                 }//执行while循环读取数据,当
    7 z  j$ ?. Q' {' ]+ G( t
  62.                 else if (0 == ret)# X# @" y, w8 ]) ~9 ^5 b1 F0 v' c  G; L. y
  63.                 {
    6 h/ m' [3 w# S9 [7 A2 R
  64.                         printf("write close!
    ! q0 A1 L! i4 H8 f5 I1 Q
  65. ");$ o7 u+ x) Z7 p" C1 _* |' T' X
  66.                         break;. @) C9 Y0 w. B8 f- C" T
  67.                 }
    4 o2 P& l2 ?) D$ h
  68.                 printf("recv: ");) h, C, K6 f. |( B" T5 |9 |
  69.                 fputs(buf, stdout);//打印接收到的数据& `  h+ [" `$ H8 o
  70.         }
    8 a6 L8 N9 M# ?" K' g3 }6 j& {3 F
  71.         close(sockfd);//关闭套接字6 u6 P( J) [, }, Z
  72.         close(connfd);//断开连接! `6 L3 G9 J0 \* m8 k. G
  73.         return 0;% E. B$ A2 i0 o  z, f% k( ?0 n5 F5 F
  74. }
复制代码

6 g# j; G6 ]; ^5 P% B$ a! N* v. `" q7 Z/ \( J
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)( O, Z, h7 _' M
  2. #include <stdio.h>
    $ \! t+ F0 G; S8 r/ y7 ]9 z  ^
  3. #include <string.h>
    * N. A) V6 [4 M& y& n6 y6 E
  4. #include <stdlib.h>2 r% z; T4 M  {; T
  5. #include <strings.h>; j5 `5 h8 v1 H0 j7 F
  6. #include <sys/types.h>: S! Z0 V7 |* Y5 n: x9 d+ o
  7. #include <sys/socket.h>$ S6 h6 q7 w+ s
  8. #include <netinet/in.h>" E, h  k6 {0 l/ n
  9. #include <arpa/inet.h>- c  d6 s) @0 m9 ?
  10. int main()
    - E) P2 M0 I/ P/ D  J
  11. {
    ! E2 O+ K/ o/ p# y9 p
  12. int sockfd;( ]2 K) H5 l; X/ I6 v! }5 }/ x& @$ `: c
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))  o5 j& x$ x' W7 z
  14.         {' |' A$ X2 ^' ~( R" B1 f! W
  15.                 perror("socket");
    * A) n' H& H" v1 b5 ?
  16.                 return -1;
    & ?) u7 l8 b3 f4 `$ J/ Q
  17.         }' P/ O: Z8 ~7 ^2 k4 V
  18.         printf("socket...........
    ) u( _6 B( S6 r& i* C% K6 P
  19. ");; C$ J+ ?7 F" j; u  c1 U& v
  20.         
    " p1 K0 v, ~: Y3 ]! W6 p% A1 I; F
  21.         struct sockaddr_in srv_addr;3 V+ d& I- ?& w/ t; h
  22.         memset(&srv_addr, 0, sizeof(srv_addr));" v  w1 d# n$ A, J5 Y4 u
  23.         srv_addr.sin_family                 = AF_INET;! q4 \. C- f: }2 G
  24.         srv_addr.sin_port                         = htons(8888);& n/ d, W' M/ N9 @: e6 _0 `# `
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ( t4 f0 H8 G0 ^3 t7 A
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    * y) B. H/ Y6 {2 y7 A' n% c
  27.         {- s3 ]% l- i& V( _
  28.                 perror("connect");% j1 X# G, d" [5 u, T* h
  29.                 return -1; //exit //pthread_exit
    6 G5 u6 V  d$ U( L! ?
  30.         }/ g' C( o7 d& k
  31.         printf("connect..............
    + k/ V3 ?) t" S; e
  32. ");0 z4 h& a- e% t/ l0 g
  33.         char buf[100];; Q: j! c2 ]5 Y, n* `% g$ b
  34.         int ret;5 i. C7 k8 d' y7 }- |. u
  35.         while (1)9 P8 }7 F3 m5 M! |: F- ^% y% ~* [
  36.         {
    & B* K: A; {. b4 }2 ~! m/ p0 e
  37.                 printf("send: ");
    ; e2 _8 B5 h! a, i4 G1 z( C9 d
  38.                 fgets(buf, sizeof(buf), stdin);
    : O# J9 p4 \) u; I$ q! M( d
  39.                 ret = write(sockfd, buf, sizeof(buf));! D3 h7 R4 d: j0 k" Z
  40.                 if (ret < 0)% w  c# s4 \% T
  41.                 {
    , \, ^; T! I9 |$ X1 ~* M5 P0 f6 m# V
  42.                         perror("write");
    : I+ X; R3 d% Z' h
  43.                         break;
    6 a, H% V2 v; w) V% M6 N' _
  44.                 }
    # K: {4 E# w- |2 u
  45.                 if (strncmp(buf, "quit", 4) == 0)
    , I5 D2 Z7 \" h: J
  46.                         break;8 S1 c& ?% I( P6 D. j6 b) A
  47.         }+ b' G2 _8 o! A  p4 ^! C. h) O
  48.         close(sockfd);
    " \) W4 A8 `5 Y0 {% U) H
  49.         return 0;
    % R6 O' ]0 B' ^9 e2 ^' b! h
  50. }
复制代码
4 t  Q' S: z! i6 y# C( e- l
2 `+ Z# n; ~  P





欢迎光临 cncml手绘网 (http://bbs.cncml.com/) Powered by Discuz! X3.2