cncml手绘网

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

作者: admin    时间: 2020-5-9 02:09
标题: 自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。" W* C0 `5 M7 k. |( a( O. Z

' [3 _; y+ d: h% C* ]

) k+ C2 w: y/ Q$ A. y2 lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
" \* C2 Z4 x  {7 l+ Z: Y
1 ?- [6 h  Z( Q" D( L( c
9 X% O7 u9 _. x9 C0 R8 S) f; R3 R
TCP协议
1 M' l) X/ A7 o, Y6 q) b9 f# ETCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
( d3 u( _3 I" j- c  b- a
6 D) r% [# K1 U
! r5 {+ e4 o, e! y1 W2 g; N
关键词:三次握手,可靠,基于字节流。
0 [) d2 r' C3 P5 X1 v6 l" _
$ {# ^2 u) l" f6 [
+ j4 @) |" h: K; [- P( b
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。* [  T/ a7 m! Q  W* y: G

0 j* n& m9 L2 C9 ]  }2 F: K5 j; TTCP服务器端和客户端的运行流程
! ]) v# S$ K  @4 q7 x5 n0 m; I, |9 {4 |" D# t

% n+ \. J6 p( {9 V/ {; K5 Z如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
0 W, T. y$ o# X/ ~' C4 U, }% e; }. g% I
* a; o/ n  J$ R6 ~
1.创建socket
: k4 r( I( u1 E2 n. ^7 t socket是一个结构体,被创建在内核中
% J) ~" v9 T( {6 U* X sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议! k, T2 v- y9 B2 c3 b/ ]; Y/ K3 D5 l

8 x" s4 o3 C& z- r& [: v
6 h' t& v! G9 k" R- a* g$ g; t; T4 G4 @
2.调用bind函数
4 K5 F! Y4 C* j$ V* k 将socket和地址(包括ip、port)绑定。
. h$ c, M6 _/ t/ c( Q, X0 \& M7 \, ^ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
' O2 ^1 J' N7 X) ^5 V7 X struct sockaddr_in myaddr; //地址结构体" s- m" q" y. d' a5 f
bind函数
  e5 Z4 T' L4 [, {9 A" c; \8 e bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% o; W4 h0 ]. h
$ f. c1 \& F6 I, Q; p+ b

& U2 j8 N( W0 h6 ]* u: c3.listen监听,将接收到的客户端连接放入队列
$ i. v8 q0 e0 W3 T$ `- x8 j listen(sockfd,8) //第二个参数是队列长度; E  K) D1 n1 d: m! d
% a& n: F! h, b  `) o3 H
5 s( ~3 R6 y) B5 M1 v
4.调用accept函数,从队列获取请求,返回socket描 述符
- Q# R2 C4 J9 D0 c5 p  如果无请求,将会阻塞,直到获得连接0 _' c( ?0 @4 m- x% Z+ _
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
9 p3 B4 j  ]* c6 N4 U9 d& H; G; h7 Q4 G# @* T

6 _) N& K4 j' I5.调用read/write进行双向通信
4 W8 v0 i6 L, P) K0 S: \! \+ m" P/ o" t: T9 S% v

6 f5 T" ~% G6 Z. K( |6.关闭accept返回的socket5 r/ l4 N% G+ |% u
  close(scokfd);
1 ?' j$ Z& X1 a; R1 j
" H6 x! f4 x& K/ @0 N+ ~

6 X3 o) |3 s* i1 V" m7 j+ S. J6 K" ^% i; a! c4 E# e
) B/ S. q' L$ `7 u$ d& U7 ~
下面放出完整代码
8 [$ |; [( k8 o# s# |
$ P3 n# g4 O) i% Q0 c
  1. /*服务器*/( k6 M! `& J4 t3 [' L
  2. #include <stdio.h>' s6 K' ?0 A" q+ @6 R
  3. #include <string.h>3 V9 T7 [) K9 ]# W, N( z
  4. #include <stdlib.h>
    6 H& \! w* H$ n) |5 A4 P
  5. #include <strings.h># l6 h8 @4 c( W
  6. #include <sys/types.h>; K! G+ x- w; G
  7. #include <sys/socket.h>
    : a+ n" w" p1 [' I9 E) _2 O
  8. #include <arpa/inet.h>- q9 u* f: D# j; X
  9. #include <netinet/in.h>
    / k0 F/ Z: U% a* ]* G+ u
  10. int main()! |' d* N* n( s. U
  11. {
      V. V7 `5 x% u4 L4 P
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字: w  O% d3 `. k% E" m
  13.         if (sockfd < 0)5 a9 g: q9 N. T9 z
  14.         {, p, F+ t" x1 R# `+ Y' I
  15.                 perror("socket");
    3 {- ~. R, y$ F1 ^, n# G
  16.                 return -1;( |. Q- {/ x- r9 A6 j/ M  Y. K/ O
  17.         } //创建失败的错误处理+ D1 `. d9 V+ ~* E
  18.          printf("socket..............
    $ b& W% W" q  r( w' M$ {
  19. "); //成功则打印“socket。。。。”
    4 S3 d8 j9 h5 C8 j
  20.          
    + v4 I2 Y: B7 u* N7 I7 c
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    5 Y$ K) T' a+ e- I! n) b
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见); }7 E# j6 u  F& n: s" m' K) g
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型8 d6 Z5 K- Z* N; m5 ]6 o! W  s
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    " U, j: M0 O0 C( A/ W
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    2 W( l0 I- ]/ R

  26. " i4 d/ D( n2 ]5 B& V; ]) h7 S
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 i4 K1 N: W$ q$ k4 X# W
  28.          {
    & s2 ~9 _* E9 x! Z0 E
  29.                  perror("bind");1 ]6 ?! Q9 K) q% r
  30.                  return -1;
    - a$ a; X/ p- W4 C2 I
  31.          }- a' }+ [+ r0 Z. S4 f! i
  32.          printf("bind..........3 w/ r7 ~$ H- i5 U  t9 d- t
  33. ");
    " P+ R2 a' I* \; w# s- Z1 J' `& F  z

  34. 3 ~8 H4 L6 H0 k2 x2 v
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听, f# n+ j. r2 {3 X5 ]5 M6 F6 E5 F) @
  36.          {% W: h  T$ N) P0 ?3 q+ g1 R4 h, C0 P
  37.                  perror("listen");
    4 q( N, t/ c8 N8 v5 h- D
  38.                  return -1;
    7 G2 f# t# R% k' m" t; W$ V
  39.          }
    4 l# Z4 ?$ _7 C3 n
  40.          printf("listen............
      T; L- u! i- p# S: ^; Q
  41. ");- _1 D+ ]$ a' e4 I* p
  42.          7 A+ l  d; g6 V) ~: y
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    5 Z0 q5 C1 [/ Z: L4 z
  44.         if (connfd < 0)2 z* ?0 F" s( w
  45.         {
    ; C( ^) _4 \; y, i+ ^& m. Q
  46.                 perror("accept");
    4 N& A$ C$ W( H2 A
  47.                 return -1;
    # E3 K8 p3 e0 `' e3 D
  48.         }) ]. W: \4 r; a* u" z9 k
  49.         printf("accept..............* O2 y5 X% [: o7 n! O1 o- V
  50. ");( l8 t0 N# A7 A& ~: M" z7 n2 g
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    0 r5 F. M( h/ t' c: \" W
  52.         int ret;- c% w% f% b6 W% M) K+ f" w8 g5 H
  53.         while (1)
    % i; Z7 U; g, W# D
  54.         {) x( `8 t+ p  p3 Z
  55.                 memset(buf, 0, sizeof(buf));8 a5 x4 B# G) W0 X5 G1 P
  56.                 ret = read(connfd, buf, sizeof(buf));/ S" {  R1 i+ r# |
  57.                 if (0 > ret)5 Z  G, q2 ^$ Y4 |9 p
  58.                 {
    ) C% U$ c7 F, E: m- n
  59.                         perror("read");) p! b! B" E( N+ j( x; x
  60.                         break;
    . R" p7 D9 r+ H1 U
  61.                 }//执行while循环读取数据,当/ Q7 }$ X6 t* G% `& p
  62.                 else if (0 == ret)
    8 K  ]5 S6 h! E
  63.                 {
    % t$ r/ B. [% u4 m3 B* _- b
  64.                         printf("write close!
    % R4 m( @: A) H1 x" m5 V
  65. ");- u, C" N" a3 f; f8 y* R
  66.                         break;
    ) B* j/ a$ b1 u9 T# U9 C/ Q8 r
  67.                 }
    8 M# B( ?$ Q5 d# }
  68.                 printf("recv: ");
    " L; H+ F- V5 N$ L
  69.                 fputs(buf, stdout);//打印接收到的数据
    ! V  l5 e5 t0 |8 s& I
  70.         }
    % P, D1 |  [$ V; f; P9 t
  71.         close(sockfd);//关闭套接字
    5 n: B+ f( L+ Q8 i
  72.         close(connfd);//断开连接
    ! m* D0 }; a9 c6 m
  73.         return 0;; l  a* ~, t  v( p, a; j3 J
  74. }
复制代码

; S8 p) |, b  ]- g1 \+ i9 _; U) E8 Z0 m7 }* @; L
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    ; F. G/ u2 z2 u* n
  2. #include <stdio.h>
    6 c3 b4 ^% y- T; Y- H/ C
  3. #include <string.h>5 c1 }, i: [  b& J6 b
  4. #include <stdlib.h>
    ( X. {9 ]  x4 N0 H3 ]7 R( t0 \
  5. #include <strings.h># c0 T0 K+ h+ s( @1 o  Q0 p4 ]
  6. #include <sys/types.h>
    ; m; h/ L2 `3 ?; a# }& }0 Y
  7. #include <sys/socket.h>
    6 B8 S) b( b* w7 Y; V
  8. #include <netinet/in.h>
    / e$ _6 p: N9 e  x4 o
  9. #include <arpa/inet.h>
    # t, K  g+ P6 L: o
  10. int main()
    1 d2 O, a: y, p* p( f- @
  11. {2 _) A5 s6 a* Q
  12. int sockfd;
    9 h) E+ H2 |' i  D  ^! [2 }# j' {
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    # h9 G& Y/ o$ O% P$ F' P
  14.         {- x% k4 P. C0 q: H4 ^: C6 t
  15.                 perror("socket");
    9 ^) W  |0 B1 w% E7 {" I
  16.                 return -1;
    9 \5 V2 g" I$ ]; }6 g8 I* x
  17.         }% [1 Y) K9 r/ N! l
  18.         printf("socket...........
    6 m0 F4 p/ K' X4 ]6 O! h6 A
  19. ");# P  r0 @0 w& u+ E7 B! g1 J( Q
  20.         
    7 O6 N+ G( T" s' I  c
  21.         struct sockaddr_in srv_addr;
    . m  C  J( ]: y. I( y
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    9 u/ ]+ N% s% @8 _
  23.         srv_addr.sin_family                 = AF_INET;
    5 \; n& B3 {9 b1 v  D9 w4 l' y; }. P
  24.         srv_addr.sin_port                         = htons(8888);% r* U9 @4 T( D, g, Z) g
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    9 Y5 h- Y3 s- |! U; v1 ]. v
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))* a; n2 d* i8 t7 k. O) i
  27.         {; Q- g6 }7 Z" \- i! L! y" ?& @
  28.                 perror("connect");
    ( W% e, j" e: d) Q
  29.                 return -1; //exit //pthread_exit
    $ X+ ?6 n& q$ m9 K( O
  30.         }! _( F8 X+ {1 h+ {0 R5 s
  31.         printf("connect..............
    % b; Z1 k2 G4 P, u. l% `
  32. ");
    . N& o: T* i' p0 K  K0 ^
  33.         char buf[100];
    2 F* Y( e# a& v* B+ h
  34.         int ret;, x8 J$ W% ]* q$ q* h( a( i8 a0 K) t
  35.         while (1)
    6 F+ i2 e/ F' o5 G8 `
  36.         {3 C# H/ X8 R) E) w+ M+ `
  37.                 printf("send: ");
    - P0 ^1 x/ H" _. x
  38.                 fgets(buf, sizeof(buf), stdin);2 w0 n( V+ L' ?
  39.                 ret = write(sockfd, buf, sizeof(buf));
    + C, l/ q2 _2 w2 U8 N# C  s5 K
  40.                 if (ret < 0)
    / L7 z3 m  K" ]* J/ J) c) [
  41.                 {0 i1 Q( c: ?: ?( [7 a- G( i
  42.                         perror("write");8 R" v& ?1 e7 y6 }. b  Z; ~
  43.                         break;1 q5 Q4 g$ J6 F# p) X+ k: Z; m
  44.                 }
    & q8 i5 u. _) k) ~+ v- U3 h$ B3 O
  45.                 if (strncmp(buf, "quit", 4) == 0)
    $ Z4 K( m" p% _1 r+ k1 Z% L
  46.                         break;* j- k0 x+ P( v, Q: @
  47.         }7 g* w$ [8 Z, k7 e6 \& R' t
  48.         close(sockfd);7 A: i( h4 @. B4 X2 J
  49.         return 0;) C) b7 h$ C9 [1 g
  50. }
复制代码

/ g, M0 ^  e; I7 E6 o3 l2 F7 `/ F+ d) [& k' t: z  G1 V





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