cncml手绘网

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

作者: admin    时间: 2020-5-9 02:09
标题: 自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。3 B; L/ O/ j$ M- D' M
, e' p$ z# {1 v- a; M& _

3 S- p- D0 C3 b! x0 w/ y" gsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
( x: q# f1 n" J! i/ @7 C4 r9 O& }  r0 I8 D. y( c2 K8 m1 L. {4 C& @) e
. s: J; w3 x8 t1 c3 I9 k
TCP协议
. ?! X5 |5 u- i; W; G' gTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
( c: r& V7 D) a' V; \# N
4 E5 _; S; K0 M7 X  Y' S
! c- I# Y2 b( |2 ?
关键词:三次握手,可靠,基于字节流。
. S# X0 W) d9 p" |4 T
  z3 C6 H! p  W! h; Z
/ a. I; h  Z* H" l
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
, Y  j$ t6 q# c
5 h0 T( H7 S( uTCP服务器端和客户端的运行流程0 D2 }$ ?& b5 u8 o' B

' {+ ?+ e, f* e: A; v

5 H3 }7 _* F9 \6 q如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
' u5 i5 t( G" o  W% ^1 o2 Y8 j: j% ~. K* `, E: Y
# K+ p+ e2 o4 Y  g8 N
1.创建socket
5 C- n: C( [3 U/ ~+ O( q socket是一个结构体,被创建在内核中
, M( [; m, s& A1 g% t2 ^/ s sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议  U+ p/ x. r$ M4 W* m1 }
& \' j' e3 t8 d& b6 v2 _8 e

; a! E% O! Q& e  j" q* S' [- [$ P2.调用bind函数; d) _0 j3 ]. Y8 a1 o2 i
将socket和地址(包括ip、port)绑定。  F& \$ |( O' ^4 T
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
& \* [: V* h- N$ S4 o7 g struct sockaddr_in myaddr; //地址结构体
+ t7 W+ H8 `5 Y8 U9 g3 k bind函数
8 w1 v% Y3 i9 E: j/ e0 G bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
1 Z# H8 d# V, A: z3 k' r
/ W3 Q% s+ `5 G# F# k! ~1 o3 V

6 ]3 i: }, T& ~0 \$ x( x3.listen监听,将接收到的客户端连接放入队列
2 W7 m# o! l2 D& g' ^" ^ listen(sockfd,8) //第二个参数是队列长度
  R) Y+ ]6 h4 c5 b
7 Q% t8 Y8 ~  Z, i5 `' O
' c7 v! n6 f& y% n4 E
4.调用accept函数,从队列获取请求,返回socket描 述符
3 r$ Y2 I) \( s6 \( g+ N2 `; P  如果无请求,将会阻塞,直到获得连接; I" g# `$ g% M  x
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数" w( ^7 G: I" F+ |4 o  ^$ l" ], H! ^
' E( Z' U0 d3 C8 }4 M
. g; y( ?1 `& ^3 H
5.调用read/write进行双向通信
" N( y- v5 n+ f. ^0 C; l/ v) w
: G9 _8 m2 _+ e) w) K/ B2 F6 J: j
1 ^4 W' G( }/ l$ h1 Y6 k7 i
6.关闭accept返回的socket$ ^* i1 K* i% h2 e( w' N% p$ s
  close(scokfd);, J  F; F  F! K! z/ i
9 m" W) ^& j+ |3 L$ w' _5 d
7 L% r. L2 X$ B9 r
! h0 p! y9 m  E, G
7 B6 Q1 D7 |6 K3 [% y& A9 r
下面放出完整代码8 [9 o) }' Q" u* ~( d* s

# t2 g5 O! v6 t
  1. /*服务器*/
    6 u1 }! [7 w4 F, |  |
  2. #include <stdio.h>/ k  M0 x9 G& x1 {% Z8 X3 O
  3. #include <string.h>
    5 D; T4 S- t; g1 y- D! k; |5 y
  4. #include <stdlib.h>" W2 }' d& C3 b# F% W
  5. #include <strings.h>7 A; X: c" d5 ]7 R% F6 c7 N2 p
  6. #include <sys/types.h>5 o+ O+ W, |% Y% b
  7. #include <sys/socket.h>
    * ~9 t3 O5 Y( m( P
  8. #include <arpa/inet.h>+ \% p! v3 v7 o) s9 g
  9. #include <netinet/in.h>
    # ?5 L0 @( i% a5 _1 J6 o, c) q
  10. int main()( ^$ i6 k9 g9 t! U
  11. {
    2 t+ Z# W4 Q9 r# E* w
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    $ A' T6 C/ f2 B
  13.         if (sockfd < 0)3 l% i1 @( Y4 S! i
  14.         {8 e1 u0 a2 _7 l8 p: d
  15.                 perror("socket");; H. n3 L  n, z! b2 J+ o
  16.                 return -1;
    4 j: m: p7 d! I% A" J
  17.         } //创建失败的错误处理& E2 k# @. Q2 ?1 g! G' e- t
  18.          printf("socket..............
    & B* Q  ?9 o) }" Q- O7 w2 w
  19. "); //成功则打印“socket。。。。”
    8 O" T0 `, l, H2 q; w
  20.          
    ) e8 h1 ^6 Z- ]- _3 b
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体9 U0 a. t- e/ o, }7 A; @! P
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见): _& y$ P8 Z; Z/ Q! Z# l8 q4 `
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型, ~3 M4 t: W; X; B0 i
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    8 O. s4 O1 B( f& _- ]
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    & u7 G9 }  p3 O& l7 J# E8 {+ L
  26. ; D+ u: ~8 x" J& {$ J8 F) X4 {
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    / g$ v+ D$ L2 X2 E9 G
  28.          {! I, C( d$ G+ {' M: U
  29.                  perror("bind");: M2 [5 b  }& t8 H) q" [; U5 \& P
  30.                  return -1;$ W7 ~; N( B! E  V$ T5 Z1 [9 w: j' P
  31.          }( E. r* u, s3 ~
  32.          printf("bind..........
    4 I- c$ v4 ~0 w; v' G
  33. ");
    8 D5 q! t' q, v) t

  34. 9 Q4 \8 p, p, `3 U7 A
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听6 Q* g& @# @: i
  36.          {
    - |3 b- K7 r$ V" \" g
  37.                  perror("listen");5 f% n: m2 n$ F
  38.                  return -1;
    " K7 G( W, m- `6 ^1 E1 t
  39.          }
    / h! W, _$ `2 z( J8 f' T
  40.          printf("listen............; Y- y' U* f8 S* N' B( g: l# S
  41. ");
    - R* p5 c& Y" |' G/ t2 c
  42.          + A3 q* S- B8 T; D- o
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求+ Q0 Y6 M4 R- k
  44.         if (connfd < 0)7 m6 H7 L3 F$ l" q; A; {+ r
  45.         {# ]0 E; P( y, c( L
  46.                 perror("accept");
    8 K2 p3 L. ]$ A9 _( U  Y4 \; v
  47.                 return -1;" O/ p# \8 w# ?* Q5 d" l
  48.         }6 ~/ Z9 {- C2 B! ~
  49.         printf("accept..............* }- U; B# u# e& ~
  50. ");
    , r3 P8 A1 z; [+ |/ a/ M- }1 i# S
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    2 @. X" R; R, G4 C0 h" l! E
  52.         int ret;: k" W" I' Q8 Y/ D7 m4 u
  53.         while (1)
    2 w  B: @; H' _- Q$ b
  54.         {
    3 {" a8 Z3 F) u9 O
  55.                 memset(buf, 0, sizeof(buf));
    + I( h6 _, B+ _2 X6 }9 T7 m, u
  56.                 ret = read(connfd, buf, sizeof(buf));: y! o7 c0 b0 r% s  d8 b! m) v! [- N! K
  57.                 if (0 > ret)7 z7 y+ O; V; ~. r- Q& q# U
  58.                 {
    5 E1 L* `) Q8 h" N9 X. g8 E. a
  59.                         perror("read");
    / {3 f1 Z' o1 K
  60.                         break;
    . N7 ^# N) X7 {9 k2 O
  61.                 }//执行while循环读取数据,当
    9 n* c6 N' R) N7 t' d5 V2 @
  62.                 else if (0 == ret)
    2 ?7 _1 A' a7 N, d7 ?. F" e
  63.                 {, s2 ~5 x- r! x, L( R$ s  g  Z
  64.                         printf("write close!3 B! M  L9 g6 i8 h# ?
  65. ");
    , x/ y$ e+ i5 E% k
  66.                         break;
    1 v. B. B; M; w! h/ m1 l# H2 u& ]
  67.                 }) E# u" s0 }2 |  L; L3 p& r
  68.                 printf("recv: ");" N8 r  [- p/ f  u
  69.                 fputs(buf, stdout);//打印接收到的数据
    + A4 S; P* O6 {$ ]) @1 }
  70.         }' m4 v- s0 o% t
  71.         close(sockfd);//关闭套接字8 s8 E5 V3 K, C7 `- N& U
  72.         close(connfd);//断开连接6 }+ s' X$ d& X2 s) J8 ?4 @! T; _
  73.         return 0;  W1 u1 Y8 T+ w1 v9 N: r0 ]
  74. }
