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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 1727|回复: 0

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

[复制链接]
发表于 2020-5-9 02:09:24 | 显示全部楼层 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
- J* ^6 k2 W: C6 O3 I
2 G7 W7 d6 b3 k9 P0 M7 D8 W

; f- v; a( }, D% S3 Msocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
* h, O' _6 C( m$ n0 P" g$ x6 T
/ H: n) ?# k& @

( S1 ^/ @- ]$ x+ T1 p, g# KTCP协议
. L6 A& h$ N4 f  m4 jTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。. S0 N2 H9 `9 O2 j

( y( r" u9 G8 v( E
& z6 j+ g0 f" p' [& P  I
关键词:三次握手,可靠,基于字节流。5 n- z( o9 k! N( K( j9 G

( m& V$ F4 z4 U, U  k

, d% R5 B8 \7 K# o$ G8 @6 R" x可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。" N3 q3 s% E% `
微信截图_20200509015654.png
$ H& W+ u) o# R( p4 \# H/ n$ \9 h3 v4 ZTCP服务器端和客户端的运行流程
4 w$ R8 \1 v1 C; j0 V  l1 `& Z7 |

# W  ?  y% ^% C! G  ~( I如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
! J* ?; l& J7 W) [( `6 C: M; |
1 h$ e" S( k, X: E
# [& T( c* w  x
1.创建socket
$ @) K  ?, Y+ D socket是一个结构体,被创建在内核中% X* i* D. P: N% w. w$ I4 G
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议  V3 Q3 p' n- V( Q/ g' X/ Q

