您尚未登录,请登录后浏览更多内容! 登录 | 立即注册

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 17032|回复: 0
打印 上一主题 下一主题

[C] 自己动手用c语言写一个基于服务器和客户端(TCP)

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
0 H! |% w7 V3 P; K; c) n1 o& H
0 x$ @8 |: j7 d, @# T4 l9 K' }: I
: \  e( b; a5 _- L1 u+ y! O9 x2 z
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。1 j2 K9 o5 A, G0 H- q8 p5 I
; b  U! t# Y5 U0 ^6 v& ^
9 `( T- H, A5 B+ ^3 f2 {
TCP协议" B2 L( V0 O; w7 S1 O# P1 Y
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
& [2 ~- G' H0 b$ g7 n0 J- K% V" T- c

7 E* s$ Z3 b, i  \4 T# a0 B关键词:三次握手,可靠,基于字节流。7 C9 e; i" W6 o* N# f7 D6 i2 I
0 e7 N1 o8 [/ ~/ P

, p; q4 t2 {4 C: n6 e: u: d: U可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。9 b) J# P+ @4 _% V. {0 i7 [
' x5 A% l' U+ o* F, I
TCP服务器端和客户端的运行流程
6 A  b, {1 N% p- b/ ~7 M5 T4 E
5 V, Y& ]4 J* `

% W: v* Z4 F, m" w3 }: V* k如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
0 T, Y6 C3 O1 h) \9 l; O. ^
  M; j! I9 _' I8 @+ ^

