cncml手绘网

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

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

4 D0 b5 ^3 _( e+ ?2 X8 c4 e
" p% D0 D( V0 ~/ J" d
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
% L; T1 g% _2 D* y
+ y; C  B" j! I- E! K0 }0 M5 m

5 U3 }% J: i9 r& A5 ]! WTCP协议3 V" n' g' c: t  D1 f3 s
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
- y+ Y3 d+ B2 Q7 c# L% }4 @  w* e& {+ h; {
; T. [, {' v4 w
关键词:三次握手,可靠,基于字节流。
, v: g) ^0 C/ u7 ]' e& @6 ?0 b
4 O( _) L1 l$ \* i; h
' S% R* T( ]7 m7 D6 ]$ j. z: M, o! H: z- H
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。4 g- Y  |0 \5 y& b
& m) j. ^4 s4 c' z% W- [$ j4 W" z
TCP服务器端和客户端的运行流程& `% W2 [, x/ y: i+ h  A2 \) r) y
# l9 _0 D7 B: s. z% H
7 h% _; o2 y7 T. K- B
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?5 Y7 j' s( E' N$ o
9 M; f& M% m0 q: W! v
; U0 @, z( }: p5 D# i8 k
1.创建socket5 g8 K+ j. R5 H: D
socket是一个结构体,被创建在内核中
/ a2 ^. V( `  b* _  h; J$ h sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议) A8 F. d3 r7 X) b- ~) ]6 Q

* S4 g" v$ k9 O+ u" T. _9 h
2 K9 f1 A6 v& R/ l- @
2.调用bind函数
4 f$ `" a  M- a& K: } 将socket和地址(包括ip、port)绑定。  e. V- ^3 |% N
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序$ u1 o& a2 g: [( P& p
struct sockaddr_in myaddr; //地址结构体
% L) N/ p9 P  z+ l" C: f' s bind函数7 |# h+ d2 _! d# o8 G- v! x; `
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 C9 i9 }# D) g! ?

2 T6 M. O& @& a0 M
  a! k' U' Y: h