- P# C# y, u" ~! `, J# a/ U
" W8 A: M2 G, Z6 `, U: S" _
2.调用bind函数
) h0 l7 ~) `& [; L" q) d 将socket和地址(包括ip、port)绑定。, @  _8 A4 G! Q# [* P4 o
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
  K9 r+ P1 Y! ?! L; f6 k& C struct sockaddr_in myaddr; //地址结构体5 j8 L4 O$ d/ P7 G
bind函数
( q2 s6 W" c4 f4 A. @6 U bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
9 q& ^4 f' b& y* O3 ?" _/ m* X% Z0 _/ @6 @4 N6 d

' }. m. [5 o/ v% C6 S3.listen监听,将接收到的客户端连接放入队列1 j/ M# d: b( `
listen(sockfd,8) //第二个参数是队列长度
0 N0 l( J- z. V- f+ Q' |1 P% k* A" Z- K' W0 _2 T3 G8 i
0 \( r% \1 W- @! b0 w
4.调用accept函数,从队列获取请求,返回socket描 述符6 E* N; e# }3 i1 L, w; B# Q& z
  如果无请求,将会阻塞,直到获得连接
' O3 X5 B& o4 G# @2 S' n) h  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数  z( f1 x! f+ i, N  v" }* ]0 l

; p0 L( o9 ]4 E) e7 e. w6 A

% @; _) Z* o/ Z* E5.调用read/write进行双向通信
% V4 {8 t5 S" o4 ]3 }6 K* P
$ N4 d& m3 {: b

- O$ x# q2 |# Q6.关闭accept返回的socket' Z* H- L4 Q" q0 `6 L$ P; M/ T
  close(scokfd);
) w% c4 `  \4 ^+ u. E" |
4 O8 k+ {1 y% m3 j$ ^# @! e
- L' z$ y& [9 x

: B$ y7 D$ }' F1 |8 R3 `% p
9 X( @: ?1 K) q6 Z4 w: Y% A7 `
下面放出完整代码
5 q2 B# ~% t9 v! b; c/ X! j
* i8 \# `% J1 l: D7 G
  1. /*服务器*/
    9 z1 g. Q, J" G
  2. #include <stdio.h># r' d% v6 |. {" j& f
  3. #include <string.h>
      {  s  R4 N3 I8 q8 Y9 e& @) f
  4. #include <stdlib.h>" c; n# ?2 g, `/ m7 p
  5. #include <strings.h>7 a! g' h+ G  p4 w
  6. #include <sys/types.h>* O& |/ |4 m6 Z6 K0 [' w
  7. #include <sys/socket.h>
    4 Q5 h; z8 a" ?+ `& @
  8. #include <arpa/inet.h>
    6 t' b, O+ d8 k
  9. #include <netinet/in.h>
    2 L3 E. W1 B: r2 o
  10. int main()2 l( `$ E% c* Z% X3 }
  11. {% F9 H% [9 U- f, z
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ; r. c' }9 v8 V
  13.         if (sockfd < 0)
    2 u3 _) X0 C( d$ S7 S
  14.         {
    ) ^; g, M0 U4 x6 J( ]7 ^
  15.                 perror("socket");/ s) q4 I- ~7 s* Y( }. p" J, z" G
  16.                 return -1;
    0 N# U0 O, m1 W7 f' R) E& l7 d* s- s
  17.         } //创建失败的错误处理
      O  z# A8 Y* [, P' I# m) F" g( \
  18.          printf("socket..............
    1 r# \6 K+ a- Z* |5 D# s  u
  19. "); //成功则打印“socket。。。。”! o& y# l. E- H7 x# g4 X
  20.          
    2 o: \0 [6 U2 m, g, C1 W( A0 m
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体8 `) j+ F0 k: k2 f/ V
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    3 D  B3 X# K: b9 |
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型9 T- K% z% J) e  Y9 x
  24.          myaddr.sin_port                 = htons(8888); //选择端口号7 g7 g% S  `) i8 {2 y
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    4 t0 }9 E1 q, M$ Z& Y2 L: e3 L

  26. 6 |- M0 N% `. ?: v
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 H" t. N, N" O
  28.          {
    ' N4 H# f, W* H  s. ]: F
  29.                  perror("bind");- r' M) c& b: j& m5 I, b1 Z" y
  30.                  return -1;  U: f9 V% C& \# ~" K# t  T* Z
  31.          }
    & x3 a% M8 E$ o) f1 g& W
  32.          printf("bind..........
    9 m0 B  e0 n% I/ a6 q
  33. ");
    8 P  v) v" U7 W% i, g5 Q$ b$ L

  34. 1 y" |2 n" v. k- U, ]
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听+ J5 a- ~. c& [9 q) m5 m/ ?  F
  36.          {
    1 ~+ I+ \& K3 d: z& k7 U
  37.                  perror("listen");
    - L% _% `: Z' X" q4 N) f. q5 R( z
  38.                  return -1;: S! Z9 ]1 i' y
  39.          }) c, K6 m. Z1 R7 ~! Y: r% d
  40.          printf("listen............! m* m( P8 C& p7 l1 U9 W
  41. ");1 }$ V' w- A. p8 r
  42.          
    5 C5 T/ _2 ?& K
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求* `9 r* H- z, P. N1 U& y
  44.         if (connfd < 0)  a$ Y5 R! j& t8 d( y. c* ]0 ?
  45.         {
    9 ~! s+ h5 n& u
  46.                 perror("accept");
    $ V" T3 O& e) ?6 ?
  47.                 return -1;
    3 ?: S! B- d0 u! |( U+ n, ~* j
  48.         }
    ( B( W' k: N* J( G# V  O: t7 J
  49.         printf("accept..............' u5 f( r; M1 n3 f
  50. ");9 J' P3 P, c. ^" F
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    & B, w0 N+ w! u2 h6 `/ {3 l; U
  52.         int ret;
    ! C" Z5 ]6 Y, v- Z5 _, p& Z) t5 O, d
  53.         while (1)5 t1 C7 w  [3 j/ V9 o) r' {
  54.         {
    % J7 N) x+ T% A0 d3 U, ]
  55.                 memset(buf, 0, sizeof(buf));4 m# [3 d+ z: @
  56.                 ret = read(connfd, buf, sizeof(buf));
    ) V( s$ [) H2 s3 {2 d
  57.                 if (0 > ret)9 c- f9 g* M- ]0 ^4 t2 G3 _/ c2 f( A
  58.                 {6 }" `. g6 h4 J6 y. R
  59.                         perror("read");
    * A* P7 B4 L5 }
  60.                         break;
    . D, ~8 d, }$ l
  61.                 }//执行while循环读取数据,当
    3 e/ @  j4 w. Z+ ]( X' g
  62.                 else if (0 == ret)
    4 w; e1 }' f0 M! D" S
  63.                 {. v  a! a" }; J$ `3 Q) X$ z
  64.                         printf("write close!
    ; X$ T+ J% h! e4 J5 C" e( o& {
  65. ");
    ( O3 k* }, x- H8 c, L9 \1 n1 V
  66.                         break;; A' w2 ~; Q' G& o2 s9 |
  67.                 }4 M+ q1 o) n4 Q/ j/ D$ M
  68.                 printf("recv: ");6 k3 u/ @0 J9 \% z  J( {$ v
  69.                 fputs(buf, stdout);//打印接收到的数据3 [" {& `' A( W; S: P, v
  70.         }
    ' x8 k4 b  |3 @& F+ O* ~( [' B
  71.         close(sockfd);//关闭套接字
    * ?9 D1 b: P( }0 @" f! D, w% t
  72.         close(connfd);//断开连接; [7 x4 e; G$ H. I- y3 d/ J
  73.         return 0;7 h* z6 c, `$ k9 B5 K/ @
  74. }
复制代码

7 y  l9 X* x% ]  D# e9 T( C7 \  |, h7 S! L
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    5 V; V: U' C  P6 J1 A3 E& [' i
  2. #include <stdio.h>1 H! m5 l" `1 Y! S, N8 c7 Y' A4 Y
  3. #include <string.h>) r8 @5 g' N/ W0 }
  4. #include <stdlib.h># K8 L& _1 k6 ^" r  A& G! U
  5. #include <strings.h>" T5 `! L: h5 d6 h& t
  6. #include <sys/types.h>2 [' k, F' b9 I/ O
  7. #include <sys/socket.h>
    ! e; z8 [& {1 P5 x3 h# D
  8. #include <netinet/in.h>
    4 l# d% q. I! b/ F
  9. #include <arpa/inet.h>
    / M1 ~$ F8 {: T, b9 F$ E( v
  10. int main()
    0 Y9 `2 P0 N  h
  11. {
    ' l7 o! d4 I0 E: t$ ]& t
  12. int sockfd;4 I6 c: Y4 }7 M0 l3 s
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))& k, U: k1 |- J
  14.         {8 v- E% k$ u+ J$ L3 O  P& M' }
  15.                 perror("socket");  r' n0 q/ g: L% k' f/ Q
  16.                 return -1;7 i: E" c3 L8 H0 w
  17.         }
    * Q+ O) t! x3 Z( K+ c" y+ ^' S! l
  18.         printf("socket...........
    3 i& X! G4 [6 a  m/ t$ U
  19. ");4 j) }; t& V  I' p7 K- h3 q
  20.         
    0 p  s  b# y) I( a/ @* u
  21.         struct sockaddr_in srv_addr;; ?# m, ]  W4 \5 M2 Q% ?0 U: q$ R
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    % Z$ E% R7 s: M+ A; ~1 e' ^% _
  23.         srv_addr.sin_family                 = AF_INET;
    0 u/ B$ h% _2 U$ f) J" D  g
  24.         srv_addr.sin_port                         = htons(8888);  {, o4 r. I* |& }
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    - I$ c, S3 Q& V0 H# v: q
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))). b; n$ L, w, i; `: h
  27.         {! R+ M6 d7 g# B% m
  28.                 perror("connect");
    : B, V; x) l( y, r0 h
  29.                 return -1; //exit //pthread_exit7 X; Q; h* L+ n$ X
  30.         }( V5 {5 ~2 {( F1 Q4 V
  31.         printf("connect..............
    ) D5 A' Y: T- u0 U
  32. ");
    2 X1 ~) ]# N! u2 F2 G
  33.         char buf[100];
    & x: @, t* j0 s) j: j* ~, O
  34.         int ret;
    ( h( ?! `$ m$ ~$ T
  35.         while (1)
    . X+ J& k0 ?2 i& q  L
  36.         {
      x; {5 G- i# h7 [* [
  37.                 printf("send: ");) N! S0 h1 u1 F' A) y  q9 W
  38.                 fgets(buf, sizeof(buf), stdin);5 D0 H+ S4 _9 D6 {
  39.                 ret = write(sockfd, buf, sizeof(buf));% i7 f# J6 H7 X3 m5 c. T
  40.                 if (ret < 0)
    : k) W) `8 w7 X' ]- g/ F# O: [
  41.                 {6 G  N+ Q4 X4 M$ D7 [" V2 Y/ O
  42.                         perror("write");
    # q. |: ?7 P5 C
  43.                         break;
    0 t* M: f& e- `! e) ?3 G
  44.                 }) @8 C) r6 ?1 t: ~" c
  45.                 if (strncmp(buf, "quit", 4) == 0)
    + A" V. F9 D; x! N" O
  46.                         break;3 d! S/ s3 ?' L- Y" O, r
  47.         }) E0 ~* d5 X( b/ \  P
  48.         close(sockfd);9 D! D2 T6 B6 v( l! v( O3 E: N
  49.         return 0;1 T. o+ x: U$ m3 |) u/ D
  50. }
复制代码
4 F( C# ?; w6 S( p6 {$ z$ V4 [% \9 h* q
2 x4 p2 ]( Z- t8 U1 m
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

        在线客服系统
    申请友链|小黑屋|手机版|Archiver| 备案信息代码:冀ICP备18019919号-1

GMT+8, 2020-8-10 13:41 , Processed in 0.144936 second(s), 25 queries .

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