复制代码
* W# n8 K- b) B5 x" A7 @# ~% c
' L. I8 I2 e" Y6 Y( H. b  l5 Q
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)+ q* M! n' J* t* x8 y6 H
  2. #include <stdio.h>
    ' k7 f2 K5 {6 h+ G. d: L
  3. #include <string.h>! P9 ~/ Y# O* [( J
  4. #include <stdlib.h>
    + W% J; T" o" ?+ }5 K
  5. #include <strings.h>) d" d: F! H$ C7 w' V
  6. #include <sys/types.h>
    % s& s8 q& Z' y" b* s: [( U
  7. #include <sys/socket.h>- {3 |& `: h- k& ?# q! e
  8. #include <netinet/in.h>% g6 |1 a. @+ l2 y
  9. #include <arpa/inet.h>7 K+ }. b; N3 w8 L* F
  10. int main()
    + \. V0 h+ F% `$ q+ l, s
  11. {; z0 ~+ h/ F# O8 V$ ~) A
  12. int sockfd;
    3 m5 s& i4 d5 R1 X& C
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))) B* A1 G2 N# V' M. @
  14.         {
    5 Y. c) `1 }. U0 L
  15.                 perror("socket");
    & x6 j5 Q5 F! _% x' @' ~1 @, E" r
  16.                 return -1;
    " Z, m; d+ m4 @) _8 ~! `( @8 m
  17.         }
    ( `8 ?% R4 o4 b4 I/ I( S0 i$ b: C
  18.         printf("socket...........& C, g* y: Y7 O. f4 z" _9 V: t; ?
  19. ");
    $ K" C% L, @) H; j
  20.         
    / `0 U1 m- A" P
  21.         struct sockaddr_in srv_addr;2 N$ K# P* o" Y/ H- D
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    ) v& J( V, h6 H& x/ ]' v0 |) j
  23.         srv_addr.sin_family                 = AF_INET;7 C8 w: g; q1 ]& n0 I. s1 G+ C; A5 p
  24.         srv_addr.sin_port                         = htons(8888);
    : R* ~8 b9 E. ]0 n7 u
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    0 V8 W, P. p; x
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))2 q" D3 H0 R* A) d3 ^, I
  27.         {
    1 E! Z+ A: u8 _) H7 ^
  28.                 perror("connect");
    & _9 q% M' H4 D! c
  29.                 return -1; //exit //pthread_exit; c, c2 P1 r- F* A' Q
  30.         }! z1 ^* b0 Z9 \* N  T
  31.         printf("connect..............
    9 m. c+ w" ?' k4 S& ]7 _$ x+ b/ V
  32. ");
    8 _: n, D6 K+ v1 p- P( B
  33.         char buf[100];0 J* m8 B5 A* u5 O
  34.         int ret;
    ! F6 J0 D% {7 k( [' `
  35.         while (1)
    % F: `2 @9 e6 s6 O- P, C* V$ w
  36.         {4 x- s* J. r2 y" Y5 N: ?
  37.                 printf("send: ");# c- C8 x3 ^' C2 `1 ^" u8 B5 l+ ^
  38.                 fgets(buf, sizeof(buf), stdin);
    * O; T( m8 i% Q/ C6 K
  39.                 ret = write(sockfd, buf, sizeof(buf));
    - g- S1 w+ n+ a5 O" I7 L3 K
  40.                 if (ret < 0)
    ' \! W( l* k# y3 ?
  41.                 {
    7 p' o6 X% M+ J0 t2 k
  42.                         perror("write");
    ( K9 J: U+ h$ k
  43.                         break;
    ) ], Y: Z# }+ ^& O7 A; z( ]
  44.                 }# p! a' g3 F1 r0 R
  45.                 if (strncmp(buf, "quit", 4) == 0)$ J* L8 j- P$ r3 W4 h* N
  46.                         break;
    1 I. L' B2 u/ H5 d, [2 I; {
  47.         }
    % K  G! J1 E" y' y0 o/ y
  48.         close(sockfd);9 E" |: K" t, b; f
  49.         return 0;6 y( [& U$ V/ o  J% X. H. x
  50. }
复制代码

8 G8 d3 v6 t" i7 f( }7 ^" l; M: @  n& s! U* i7 _6 D5 }4 F8 G





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