cncml手绘网

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

作者: admin    时间: 2020-5-9 02:09
标题: 自己动手用c语言写一个基于服务器和客户端(TCP)
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
* H; H5 |6 J! s7 Q# k/ u1 o: ~9 d' N" W1 r
5 b0 y' W! E$ G: s9 P
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
# k5 v& p' H) r3 f/ p# H$ I0 M* N* I1 Q# S1 I/ v+ U

& |* ~" e, B$ ~' lTCP协议! x% r5 E( R  P
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。% {8 ~# _5 k. n5 Z" y

! j$ A; n+ V  r' E0 ?
6 ~# w# {( N" f2 \7 j3 i
关键词:三次握手,可靠,基于字节流。
! w9 ^0 |1 M" B
& W5 o# Z$ ]- e  J! |* I
* X- z6 `& l( a/ x
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。( H5 x; m  e, W1 r$ h" a9 i/ X$ v

3 Z; Z9 F4 J' r/ N. l1 sTCP服务器端和客户端的运行流程$ }$ S* x% Q2 }- J  n  z

: I. w. R' T& P* t, N; d4 U
2 W+ O" Y3 G/ [8 i# {  _! w9 X6 B( a
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?6 r9 |, \4 C3 E4 Z/ m

1 Q& _- O( y2 y& _4 u, `

! |( X6 q0 v2 K1 o1.创建socket( f& Q+ h7 ^: ~+ q) i: X$ i
socket是一个结构体,被创建在内核中
( y7 v, b% i4 v  T  R sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议; @. i4 o, k0 D: n: w% y

! A/ B) J2 h7 h$ X0 t3 [& |

' ~- [+ m3 j. K. t7 B9 F2.调用bind函数5 ^6 I+ _; X9 K9 l. s( T6 n* c
将socket和地址(包括ip、port)绑定。% N6 x% v1 o, S. T- }
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序  u3 d' B, g  x
struct sockaddr_in myaddr; //地址结构体7 a! k9 a! A8 S3 z& ]; y
bind函数$ j, A  P  Y: u. Z' e6 ]0 O
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
6 U5 z; @" v% ?2 w4 b: Y5 j6 J1 I9 v# Q! t9 D

) l# d: x% ]( ?1 @1 C: e3.listen监听,将接收到的客户端连接放入队列
0 K* r# t0 l& ]& d5 d- v listen(sockfd,8) //第二个参数是队列长度
. Y, W- E! v. ~! u, Q: G2 b/ M( w
. M! |! ~. ?+ ]. N: K% t
4.调用accept函数,从队列获取请求,返回socket描 述符4 L7 C% m7 ^# b, F# e0 Q! ~
  如果无请求,将会阻塞,直到获得连接
3 {+ D' V3 k" K+ K+ D7 J  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
: Z5 c# |1 M: Z; b! g. T
: q: o) o0 r; R

% q. v0 P6 E% U1 [$ g# g5.调用read/write进行双向通信
$ O7 W6 w! ~5 E0 s& R3 b2 v
! a+ g: o7 |% N
0 ^) E4 X/ o" e5 t/ z. ^
6.关闭accept返回的socket
( g2 W# d* l2 L/ }" y  close(scokfd);
  W: G  H6 g* p7 p( c) G" K$ U6 [& l% k& s

4 Y' _! O' ?3 I3 B. e6 \2 u
- Q! [1 x8 J2 `
* y5 }3 P% p1 V" |( g1 K& }
下面放出完整代码
! k6 P; N1 G, ~/ D, {
5 W( h, r( @9 J' s- x
  1. /*服务器*/
    ) [/ s3 p& s, `# f0 O# ~' s; L4 Z
  2. #include <stdio.h>9 X1 Q4 v1 v- L$ {2 j
  3. #include <string.h>% _1 o# y! j+ w, }+ t! u
  4. #include <stdlib.h>
    . t9 Q9 g$ g  }. F& `) R
  5. #include <strings.h>
    0 C: z! a9 S  N! p
  6. #include <sys/types.h>4 `8 `' |2 r5 j: o7 t6 b+ h7 X
  7. #include <sys/socket.h>! I: t( k# d  w  I3 ]8 j
  8. #include <arpa/inet.h>
    0 Y4 t$ m; [$ J- o* f' A2 |1 ]& d
  9. #include <netinet/in.h>
    : [6 L5 J& x- U4 D4 a8 Y: b, o8 O
  10. int main(). l% R) D( p- c' y1 _3 l- H
  11. {
    * G0 G" _+ ]$ D3 f2 l9 @! |
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    6 {$ l: B; [/ F+ f/ q
  13.         if (sockfd < 0)0 e- H, D* x7 M1 e
  14.         {% |! X1 z7 u% J
  15.                 perror("socket");; R& c$ o- W2 z6 H
  16.                 return -1;
    * v7 ?! z6 e2 t1 K2 a4 B# U
  17.         } //创建失败的错误处理8 \+ ?% t! T% {/ Z2 g  n" n
  18.          printf("socket..............
    : D, G  E* R& V) ]+ u- P0 s8 n: Y
  19. "); //成功则打印“socket。。。。”. k4 Z1 s6 s# n' [7 d" v! H* [; g
  20.          
    2 ^$ P2 d! D2 i) l" l  X4 ~0 b
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体$ ?; s4 x$ R5 z3 V3 q" A9 i9 m
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见). ]2 g: r& S% R$ i# h
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型4 l6 T8 W& ?+ ^/ e' y6 M1 r4 E
  24.          myaddr.sin_port                 = htons(8888); //选择端口号: X) R4 k7 l# F3 F
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址4 N9 K3 z/ m  F, }: w" s
  26. ; [' D, s+ B+ j" @
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    0 t9 x% d  z" W) V' ]+ j* s
  28.          {9 S1 p2 @2 A6 t5 ?' j3 h  D2 p
  29.                  perror("bind");
    0 R! V2 d: E9 C  ]
  30.                  return -1;
    ( y$ k' B+ O+ ]# s% }/ u
  31.          }
    8 u0 g' s2 f5 T* H6 b
  32.          printf("bind........... n* ~, r3 n  ~; \) n% Z) J
  33. ");
      @$ Q$ Z) O9 F$ `) T) R, l
  34. - T: `3 n. U9 d) g; F; G+ j
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    5 v, ~3 i. M/ S0 W" d- G* I& v
  36.          {# W, |. y" B  w7 E
  37.                  perror("listen");
    5 v7 r6 j) R5 W% o/ w# }/ b
  38.                  return -1;
    - q& U8 a6 g" Z3 }" i6 o: X
  39.          }
    1 P3 m( G: Q" W4 w- y5 v
  40.          printf("listen............
    , @) B2 y% q+ a# x; u9 G( b
  41. ");
    4 f' t  L. y* c2 Z0 s
  42.          9 l1 @! y6 a. N1 w; T7 P
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    6 Z: u0 G5 X* u
  44.         if (connfd < 0)
    2 i% L( t! a  z
  45.         {, S% W) P  v) h/ \) s0 I
  46.                 perror("accept");( \: Q# ^' W5 I
  47.                 return -1;
    ) d& J# ~+ y9 M/ V
  48.         }! {) s8 J3 X  `% I2 R) A( P
  49.         printf("accept............... G" C- _2 d4 f+ s9 ?9 O! l
  50. ");
    ! G: M8 X3 @7 t8 R3 V) A# H
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    $ {9 I0 n6 ?1 Q$ g' ]+ u
  52.         int ret;& t% W# R: `% N, G1 t8 W
  53.         while (1)/ I3 b& E# A0 G) x, l  b0 m2 A3 W
  54.         {/ T3 G( ~) d; [: `
  55.                 memset(buf, 0, sizeof(buf));
    1 n9 `& a7 z# E  R* ]8 R' x
  56.                 ret = read(connfd, buf, sizeof(buf));
    3 O1 F6 ~- \" _3 g  z- G' L
  57.                 if (0 > ret)1 m! U+ B- y8 r  ~. K% [. U
  58.                 {
    ' ~  k- X! k  F! d8 m8 M8 v
  59.                         perror("read");1 F8 ?" b3 [" }& X1 C
  60.                         break;
    1 v$ a+ p: Z% q
  61.                 }//执行while循环读取数据,当
    . s* h! @4 c) W' c6 }
  62.                 else if (0 == ret)
    ( v9 H/ H$ D" J  Y
  63.                 {& [( I+ o; J7 Z& I$ q2 F* c  F. `
  64.                         printf("write close!
    / F3 |% _3 d  f; @4 e
  65. ");0 K) @1 J9 j# X  e5 o
  66.                         break;/ c+ s2 s/ M, U, @3 g( _
  67.                 }& n- c; D' K! W9 ^- `/ }. ?
  68.                 printf("recv: ");2 R% p" w% E/ |- Y; B
  69.                 fputs(buf, stdout);//打印接收到的数据$ ?8 V- x: o3 X) f% i6 |) ]* y6 q
  70.         }1 R- a) b5 a0 p+ {$ |' s7 T( y
  71.         close(sockfd);//关闭套接字
    5 ]) B1 `$ n8 G7 @/ A8 ^, f
  72.         close(connfd);//断开连接" m, \; r+ H( _/ k4 {
  73.         return 0;
    ) I/ O( X5 F% }, J/ |
  74. }
复制代码
4 q, J& \; c$ Q3 G2 H7 l
+ y. U7 \( r, k. E" ~/ X+ x
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)3 k5 ?4 o! y+ I& }1 y
  2. #include <stdio.h>9 s' m) q2 Z7 \; ^
  3. #include <string.h>4 d" Q! N% {; d
  4. #include <stdlib.h>' g0 s! [7 o- Q! X* b4 Q
  5. #include <strings.h>
    & V7 @7 u) q2 ?& q; [) e( R! x6 |
  6. #include <sys/types.h>
    ' i7 D2 @( l) C8 H. t
  7. #include <sys/socket.h>; f) a, h% I. E9 O* L
  8. #include <netinet/in.h>( f3 H8 y% v4 F, d
  9. #include <arpa/inet.h>
    ( i( S; c- e3 x
  10. int main()
    2 n$ J3 M$ @) P
  11. {
    * Y; z6 P% a6 V: N
  12. int sockfd;
    8 L5 d; }- S8 N; M5 J% }8 ?/ f# T7 W
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))4 I" F/ H8 w1 R0 y- @
  14.         {/ h8 \* Y: `( B' J8 C
  15.                 perror("socket");
    ) |, r' f5 ?, ^) M. Q
  16.                 return -1;
    # y- X: i- k! L7 x! Z6 O
  17.         }
    4 U  V2 _6 i. w1 i+ ?( J
  18.         printf("socket...........( j' |! Y/ l. t/ O' n+ p: x
  19. ");" x6 [5 V  L9 r$ U
  20.         : f& O6 A& Z# F& U. `
  21.         struct sockaddr_in srv_addr;/ I& b! u( R, C; }  f
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    8 d+ u: @4 I0 U& V' J  d
  23.         srv_addr.sin_family                 = AF_INET;
    ' U/ S1 C& K3 j0 c" K' r: _
  24.         srv_addr.sin_port                         = htons(8888);
    ! L  w* r4 f$ g3 r4 G$ U
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    0 g* h* J7 h9 D, L1 c; f/ J# P
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))1 ~' Q: ]0 l$ s% @  k; J% F7 y
  27.         {
    % X- n7 ]! l1 s6 t( {: t
  28.                 perror("connect");
    % t* x) G# }3 n, R  e) K- C
  29.                 return -1; //exit //pthread_exit
    ) }3 h7 m1 q- T) K
  30.         }
    - u) M7 }5 A* u
  31.         printf("connect..............
    , w( f+ _6 Q5 V" w# ^3 S  i; E
  32. ");# y) \; f( B7 }0 R4 o
  33.         char buf[100];
      d* z9 P4 a4 o6 N2 D! v0 I. L4 G- i
  34.         int ret;$ a( }) v' \  a) f, i
  35.         while (1)  M# b% k7 U+ c% m4 Y5 [9 g
  36.         {; }0 l# i5 E# ]+ y6 N
  37.                 printf("send: ");
    ! `+ i% G, |# F% T+ z2 @* L, |
  38.                 fgets(buf, sizeof(buf), stdin);
    $ ?/ ]8 [* g9 u+ Z  G
  39.                 ret = write(sockfd, buf, sizeof(buf));
    / ?+ W& q  `  f7 `  p: N
  40.                 if (ret < 0)  e0 ~4 M+ q5 u1 Q
  41.                 {
    ( y0 Z' q7 ]8 @7 w! g6 ?  I
  42.                         perror("write");8 C/ `; q2 r0 s
  43.                         break;' W% S+ H6 ~  U7 Q5 N
  44.                 }' H% `. w! U* k5 R+ l6 i& e
  45.                 if (strncmp(buf, "quit", 4) == 0)
    9 E1 x( y* H* E+ C  n7 A  `! x
  46.                         break;3 b; M( e! k' C( G- e
  47.         }
    / x) T8 ?3 f- y/ w
  48.         close(sockfd);2 c1 Z8 ?1 ~! _; `$ Z" z) X: m
  49.         return 0;  B( D3 `( @' e6 v: W0 C* z6 U
  50. }
复制代码

$ b$ x- u) f" g; `7 U
  Y+ V  q8 K6 h2 e+ x




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