cncml手绘网

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

作者: admin    时间: 2020-5-9 02:09
标题: 自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
& [* j. X  e7 d7 L# W) D  V' O- Q
% [; O: Q' [9 o8 Q" f- q: p) [9 V) H3 \

  B: Z4 t; ]( _8 E; @4 rsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
. i5 W# a' f/ r- s0 O$ @0 h$ l% y1 ~* i3 i* H3 S
% A4 I3 c. Q. L5 t3 n$ V
TCP协议
* e' L1 ?$ M( n" l, u# u$ y, MTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 J+ U& Y: q- h5 ^! j( ~
; L/ L, V6 Z) e; H" c
+ K' T$ r9 H# a* X+ t( V! c( |
关键词:三次握手,可靠,基于字节流。
/ b6 W: u6 R8 l) t5 D; B: u
' M3 T5 a" y( N  W& y% L; [9 j+ X
5 w+ w1 g3 y0 V1 T
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
+ B; ], f/ i/ {- J' } ' S- h: ?0 T5 F; w) `: g9 D# S
TCP服务器端和客户端的运行流程
) S% v$ v( M' k# c  ^! h  j
: \) G( Y/ F  d4 X- i: k
8 a0 f8 v* o. P; N5 e
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
1 S+ [7 S! S/ {! l6 k$ ^* X+ b3 F: |' S/ p; l: }( {9 ~2 g( F
$ j& V3 |* n( |$ N2 o& j8 j7 c9 V
1.创建socket, M% T; Q4 G( h
socket是一个结构体,被创建在内核中# n$ M2 A. r. P4 X0 j
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
% `: T/ Q: P9 W' G& j' r+ I6 D3 R) F( b0 @

* ?2 z+ J" }3 B9 u! f7 |, g+ ~2.调用bind函数
7 U5 E5 n2 h0 P  @ 将socket和地址(包括ip、port)绑定。
. x0 j- A- D3 ?6 ]+ U3 H 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
8 r6 I6 w! c, s) R6 h( ^0 |8 s struct sockaddr_in myaddr; //地址结构体
5 b1 |5 L$ P/ h& l bind函数" g* l3 a7 K# ?, `. s3 Y3 {2 M$ g
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))' y2 u# l+ R7 @# {( ]
5 T, d5 V* ~' R6 H
- M8 ~! {( ]2 q2 j6 b7 e' ~1 H
3.listen监听,将接收到的客户端连接放入队列: s7 G. F& l& N  t( c1 R% i9 o
listen(sockfd,8) //第二个参数是队列长度0 W$ q- K9 y1 T1 w
7 J) o$ @! T5 m* [- k) A& f
* ]/ e& ]. `7 ~$ ]* l6 I
4.调用accept函数,从队列获取请求,返回socket描 述符  e% t$ t. e" o6 [
  如果无请求,将会阻塞,直到获得连接" s9 L7 L) |% a
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
- N: \- [% Y0 r) s8 G: T  f/ _6 ?2 F  @1 ?

4 U+ L2 C0 p* O( B$ O  O5.调用read/write进行双向通信) ^0 U+ z4 R8 |
9 x8 e* f. l/ D6 [; U+ T# ]2 ~
% S, t6 W, p" L1 i) ~. n- B3 ^8 N5 U
6.关闭accept返回的socket  L( D  s7 q* W: s, \- U
  close(scokfd);, }, j; O& {4 W7 B* Z
2 m' C4 f; Z9 p( [/ a* [

  M: G% b* B. ]
3 v4 b$ d" c! o0 |* [! V: h% E

. k. a6 ]1 P5 e9 O$ \0 ^下面放出完整代码9 }/ j0 _8 D8 Y6 R7 B1 H9 l* N# g

0 D7 ]# L- b  @+ B- f( [* S
  1. /*服务器*/
    & q& D0 F8 s* X
  2. #include <stdio.h>
    ' X: x: _1 f- F2 c
  3. #include <string.h>0 @4 e" |1 B6 [" w5 G7 B0 _
  4. #include <stdlib.h>, w" Y! E9 x4 v8 `9 Z
  5. #include <strings.h>
    & N9 r3 k" V( [5 D$ J
  6. #include <sys/types.h>
    - s, s2 Y4 J, }7 U1 o
  7. #include <sys/socket.h>" V; I- s, U/ R/ ^) W
  8. #include <arpa/inet.h>
    $ w. Z/ m" U3 [. s% ^
  9. #include <netinet/in.h>, n8 w0 @5 _) h: r) {. q
  10. int main()1 C, j- d- y5 a" k
  11. {' A8 `4 e* P4 U5 D
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    . |! S9 r3 v: X- `
  13.         if (sockfd < 0)
    9 m; s* W1 P2 s* r
  14.         {/ i! _& a1 E: Z
  15.                 perror("socket");% n  k5 t: y; o. o
  16.                 return -1;
    7 p: e( Y) B/ _4 t# B. o4 Z
  17.         } //创建失败的错误处理2 \3 V$ z. P1 F1 Q7 P7 U: B, J9 g
  18.          printf("socket..............
    ! r! h5 z# j- W2 w# j
  19. "); //成功则打印“socket。。。。”. V9 [& D2 _4 T" N
  20.          ! v3 `$ N, |$ E, Z- \. a
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ! |% X- L& N, Z! P! M  ]
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    + Q- A1 H, T/ E# B2 R6 h( M* ^/ o
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型7 \/ ]" s" A6 y" F+ K9 Y
  24.          myaddr.sin_port                 = htons(8888); //选择端口号+ a! @: M0 ~( j! k! \* R/ p0 d
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址1 @# G+ ]2 Q% j  x' Y( g/ k+ C# ]

  26. 3 R0 G4 U. X+ ^6 x& T, P
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    3 E/ i3 Z  P+ Z$ M' E
  28.          {' P. i2 K5 ?2 u1 a( g$ Y) P
  29.                  perror("bind");
    $ {+ K$ Z0 W% ]5 e! ~
  30.                  return -1;
    6 k  B3 G  T& D$ X0 C% R
  31.          }
    + R0 B! i/ Z3 F8 y
  32.          printf("bind..........) K! l* M, l( p" B4 c1 m. p
  33. ");: Z' c( L$ v% P3 V
  34. ; y" D* W" a0 ]
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听/ w; L1 [1 r9 X
  36.          {  {/ T% d, B6 J
  37.                  perror("listen");
    # q, w/ k- Q7 `4 {! S' P
  38.                  return -1;
    . V! `+ b$ V5 ?! X& X0 h
  39.          }
    ! w) t0 Q8 t; U. ~" n
  40.          printf("listen............
      k3 a0 D. k8 m2 T+ d
  41. ");0 \8 _  U, O* H! ~/ ]% \* d" q0 q
  42.          
    8 e: k/ z' V* _7 G; X$ l; u8 z
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求0 x; Q- r' n5 Q' e( D, B
  44.         if (connfd < 0)
    6 k: v7 l- i0 ^7 n+ P
  45.         {
    - Y5 H  @' n" }% A5 g9 P& g
  46.                 perror("accept");
      s: a! ]& U. A  L) i0 ^& }
  47.                 return -1;' y/ y6 y4 x" v: ?9 t, E! C
  48.         }
    " ]% r: X: f9 \# W$ u2 O
  49.         printf("accept..............
    3 L/ r7 F9 b8 }% F
  50. ");
    1 T' s0 H/ {, ~$ }, Z
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    . D4 k5 p& [0 K. e0 h5 Z
  52.         int ret;4 s& L0 ]( A6 H: E4 g$ {. J; j, A0 \. x
  53.         while (1)! L. h8 n$ Z5 R" @2 P
  54.         {
    0 G/ W1 \& ~+ h: i7 j' d5 l& p
  55.                 memset(buf, 0, sizeof(buf));
    - R8 r8 g6 @" I: [$ k  c* L4 m
  56.                 ret = read(connfd, buf, sizeof(buf));
    & b: a" z- `/ [7 |1 t
  57.                 if (0 > ret); D- z' q9 j9 x# z- G
  58.                 {& c, p2 {, R/ O+ T, q
  59.                         perror("read");: O0 [# Y  [: ^1 Y- f* D2 ^
  60.                         break;" t( P& ~+ Y$ j5 H3 |7 J; _
  61.                 }//执行while循环读取数据,当
    9 i8 a7 z$ Z+ z4 }* x6 t- I
  62.                 else if (0 == ret)
    7 z# W6 r: R; J
  63.                 {8 t4 }0 J+ h" ]( _8 o
  64.                         printf("write close!6 Q6 i* l) Q* t* E4 W3 o, z
  65. ");/ Y3 w# c- T. }- D9 P
  66.                         break;; s* s8 O7 [/ w  R" b9 Z
  67.                 }2 V3 p* a" J* _4 O
  68.                 printf("recv: ");# M! q* ], [! c2 |, B9 Z
  69.                 fputs(buf, stdout);//打印接收到的数据" l, n7 z7 B6 \% }0 {. v/ R" X2 o
  70.         }) j: B; r' v+ @/ A4 Y5 b' j/ |
  71.         close(sockfd);//关闭套接字3 ]3 r, v, M" R
  72.         close(connfd);//断开连接! `* L3 a; k) _! B
  73.         return 0;4 P+ Y2 i% C$ o  N& h
  74. }
复制代码

- ~! H# D. U( M( o- h& G3 H
% K" X  d3 H) d8 w8 {
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释), v+ I7 J% ]+ c9 j. e0 g+ V# E
  2. #include <stdio.h>0 T" t5 y' D6 I! D6 ?( A$ X  d
  3. #include <string.h>0 ~9 {  H2 j, ~4 n" \% M* N$ _
  4. #include <stdlib.h>, m! |$ ~7 ?+ u7 u/ X' P- a+ w
  5. #include <strings.h>
    / m) b5 y! X2 K; ]  B
  6. #include <sys/types.h>& }/ r6 B1 T8 }
  7. #include <sys/socket.h>1 F/ A% S4 B; y
  8. #include <netinet/in.h>
    $ f9 D& h$ s9 t! _' c
  9. #include <arpa/inet.h>
    % \& z1 p' \2 R) i7 l+ J# m# [0 l
  10. int main()
    5 _8 Y) U" c! i; R, i5 H2 P$ ^! x
  11. {
    ' k" E. j1 O) j, }" H
  12. int sockfd;! V6 n" B0 R0 u) v, X% Y5 p
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))) n. s5 k1 i9 Y! \! e0 Z6 r' g
  14.         {1 z0 o. {0 J8 e
  15.                 perror("socket");
    " ], s  V  M- C$ H" V1 b
  16.                 return -1;
    1 Q; L% x3 A$ G3 Z. a7 |
  17.         }
    4 H) L/ {6 w5 v4 O8 W, d
  18.         printf("socket...........$ l5 [9 t; H/ K& H5 `: r/ Q# \# \
  19. ");
    9 P$ K( m9 k& H5 u; K
  20.         $ u8 K- U5 F5 T# y5 B
  21.         struct sockaddr_in srv_addr;$ b3 F7 x; A* l; R$ v/ \
  22.         memset(&srv_addr, 0, sizeof(srv_addr));2 d/ I# M" J, p: _
  23.         srv_addr.sin_family                 = AF_INET;
    9 g7 P) F" T. O, k$ a6 S
  24.         srv_addr.sin_port                         = htons(8888);
    * S) C' ^: i! {7 B( V3 ^0 ]5 v; N
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");' y1 Z" G. i6 G, x" N0 H' J/ [$ J
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))), u2 n* T7 q; _9 h7 z3 I" ?+ h, K: |
  27.         {
    % F( I" d  U4 r
  28.                 perror("connect");
    : a5 z: B4 e" O/ B: d' t  m" k
  29.                 return -1; //exit //pthread_exit4 D4 t8 N+ s1 {/ u! H9 h& u
  30.         }
    5 E* s$ _: v! T
  31.         printf("connect..............8 X- f3 g2 \; Q  i! t
  32. ");
    9 x7 p& o: j; j8 K; {* q: c9 U8 G
  33.         char buf[100];* M0 {/ K! P# u3 i; ^7 Y
  34.         int ret;' ?) c& H. b' O0 ?5 }5 r
  35.         while (1)1 o: @$ Q+ g/ O! P+ r
  36.         {
    * H' m5 \( m' b7 V: b0 e5 u6 P
  37.                 printf("send: ");# s" _, F) m, p/ T& `
  38.                 fgets(buf, sizeof(buf), stdin);* n) _7 B' C3 w7 Q
  39.                 ret = write(sockfd, buf, sizeof(buf));5 M7 X) X- L# V' N5 A4 E8 R! ^
  40.                 if (ret < 0)
    9 `; b& R+ O# q9 O
  41.                 {" p6 `  M/ [' j+ Y
  42.                         perror("write");
    & h, M4 ?: i  p8 \: F
  43.                         break;, X2 G. j" H1 n- p
  44.                 }
    ; s4 y* L4 d5 b
  45.                 if (strncmp(buf, "quit", 4) == 0)
    * e# v& _8 W2 {7 g: i9 s1 H
  46.                         break;
    1 n+ V1 Y4 K; i7 W- |7 L- d4 i
  47.         }- Y' d3 l' o/ u
  48.         close(sockfd);( {1 k3 Z+ [# V4 L3 ?) k% |' f  [
  49.         return 0;6 H7 _# N( {) Y) [- q
  50. }
复制代码
- w. ^+ T% ?& W& N8 Q

+ R$ ~& N0 h( v: U, y- t9 w




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