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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。: o3 ^$ Q& R" |: x5 U
) B$ q* x3 u6 `8 K
# [# k: j; y7 [/ D
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
7 i, t' O4 C' W6 m- F5 J: A1 v8 c. G1 [: _& k3 u! n

2 m, }) j* w% k7 hTCP协议$ X4 q  c- R& K4 S# U( s$ f; @
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
+ \* x. {& N9 u: x( K) `1 y% d1 u7 K) S: o6 q6 ?

- J; p+ H) z1 ^" F7 s关键词:三次握手,可靠,基于字节流。
1 Q! q1 X& U$ s- ?
, S4 v0 k; F& x% ^, y9 s9 z! ^
( S  p# j) w* X4 E
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
) i- o9 e8 J# F  [9 Z 2 r" @" v$ G/ T* f
TCP服务器端和客户端的运行流程
' t( e( g) H. e0 ?) }# w  X" G1 s& s( t# Y; l# h

; ^5 J7 q* Z* ~  f' E$ G6 J2 i如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
5 [* w: }* ^0 ^! C. L
+ Z; X. z  R8 g+ i/ c4 X' N( ^
" `! H9 w9 v1 ~6 ~$ e6 i3 b4 r
1.创建socket1 ?8 Y  u& U6 t3 p! ~$ M
socket是一个结构体,被创建在内核中. D) d: M8 k6 j6 M% _( l
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议1 h& K- P# F' j$ v
; p1 M7 ^! E9 w  h( B

# H: A! V+ y0 b2.调用bind函数
% B+ V3 M) Q4 F# r& m 将socket和地址(包括ip、port)绑定。- h3 R& q' U/ w/ l# b% M
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
9 G! E7 h- V+ Y, c( j& j9 `5 O struct sockaddr_in myaddr; //地址结构体- i" f2 }, ?8 {
bind函数
* ^0 S* P( |; O bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)): b! J1 z9 H7 F+ r: C1 m

7 T% x6 C0 h) m

. V6 X9 F7 E. l" k: ^5 `3.listen监听,将接收到的客户端连接放入队列
! [3 c7 P1 _9 E+ W% ]7 T6 S listen(sockfd,8) //第二个参数是队列长度
8 M8 z$ b" H, H! _; D' q; f: b5 z1 \5 R7 }

% M; z) E! g3 @. |" a4.调用accept函数,从队列获取请求,返回socket描 述符
7 ~' u! Z, l) a/ l  如果无请求,将会阻塞,直到获得连接
: G) ~- x6 \6 _- }" W5 k  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数8 f& r5 D2 z$ ~9 H: e
# R- ?9 v% `' r' |' b% |) h3 g
) U+ H$ o$ `5 `5 {8 E- `
5.调用read/write进行双向通信# e" O" L1 Z' ^. {7 ^

( z* ~( x+ n' H! I% N

# Q% ^7 o* ^% v0 c2 [) H6.关闭accept返回的socket
/ S3 o* Z9 B7 L- M) c  close(scokfd);
2 @' R3 l6 O0 v# t* W; G/ I8 r* j2 ]8 t/ Y4 r$ k- E% R9 p6 E& b3 D

0 X  u2 X. r: ~& O# F+ O" q" k; d+ g
. a2 L+ N4 y# t6 j

1 C* L* j1 p. b" {下面放出完整代码
  p& l( d& Z/ l2 `, h) e4 I2 s( ^
' T0 I0 j. _( G, I! I& M
  1. /*服务器*/5 Y1 X' u! Y  r, B$ ]* b. m
  2. #include <stdio.h>
    1 Y, D( Y6 D& ?7 n2 A+ g6 c
  3. #include <string.h>
    + u, [& L$ @% g" t  v
  4. #include <stdlib.h>
    $ z+ ^9 n6 t3 r$ d% n4 u; n, S
  5. #include <strings.h>
    8 o4 k9 |% k0 q0 z9 a4 J
  6. #include <sys/types.h>& l3 ]' q$ b4 O4 ^0 X, q
  7. #include <sys/socket.h>4 }0 C9 E# H) g1 _6 o4 `
  8. #include <arpa/inet.h>
    , M7 b4 q& a$ x* p+ ^  K! Z9 O& s$ I
  9. #include <netinet/in.h>' V3 r; Y3 v8 J, g- p* e3 W' }1 ?; a
  10. int main()3 i  q& |/ J4 d1 n
  11. {3 A+ ?) ], Z% z% C9 I( ?
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- {5 J+ {8 s% z2 g1 {
  13.         if (sockfd < 0)
      I7 p' T, _. U/ u
  14.         {& p  t& _  W5 f: j
  15.                 perror("socket");
    " [2 H7 j( r% V% }. p
  16.                 return -1;# @5 a; R% _! i0 D- q- z1 v/ |
  17.         } //创建失败的错误处理
    ; g0 q* ~- B- u( M  c5 v2 s2 Y4 }
  18.          printf("socket.............." ~0 m: O4 M, M. u
  19. "); //成功则打印“socket。。。。”$ K/ d3 }. T7 J6 g. j
  20.          ; Z2 V. T  A+ Z  ~; _( Z
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    3 \' G# P) W) F9 G, `
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)3 e% R6 \" x- v! G" _4 P0 {* B
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型( [6 d3 o" L& U+ e
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    5 Z* V& E; C9 u- p# n3 Q, m, {
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址& ]9 Z; O: p9 d% q3 o
  26. + {8 T4 R+ e  v1 R: {
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    3 v- W( P$ h6 H/ c! A; _
  28.          {
    7 k3 Y, `! s& @" `  ^
  29.                  perror("bind");
    2 Z) @# X5 o0 N: Z" N
  30.                  return -1;
    - p2 W, G  }9 r# [* S3 N5 L" T4 R5 m
  31.          }5 Q$ Z8 |7 P9 O: e
  32.          printf("bind..........6 Q+ L; w7 F5 G6 H
  33. ");
    . D# p, t( _5 Z( N  |9 C. z5 N9 k

  34. 9 Q$ z: [% h. J9 A1 H
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听8 H- G$ s* m* a% ~$ t; p* `% x8 T
  36.          {
    . M* V/ f5 ]" u0 W4 W" W" A* x
  37.                  perror("listen");: s/ {/ c0 y5 x( s5 m
  38.                  return -1;
    8 B5 V9 {- ^: k# z
  39.          }
    ' p/ i" D* A' w1 [' ?/ I+ f
  40.          printf("listen............
    ; q; M' r6 q5 O( G2 L
  41. ");
    . S" o5 D1 p& p5 X
  42.          
    2 @" A& h, D' {/ E; P0 \3 A6 K
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求  L9 t! x& O9 Z) a: ^: h9 a" d
  44.         if (connfd < 0)* H& i0 c: |! k; @, A) {3 `7 \
  45.         {0 n# _4 G, r# }( r* W
  46.                 perror("accept");
    3 P. j: C( t0 {$ M$ G; z
  47.                 return -1;
    $ z1 k' w2 j% V" G  ~; C: u7 a- n
  48.         }) _" g2 I1 s# n; C
  49.         printf("accept..............
    $ J# W' K! U/ q9 Z
  50. ");% a& _$ r" Q2 D! L+ p+ |3 W/ H; V
  51.         char buf[100];//定义一个数组用来存储接收到的数据- w( Y2 S7 @4 s2 X2 V$ U
  52.         int ret;, _& e' W: K8 W% z6 F; W. V0 T
  53.         while (1)3 T0 Q$ ^- p  L" m: j( p2 U8 a
  54.         {
    5 q; z7 S# h3 f
  55.                 memset(buf, 0, sizeof(buf));
    6 S5 c9 P% F& l/ f! z- y/ f
  56.                 ret = read(connfd, buf, sizeof(buf));
    , i7 J# }3 E/ Z/ \7 F2 H% X
  57.                 if (0 > ret)0 q" z1 z0 D$ u2 X( i' |  M
  58.                 {
    . }, e/ Z% ~& t/ \
  59.                         perror("read");3 a0 i9 ?6 A) |* D- w. s
  60.                         break;; H  W1 v& Z/ e) ]3 {. A% J
  61.                 }//执行while循环读取数据,当+ T& Q) u0 C3 ~, J, N! s* R
  62.                 else if (0 == ret)
    7 R% o$ E+ y$ `* L( q7 C
  63.                 {- Z5 Z3 R! K; d% U8 k: ?' N* j
  64.                         printf("write close!$ t. ]3 b* F: V, x  B$ U, x5 Z& q( P
  65. ");( b" b- v3 O( w! B
  66.                         break;
    2 d0 H6 [/ ~7 |$ \, P
  67.                 }
    8 t0 H7 `9 B3 o
  68.                 printf("recv: ");
      j+ i1 V# j  E. R. q6 ]: w1 H
  69.                 fputs(buf, stdout);//打印接收到的数据& {: {: F4 S' q! C; P9 U! p0 J
  70.         }4 ?5 _: d) H4 z) E1 P( [
  71.         close(sockfd);//关闭套接字
    ( Q% ^; J6 K+ ?4 O1 F
  72.         close(connfd);//断开连接
    $ L* Q, s* A) @/ c3 c# F+ W
  73.         return 0;
    . J  @! ^, |4 P5 q: I4 [  x6 g
  74. }
复制代码

1 U' p  P+ i- T' v  e9 k- h, d% ?  w/ K8 ^! m% a1 L9 B
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)) q5 ~( o1 }5 M7 R' i8 h: i5 y9 z
  2. #include <stdio.h>
    0 O) ?" W* a! z. ~
  3. #include <string.h>" H. u7 @. P7 G7 B; o% J0 G
  4. #include <stdlib.h>
    # D. @+ M  _! ]' `9 ]
  5. #include <strings.h>2 t( C/ V% f6 ~; j2 `2 r+ w
  6. #include <sys/types.h>
    8 a' O, a9 s+ g# r) w8 c9 i/ f
  7. #include <sys/socket.h>+ H2 [& ^) j$ Q+ Q; G0 p( A
  8. #include <netinet/in.h>
    5 w  p" d' ~( Q+ i: `0 z
  9. #include <arpa/inet.h>
    # H& {, w$ |" e- M" ^2 s
  10. int main()
    2 z/ q# D6 J  O4 n
  11. {
    8 ?( W( j6 V! x" O% G
  12. int sockfd;
    ( Q& {8 x; A0 U$ _5 x$ \# j
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    1 e$ M0 R7 ]  }# ~9 @9 ]& T
  14.         {) M, c3 I$ A" y$ G+ v
  15.                 perror("socket");
    ; b+ a  i: D8 f$ b, u" r' j
  16.                 return -1;$ i1 ~, f$ j1 g6 \/ e9 _
  17.         }3 W% z# q) n, w* X
  18.         printf("socket...........: n4 o7 B" Z% R7 Z$ }
  19. ");
    4 B) H. H' H% [% M9 F
  20.         7 E  i6 [5 w6 J  k1 [
  21.         struct sockaddr_in srv_addr;
    ( M/ L' {, C0 X5 `7 n
  22.         memset(&srv_addr, 0, sizeof(srv_addr));8 |5 [  [3 O7 s5 O3 ?
  23.         srv_addr.sin_family                 = AF_INET;
    - K1 G/ X1 K' o: g( A, K& ~
  24.         srv_addr.sin_port                         = htons(8888);
    9 `2 r" o5 [0 [, P* s: l& _
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    & s) i4 B' ]$ [8 V  W& S
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    1 J' N5 Q5 f0 ?* E
  27.         {
    4 [) N9 p4 A) \: k# G
  28.                 perror("connect");
    2 }$ o  ~4 y2 u. a. @3 \
  29.                 return -1; //exit //pthread_exit7 i, z  E9 l- q; v
  30.         }, y6 o. `9 Z& ?
  31.         printf("connect..............
    * k7 Q! w& |6 ~# {% [; w) _0 m
  32. ");
    * H" S3 m0 B$ K$ X
  33.         char buf[100];
    * I* I. V. _* J4 M6 ]
  34.         int ret;& n1 \, l9 I% L; B! u+ e, N. K
  35.         while (1)
    * l/ {, l$ [& _3 @. y8 {3 h
  36.         {7 u% |  P% A2 ?0 L
  37.                 printf("send: ");3 M3 M* d. A( ~
  38.                 fgets(buf, sizeof(buf), stdin);: ?8 D5 f) D$ h& D: [) d' N( u; z
  39.                 ret = write(sockfd, buf, sizeof(buf));
    4 c) f# R$ ^% H6 m
  40.                 if (ret < 0)
    8 R) b+ D: T" G! g6 u' [, O
  41.                 {
    4 Y. {2 z3 b2 g' [+ b! l
  42.                         perror("write");
    6 h+ I3 `3 E6 S, s, B7 X
  43.                         break;
    & ]7 R3 ~) I! c9 F% t0 A/ ^
  44.                 }
    ) z% \  d5 Y: \
  45.                 if (strncmp(buf, "quit", 4) == 0)' m3 A3 |- ^. e% `) z- Z
  46.                         break;
    * O4 ?, _+ @0 H$ t8 v5 w6 z
  47.         }
    1 ~" b' H: R4 ?& t0 H9 y
  48.         close(sockfd);
    ) N* V2 Y8 o* R7 V/ u$ T7 P$ V
  49.         return 0;
    $ T& F- g1 |" j5 y9 T0 Y
  50. }
复制代码
4 z" z' B* Y9 L9 C6 K; ]- S
1 E* G4 C5 Y  A: j4 l5 ]) N& W
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-17 16:06 , Processed in 0.082393 second(s), 22 queries .

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