5 t7 d) I+ b% }6 G! f/ ^1.创建socket+ f) Q5 f. u. w- P9 H5 G
socket是一个结构体,被创建在内核中
5 F& P: v2 K* ` sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 }, w9 v0 d. f5 i1 ~# ^7 s) g$ J  R1 q0 @2 D+ d( \7 r- |

3 N/ Y9 B: o; g9 N1 i' K6 E* c2.调用bind函数5 M) e# G; y4 R# f5 K
将socket和地址(包括ip、port)绑定。
1 X3 A* F; V$ U 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
" r' x) Q% u0 }8 J struct sockaddr_in myaddr; //地址结构体
8 m( t/ i7 A' J# U bind函数
, Q+ `2 ]- \2 z- f( g4 {& ^ bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))) e, M4 J8 M9 F; ]% x. ?5 C5 R
0 O4 o3 [+ K( C, X7 B  `& g$ `) L, o' H
! t. {$ ^% q6 X6 J" A) p5 ~
3.listen监听,将接收到的客户端连接放入队列
. W: a% P) {( S" C1 \5 W' m( o+ |' P listen(sockfd,8) //第二个参数是队列长度5 V& G8 w7 Q' b, V1 Y4 m

& a3 m9 a2 w) A1 S# K+ X7 f1 J
0 y# \# N  k2 @& K
4.调用accept函数,从队列获取请求,返回socket描 述符7 F+ l5 H# h$ W4 o
  如果无请求,将会阻塞,直到获得连接
6 v% s& `; r$ @" ^+ J  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 Q8 Y; `3 P- p" U0 V6 I

6 [" c" ^7 m% S5 P' z
& q3 b% H4 N$ D- M% C0 s1 ?- O5 X
5.调用read/write进行双向通信# e- A- r+ d: s% n4 \  [4 x, S

9 u' [$ @8 U# z! C
1 R5 }6 l+ w4 e% k
6.关闭accept返回的socket
$ r( s8 ?- G. @( y% f1 q5 O  close(scokfd);" w' |( s$ J2 a& W# D

8 C. y& `0 f/ Z

' G0 u% S, o5 J! a9 F
1 q' [3 q9 ]% w$ i8 Q) L

% G% W" D; n1 D' h: H下面放出完整代码
/ R6 N6 e6 a3 U  ~" j, ~* z2 Q6 k$ c& H, e
  1. /*服务器*/
    ; b' N  g) \9 T* m- y; b
  2. #include <stdio.h>
    . R) g) |6 s4 ^- A
  3. #include <string.h>
    # G; X4 W& u( W& R$ b6 H# j
  4. #include <stdlib.h>
    4 o2 ~: z4 u; g
  5. #include <strings.h>
    ; _& B& D9 r- |6 T
  6. #include <sys/types.h>
    2 L9 I+ ^  }* `
  7. #include <sys/socket.h>! P2 x* l; Y; a- N
  8. #include <arpa/inet.h>
    2 I) T/ k' s+ ]' u
  9. #include <netinet/in.h>
    2 I; Q$ Q5 D, V* R3 p
  10. int main()0 x$ Q$ o6 v7 _7 T1 I
  11. {
    3 ~2 z2 o  J* C! W% f0 {' D
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ! G4 h! f1 y$ y2 j0 X
  13.         if (sockfd < 0)
    / w9 P- ~) j' R
  14.         {
    " l; O3 {; {% E# r4 e4 r& `. G: e* y
  15.                 perror("socket");
    ) U  m' ~/ Y/ }- A
  16.                 return -1;0 K! x/ {0 r& ?0 m' f, B
  17.         } //创建失败的错误处理
    + _8 G( t0 Q, R
  18.          printf("socket..............
    ( K6 H% K# b* x0 M9 \# |3 S* ^* S5 k
  19. "); //成功则打印“socket。。。。”- }1 q; U* d1 c# Q
  20.          8 [. k- }7 j, Z3 d" q& U. H
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体0 G$ J0 O6 \/ \  @8 P: X7 A
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见), j3 n0 ^0 O( @
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型% f* e+ \+ a, n' |( j. z8 D) ~) y
  24.          myaddr.sin_port                 = htons(8888); //选择端口号' m( ^' g! n4 }* |# ~  }! x! f
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址0 i% v' h$ |/ d  c. n9 c# G
  26. % V, P$ v9 ]' Q# u4 _; r. ?
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字, Y0 C' O. S" z. i
  28.          {; p; o* G6 c0 M2 h. t5 j/ V
  29.                  perror("bind");
    & E, x( K+ z: ~7 u: I, @3 j
  30.                  return -1;0 \+ m+ `' ^5 H6 A* M! M$ h' z5 \
  31.          }
    1 j3 n; H7 a+ y, R$ L3 m
  32.          printf("bind..........  R( q$ N( U" K5 e* M
  33. ");* m. h1 }4 m9 k4 E

  34. & L# W. v! x) f
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    4 D/ l; O' ^* v$ \' Y, [2 D& f$ V6 p
  36.          {
    % q$ y- |# J& V. a
  37.                  perror("listen");/ m. s8 j" p0 V" D$ q7 Z
  38.                  return -1;- y9 C: ?0 Y5 g7 X3 `, Y; X! Z
  39.          }
      D: r8 a! A# d
  40.          printf("listen............
    4 ~& s# ]: `' t: ^, j# K3 s9 C
  41. ");7 u& ]5 O( m$ n( E# O% B- a
  42.          
    $ B6 R5 V  N/ ]$ Y8 k7 Y# Y$ f7 _' ?
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求/ R' o+ G- K0 V* ], z! H3 g
  44.         if (connfd < 0)
    ' q  Y3 Z! Z: D
  45.         {$ X6 r" ~+ F; O  \; ]1 f
  46.                 perror("accept");
    ) x3 l; Z' q& Q5 d" Q  h* w" S) K, K
  47.                 return -1;' G3 E% V4 T/ p* {
  48.         }3 o" C  X6 {* p
  49.         printf("accept............../ I) C. }; G' `$ g9 b/ }
  50. ");# O  J! G  E, P
  51.         char buf[100];//定义一个数组用来存储接收到的数据# T# {4 t2 {( s7 _2 L
  52.         int ret;
    4 p2 z" P: \; b* J; f  s
  53.         while (1)* Q/ k5 `' \0 [! R/ u- z
  54.         {
    4 I5 `# H0 j3 r2 S
  55.                 memset(buf, 0, sizeof(buf));
    ( f& S6 C5 K  O) a$ d  _
  56.                 ret = read(connfd, buf, sizeof(buf));2 K* I/ r$ L% E  M, `
  57.                 if (0 > ret)
    8 F0 }7 X6 ~. I1 O( v" F
  58.                 {' c+ G3 W2 P% o
  59.                         perror("read");9 K) y- F1 ~; [1 K9 w
  60.                         break;% k$ I: L& q" C
  61.                 }//执行while循环读取数据,当& o7 z/ T+ W  Q0 V) d5 ~/ r2 c
  62.                 else if (0 == ret)# e! L( w" w& x- r# ]
  63.                 {1 t, O7 e; l' F' }  q8 R; F# c
  64.                         printf("write close!
    : I! Z0 j6 ?. D0 ~$ u1 H) K: L6 j+ k
  65. ");) j" ]2 N( d% Y' K
  66.                         break;
    % L" G, N1 ^- v& n# g9 R1 ~. e
  67.                 }! x6 x* J% s! h( \. N# A6 @
  68.                 printf("recv: ");
    8 v( b% U, k3 T$ y
  69.                 fputs(buf, stdout);//打印接收到的数据
    , X. s  |- J* j' m
  70.         }" ?/ Q3 K6 i3 `3 c8 t) t
  71.         close(sockfd);//关闭套接字
    ( O  d/ y6 a& y. K! U) f+ @
  72.         close(connfd);//断开连接
    ' O# M8 G- q5 r/ S9 ~4 x
  73.         return 0;- @+ _* P$ S0 c4 U" k5 L
  74. }
复制代码

. n7 N2 ]+ u0 x; Y$ N
1 H  _* P) F/ u& V
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)1 p0 H6 c% Q. Q2 s6 q" |6 t* |
  2. #include <stdio.h>( @0 |% x* j0 F6 }
  3. #include <string.h>5 q  |; v5 }( e" _
  4. #include <stdlib.h>
    2 q2 @  v' D: {; `9 \# B0 e2 w
  5. #include <strings.h>% b5 B" S% Y# U0 [/ f2 C
  6. #include <sys/types.h>* h0 D8 Z+ b& L( W
  7. #include <sys/socket.h>
    6 P: f- L: u7 z% C5 H4 O' J
  8. #include <netinet/in.h>
    " G1 o+ a; M# k$ x
  9. #include <arpa/inet.h>
    . |. t' a7 U) h
  10. int main()! R* U  K- f8 F: u: b! r
  11. {
    + a3 J  _6 h* v* V' ]* q
  12. int sockfd;+ a& Q8 \8 Q$ s
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    " R8 m0 F1 O4 p  H; S8 y
  14.         {
    / K) ^4 i5 M* {- l( m- ]
  15.                 perror("socket");) s) T. q1 X) n" Q7 q5 S* ]8 ?$ u
  16.                 return -1;) C6 B% F% s- ]' z2 S, H
  17.         }0 k& r0 H. I" c  k7 S$ o* Z
  18.         printf("socket..........." N6 n9 i$ X3 f  d6 \, w
  19. ");
    1 d5 ^7 g( @) A4 I
  20.         4 a1 |1 B% \( R# ~  c
  21.         struct sockaddr_in srv_addr;
    - K+ a& E. m$ |# ^
  22.         memset(&srv_addr, 0, sizeof(srv_addr));5 d, b; ]0 x; }
  23.         srv_addr.sin_family                 = AF_INET;
    & `% l& W3 y6 b/ p6 H% l
  24.         srv_addr.sin_port                         = htons(8888);
      I$ S, M% K" i1 c
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");( W8 v* B) M6 Q5 w
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))): q) W0 W$ f% c6 Z6 ]2 l3 b
  27.         {
    ( o  {" ]# b# {3 _( R
  28.                 perror("connect");
    , G( i) S1 T/ `+ ?
  29.                 return -1; //exit //pthread_exit
    7 Q8 \) t; y0 T! N% T3 m
  30.         }2 b" [& ~- k$ R5 H- O% d
  31.         printf("connect..............
    2 S2 f( h' I6 [( l1 ^. y+ @- x# j
  32. ");
    ' V+ ~4 V+ R( u+ R6 s  _& F+ k- \
  33.         char buf[100];
    ! r+ N0 Z- a/ [* R1 {+ H
  34.         int ret;
    0 A3 ?' \4 F; t& S/ a% e6 M* a9 ?% P
  35.         while (1)% o* \$ r9 O6 M  q1 N7 c
  36.         {7 f$ [$ z# L5 I- \
  37.                 printf("send: ");
    * K) O+ j2 f/ M' N, ]
  38.                 fgets(buf, sizeof(buf), stdin);1 D( n3 |2 \6 L, L* S; A' U
  39.                 ret = write(sockfd, buf, sizeof(buf));
    & N- Z6 Y! @. J: U
  40.                 if (ret < 0)1 Z- n/ b$ x+ M8 S
  41.                 {
    + P" K) T- a) E* l! h# T
  42.                         perror("write");
    5 t: r2 ?9 r+ s$ v/ ]' n, D* ]: X7 L9 f
  43.                         break;# Y2 e% d2 W+ [' a( t9 L, h! \* B
  44.                 }" h0 f; o  {2 E; @& L" ]
  45.                 if (strncmp(buf, "quit", 4) == 0)- q7 Z5 C1 J/ v; K8 Y7 C
  46.                         break;# j& O! ^  N# H! H5 j$ a  m
  47.         }: A) k4 D) X+ H. U
  48.         close(sockfd);& _$ O6 T8 v0 K3 m; \# P" i2 `
  49.         return 0;
    0 \( I3 F, E0 y
  50. }
复制代码

2 P6 l4 f' d! a. i3 B" e
0 M/ W2 k, E" v2 Q
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-5-25 10:07 , Processed in 0.063219 second(s), 24 queries .

Copyright © 2001-2026 Powered by cncml! X3.2. Theme By cncml!