3.listen监听,将接收到的客户端连接放入队列  T' I+ y8 I, [8 A
listen(sockfd,8) //第二个参数是队列长度; }. b: Q. K6 q9 q+ m2 H
# _5 I0 G; @) `% q4 M. O
. R8 q2 Y& E# [8 }3 }( y* F
4.调用accept函数,从队列获取请求,返回socket描 述符( y* g; C. E8 D( m# X
  如果无请求,将会阻塞,直到获得连接
( P. ]+ S4 N  q+ l8 `: R  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
' ?2 Y# v, M/ z' f# s% J1 v, W: E+ d+ y5 F! T
" o- r& {$ i. l6 o4 Y' m/ h
5.调用read/write进行双向通信
! H+ p' y$ I" t$ x8 a; P  \; M
# g8 \; _! u3 {5 |) u: ^

. _; ?( @3 V& J; t) v+ J+ @4 O6.关闭accept返回的socket. Y3 e. m9 x7 H: b( _
  close(scokfd);! C9 {0 G: G( P& C$ u# z

1 g2 H; i+ `$ E6 x# D" A, j) t* P
" `) {/ T2 _2 ]
7 E, M! t. A0 H

; s9 v, p. v# P  O+ J4 K! B# H下面放出完整代码5 _# T% D* V7 `' O1 V* e
' Q5 `' N, N! [" q
  1. /*服务器*/
    . {% V  _" g# j7 S; X# e
  2. #include <stdio.h>
    9 C1 p. |* }1 y9 T. ~
  3. #include <string.h>
    * F4 b9 P$ W, j$ b' U
  4. #include <stdlib.h>3 n7 b7 y3 J& n5 q8 f' r
  5. #include <strings.h>4 r- Q/ D  F% P
  6. #include <sys/types.h>
    . U! w+ ~# x! s# h
  7. #include <sys/socket.h>+ ?) w& a, F. l( f
  8. #include <arpa/inet.h>$ `& _- G, z* T
  9. #include <netinet/in.h>
    . w0 p0 E& i. B, y  Y
  10. int main()5 v+ q3 `" S- E
  11. {
    7 H: u6 J7 r0 ?; V
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字  {' E- ^& Q0 j; P
  13.         if (sockfd < 0)
    3 E) d: }7 B. H0 L* S, D# [
  14.         {
    % ]- c5 X/ \' R
  15.                 perror("socket");
    $ u9 x! z: w3 `6 [
  16.                 return -1;
    : S# i) S3 f, y9 m  F5 I5 d
  17.         } //创建失败的错误处理# h2 i& q% d% G- k, p/ Q. }: {
  18.          printf("socket..............8 }" M! n/ }2 M. ~! t8 V( B
  19. "); //成功则打印“socket。。。。”/ V/ |# _. s/ D
  20.          - |" p6 E' _/ Y
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体! ^% c) c( x& d/ b, L& _7 O+ o
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    1 _# C; v. \# o% @" B9 J
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    : S8 G; s' \  v
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    4 {4 s# w4 J9 Y
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址; ?3 N5 Y7 I3 e* t, S8 |
  26. 5 H( z3 ~: O5 z/ }" J& N& S' _- Y" d
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字( P, d$ T' a, w8 @& J; C1 ~
  28.          {
    $ Q3 t4 o/ C- m
  29.                  perror("bind");
    . `3 h" I) Q5 ^
  30.                  return -1;% x/ h2 n8 \' C0 e1 K
  31.          }1 G6 P* |* L3 y, `+ ?9 w  p* v, ]
  32.          printf("bind..........6 X& O- n5 \! d# u7 X% t* {6 [5 w
  33. ");
    0 }' K" q9 v1 \/ P; O% v8 Z
  34. 3 J. ?4 ~  G8 u0 x! F! X8 h9 Z
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听* {9 W& d2 M) C( F/ p" ^
  36.          {1 k6 F+ P: A9 g, J
  37.                  perror("listen");/ s+ }4 U8 E7 a: g' z3 g  T
  38.                  return -1;
      D: {$ f2 K; k; S
  39.          }
    . L( z" L, Z" C8 h4 O
  40.          printf("listen............
    , _* g6 s3 b4 p  y. ^& l9 }& b
  41. ");. J  t- G5 [* a/ E' ^
  42.          - s" ]9 A7 o& Q+ d) Y' v% |
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求1 U! m, b# {& [% }2 H  G2 i- i
  44.         if (connfd < 0)
    7 B5 t+ ?* M% C' e, E
  45.         {$ w/ I# [9 D" z* B
  46.                 perror("accept");0 }* l9 S0 y, n# d
  47.                 return -1;1 G5 G+ ]* s2 j9 P! s" I  o- j
  48.         }
    , |7 e& s) B* f; B$ `# f  K
  49.         printf("accept..............
    1 L) g5 P. |) A4 V# X8 N/ c1 h
  50. ");: Y# ?  R1 I- z3 q$ A7 g7 b
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    . {/ V* h# Y3 l7 ?  M% X
  52.         int ret;" X! R! p3 O/ O! ^" b; ^2 [! i  t
  53.         while (1)7 k% W8 e! Q# v8 \! w0 X. J
  54.         {9 X2 I  g' ~% f. B/ E
  55.                 memset(buf, 0, sizeof(buf));- i& i. r: r0 g. ]5 l' P# z4 H
  56.                 ret = read(connfd, buf, sizeof(buf));$ K8 R3 H* T. R  o
  57.                 if (0 > ret)* T* ]7 P9 `1 h$ b4 s0 K5 }
  58.                 {/ B2 W! F6 x( S2 Y' k# W5 Z# E
  59.                         perror("read");$ @% M' f( }3 v% P+ y/ t0 n; N1 N
  60.                         break;
    1 V# Q! t' s, S2 m! @+ a/ z+ ?
  61.                 }//执行while循环读取数据,当0 W3 ]) Y& ]. A6 `
  62.                 else if (0 == ret)
    9 p2 l& v; m$ \+ g+ p8 t
  63.                 {
    # A1 L# \# O$ R
  64.                         printf("write close!
    1 u: d$ A+ ~8 J3 R
  65. ");
    . f* c9 b  E6 S7 v& M
  66.                         break;3 V. f$ t; r6 {) E
  67.                 }! \2 H/ Q4 l, R4 Q( i
  68.                 printf("recv: ");
    0 [6 @, Y' @$ B# E+ u. m
  69.                 fputs(buf, stdout);//打印接收到的数据
    1 ?3 ?) ?0 v$ {+ z
  70.         }
    # D* B0 R5 e- Y9 Y( o7 B
  71.         close(sockfd);//关闭套接字
    9 J: s$ i0 ], i, c/ L" R" _: R% J
  72.         close(connfd);//断开连接& D( x) J# }# f) f" u4 y- p
  73.         return 0;
    4 b2 A/ I: B) q
  74. }
复制代码

9 P; E( B* w: D) F/ T7 [' m7 P& ~1 R9 `1 S. b: \$ i9 n" @
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    8 h0 [5 C$ m  q/ S' ^  o* Q
  2. #include <stdio.h>
    ! M) k! z8 I8 E( |5 S' o/ o4 s6 @, U
  3. #include <string.h>
      Z' F1 S/ R: ?2 r  Z6 y) E
  4. #include <stdlib.h>
    4 J4 D2 X6 E2 z4 [
  5. #include <strings.h>
    : M/ T, q! M1 f7 _& y* i2 \* L
  6. #include <sys/types.h>
    " O; E& v+ Q1 o: z" i; r
  7. #include <sys/socket.h>( l5 E" H8 Q% ?5 C& X
  8. #include <netinet/in.h>
    4 b  ^% B; Y* V" m  j2 ?
  9. #include <arpa/inet.h>
    9 h/ p; ^- J8 D6 v: \  u* C
  10. int main()1 x8 d4 p, \! }' K7 k5 K) W, \$ ?
  11. {
    , ~8 ^, x2 q- k5 W1 @
  12. int sockfd;+ ]' B2 A3 ?. T5 _( S
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))0 k5 X9 q* [; ]' z
  14.         {
    7 ]5 p7 D- ^# v/ @) b& D" T! S; P- I: w
  15.                 perror("socket");
    - q2 A' b0 ^( ]. L) w, D
  16.                 return -1;8 o/ @5 ]% a" N+ @- o
  17.         }
    : u( [/ D9 Y7 w. {
  18.         printf("socket...........% L. m4 o5 l7 p& `( Z
  19. ");& X# K' `9 A" ?) H
  20.         
    / @0 l. T8 `& z8 y. c( K. L
  21.         struct sockaddr_in srv_addr;7 B5 K! z5 y( I4 x% W, s
  22.         memset(&srv_addr, 0, sizeof(srv_addr));- @0 ]$ q6 n  G, P) v; \& R! }
  23.         srv_addr.sin_family                 = AF_INET;
    9 s$ |( o, P% z: w
  24.         srv_addr.sin_port                         = htons(8888);7 |$ O- V1 c7 _2 ^5 B: i
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");) C4 U" n/ \' v2 S+ v
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    . q4 o2 r2 u3 b1 X( F  _
  27.         {1 v# B5 P0 Q7 \" f( j
  28.                 perror("connect");, ~8 W. D3 e( N" j2 I" J
  29.                 return -1; //exit //pthread_exit" b- I0 l/ j, U4 u7 @( ?
  30.         }
    1 q8 B- d; q: h* ?$ G3 A* ?
  31.         printf("connect..............
    7 X' f6 f# s$ j1 Q7 h
  32. ");
    . y% b: y& q# b  f
  33.         char buf[100];
    2 O0 W7 e/ a/ y- |( m7 W
  34.         int ret;0 d! G, }( G! M8 w4 _, e! g) E
  35.         while (1)2 E9 _" y7 a$ I, V
  36.         {
    . `4 L" }3 q$ E8 F- Y
  37.                 printf("send: ");0 `# h1 j% }' f' F0 K0 l/ p3 j
  38.                 fgets(buf, sizeof(buf), stdin);
    * d: p5 W- k- f' T1 `+ C4 s) H
  39.                 ret = write(sockfd, buf, sizeof(buf));6 o% A* r6 N$ Y) O& U* Q) k
  40.                 if (ret < 0)% u; l: T2 M) J5 x. P+ j! d8 h
  41.                 {5 m" s- i% ]+ h1 O9 c) ?: ]6 r
  42.                         perror("write");. V/ g# z7 O8 V. y$ ]
  43.                         break;
    1 I* U  {( w1 y
  44.                 }
      {3 h5 G4 Q+ i* M+ X& y. S
  45.                 if (strncmp(buf, "quit", 4) == 0)
    , V% t% u3 ^4 j
  46.                         break;
    + _6 W  C! |! g" a1 y4 Z
  47.         }5 h* s5 ]6 Q7 E/ M9 R
  48.         close(sockfd);( l4 |9 i7 O. X! \# _5 W9 J
  49.         return 0;+ T' B: h, J9 Z
  50. }
复制代码

+ Y. Y2 w5 c, O1 g# @! O6 Y5 @
0 S: ~! V( A2 l4 A7 U3 B4 [- w




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