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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
+ H# C( Q4 F5 ]( [7 ^% f7 @  y! I: U" E' h
3 o+ @# |4 W+ t! e$ {8 S
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。( G* X. x( j9 d% l: v. `
, C' ]+ F2 ]# H$ ]' z/ L
. A% z- j+ }2 d0 H0 Z8 A/ ?7 N
TCP协议
) j! t! V1 K3 r  p% H  M1 O4 PTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。3 s0 \) t% k5 ^1 ]1 x: e

+ N) e% h4 R5 a% x+ W0 `2 X

( X+ y6 x3 k, L关键词:三次握手,可靠,基于字节流。
. q! h4 a) a0 X4 j
3 H2 M5 ~6 D3 x/ e: `
9 `, ?. ~$ c2 [$ [% s9 ]' @) o7 i
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
3 v+ y  R8 H/ i8 x1 q' t 4 X& r/ d5 c+ O
TCP服务器端和客户端的运行流程5 Q3 y  t& a8 R- _

: r& @/ t8 ~* M& {8 k

# J* ^2 j+ f, A7 x0 d( Y如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
3 X: v9 \$ D# Z: o6 C
( A% m( X! Q+ V5 F

& E. k7 I/ p& U2 l6 A* A" n1.创建socket
- P9 m; o( p7 \% u& P socket是一个结构体,被创建在内核中
+ n6 T# B$ b9 ^2 H sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议4 o5 v" I* V  O* A, _1 m1 M
; A9 z: D  O$ B! C9 h' P* b) W% c' \
& L: A' [2 V- _" D( |
2.调用bind函数
" V$ j% O8 J" N+ A& `* i 将socket和地址(包括ip、port)绑定。8 b  y0 s4 j  o5 S0 t
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序, A/ a+ m# j: f6 j5 b) Z
struct sockaddr_in myaddr; //地址结构体
3 A6 {+ G* ^7 _ bind函数: D# m7 @# N4 v6 J
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* L5 y) c% R2 {, T# `, q

2 [5 `8 }" W5 j' c

) a& g7 u5 O0 k* ]) u  G) \3.listen监听,将接收到的客户端连接放入队列
$ S* A; J$ o5 E4 x* k listen(sockfd,8) //第二个参数是队列长度
8 L1 z: E; e) E9 a
" z1 U& Q8 `/ k  {% z3 i9 J4 r

, x3 p% _. j0 R! f4 k6 g4.调用accept函数,从队列获取请求,返回socket描 述符% ]) A7 \, F/ S+ r& a+ o# h3 l: B
  如果无请求,将会阻塞,直到获得连接; [* H0 W& a* K& f1 o% X. _
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数/ y; g, X% C0 K  U# B
9 y8 c+ N, q) w0 e3 u

4 h7 K& [* R  u& H5.调用read/write进行双向通信1 q$ m/ D* W+ F1 K* J' j$ |

! `4 l. W4 C0 |

- E0 u- p; @% m9 ]5 r6.关闭accept返回的socket
5 T# d( q) U7 F  close(scokfd);
- \' t- o1 P8 w( a7 r% V$ A! I7 _0 p
) k3 ]4 l8 {' L: d# ]

5 E6 o+ d, G3 B. ^2 g$ a

- p8 j' s2 ^+ S+ }6 v下面放出完整代码8 P& R5 F, G- d- T) J# |# |

. Y$ Q2 ~: H6 u% L& H
  1. /*服务器*/
    : `: T" U! ]/ _
  2. #include <stdio.h>
    7 n( X% E  @) l4 q) L
  3. #include <string.h>
    $ q, f4 t2 B7 o- t4 z- a
  4. #include <stdlib.h>
    6 j- Y+ i! i0 I6 g* c
  5. #include <strings.h>0 ^: }7 Y6 v- C, t4 V4 Z
  6. #include <sys/types.h>
    2 c! ~( l/ I$ J6 E& I3 X! P9 c
  7. #include <sys/socket.h>
    - R' a2 f/ E. k" Z- B4 \! M0 W3 }
  8. #include <arpa/inet.h>6 G8 N% w* s$ P+ X9 e
  9. #include <netinet/in.h>4 K5 A. I2 D0 U
  10. int main()
    ( P6 X+ W2 @, v# g+ S6 V
  11. {# @( R2 z) t; B6 _' G
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    * F: o3 @, ]- ~* m
  13.         if (sockfd < 0)
    # @' X( L9 \2 _* ]" o4 v5 c, ^( Z
  14.         {
    # ~/ |# t2 q# A7 X" E' ?7 R3 e
  15.                 perror("socket");& b5 Y6 ~4 m# s1 R! b+ h/ U
  16.                 return -1;! W% Q# g- y( ~1 t- V) b7 E0 ]' g
  17.         } //创建失败的错误处理
    3 |* \$ p* I! W5 q& `" B% t
  18.          printf("socket..............4 Z. [% k9 @) r" k# k
  19. "); //成功则打印“socket。。。。”( o% z/ m4 p, _8 o7 ~
  20.          
    * k1 _# \8 u! o# n! Z2 v  i
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体9 V3 b# d4 @5 ?) s& |; N7 L
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    - f3 |! m& J3 S% L  ]
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ' F" Y& w$ Y/ s7 \) O3 h$ s8 v
  24.          myaddr.sin_port                 = htons(8888); //选择端口号1 K% Z2 [6 E: ~. D9 n0 k8 J
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址8 i+ s- f% j& R$ r" T. `
  26. 3 b# t: j' _+ R# u* a' ^" @
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ( r( C% N" y( `! s& }0 X
  28.          {( r* E- U* d. g! ~" ], R* F
  29.                  perror("bind");( }3 {" u2 E9 ]" c8 A5 L3 ]
  30.                  return -1;
    8 g! J) ]! t" U; v6 T" p. _+ E
  31.          }
    . R# c3 }. N' ?, v
  32.          printf("bind..........3 N. R5 M' V( R% |# l( N/ I
  33. ");
    / c0 [) l# F% F
  34. ( E0 s9 u  ^( L9 a2 W# t" H
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ) u$ `: L5 w" u8 z
  36.          {$ d0 N/ j2 y( F/ b
  37.                  perror("listen");) e6 k% B# z5 p; [
  38.                  return -1;) i( I9 V/ {6 l
  39.          }" G) E4 ^: s. c6 p0 W, m- e
  40.          printf("listen............
    - S8 A* Z4 ^. @8 d
  41. ");
    4 s2 l% S3 R8 A! o/ r6 k) z: L0 v( p  B
  42.          
    / b- _( r  @: T3 J4 `( W
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    0 K% d- `' f: v2 g. r* t" [
  44.         if (connfd < 0)7 i2 _: @7 d" T) {+ T9 W+ H3 k
  45.         {' A' H7 l. Y# F  R/ \( P
  46.                 perror("accept");7 W9 p% B) u' ?8 o! \0 Z6 J
  47.                 return -1;
    # w7 f+ F5 R) o! F
  48.         }5 z& _4 a, s3 s1 N$ ~
  49.         printf("accept..............
    " U- x" V; N7 c$ G. ^
  50. ");- D9 u" n$ E3 X3 L
  51.         char buf[100];//定义一个数组用来存储接收到的数据* Y; Q. Q+ B, ^: C! c8 z
  52.         int ret;
    & \/ G$ [$ X* q3 e: H# o& r
  53.         while (1)% j% n/ |" w6 ^" T$ k! m- w) K
  54.         {6 W1 h$ E) b$ r( y
  55.                 memset(buf, 0, sizeof(buf));+ q$ K8 M/ b: Z. ]. ^/ C  R" C
  56.                 ret = read(connfd, buf, sizeof(buf));
    7 n% \! B# N) O/ R4 n" @0 X4 C
  57.                 if (0 > ret)
    & }# ?; H$ I+ D9 _# P
  58.                 {
    8 b/ p9 E* v2 p; y0 C6 d; W
  59.                         perror("read");
    + j5 v& O& O- {" A) c
  60.                         break;
    " B7 J/ Y) R' @
  61.                 }//执行while循环读取数据,当' {5 P1 h8 u% e7 i( P5 N' H/ f/ r
  62.                 else if (0 == ret)
    + }1 x# j: `" X0 R. I
  63.                 {# C: q3 [: _" f1 v
  64.                         printf("write close!
    5 U8 d. p2 u* B& y- v; {
  65. ");
    5 a2 W6 h0 K. S. N
  66.                         break;
    : N. b; [! c1 _; }2 o# z& A
  67.                 }& Z3 G+ |! o, ~+ p7 a6 c0 ~
  68.                 printf("recv: ");7 r( m, y0 S. t! a0 k: s$ o* `
  69.                 fputs(buf, stdout);//打印接收到的数据
    ! _0 S! Z. }$ I% ]- v. K# L
  70.         }( I" }; \( \6 t! ?$ G* |
  71.         close(sockfd);//关闭套接字; C5 j$ D# u9 |; D7 z+ o2 c
  72.         close(connfd);//断开连接
    ; c! N, a+ p% y2 A5 y
  73.         return 0;
    , d; u' |5 R6 @) ]: g2 u/ \3 d
  74. }
复制代码
' [7 `5 _3 n$ [5 H2 l% `3 Q
" q8 ~8 ~" c2 O& F
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    / p+ C4 ]) x" u" B5 e) T# Q
  2. #include <stdio.h>
    " C4 l2 @! S  `: q1 b, Q
  3. #include <string.h>
    : |2 A* W* @; B* p
  4. #include <stdlib.h>1 n5 `; G9 v9 y0 z
  5. #include <strings.h>% z, ?8 z- i; m, A& Y
  6. #include <sys/types.h>5 t+ V0 V9 C. z- L6 ?3 z
  7. #include <sys/socket.h>/ d( d- Y' O& s. X0 K
  8. #include <netinet/in.h>
    $ _3 d. Q: b! W
  9. #include <arpa/inet.h>
    2 D( m6 ^' v1 C, i4 i& h
  10. int main()
    5 x5 l  s, z. Q. A+ b( }
  11. {" _, ^3 O7 `; U1 ?6 Z: N
  12. int sockfd;
    : `# Q& a4 k9 n+ y: y: w; Y
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))  t) p* u, h9 i. k1 W1 Y4 Z# v
  14.         {. k: W2 j8 b0 L: s
  15.                 perror("socket");
      N: b+ F1 {8 T1 e% y% T! Y
  16.                 return -1;  a) ]. W/ `* \2 Y6 C$ M  N; N
  17.         }
    3 a) |6 ^3 c* l3 _, M0 @/ ~
  18.         printf("socket...........* `$ }( D2 |9 k' t8 c7 s
  19. ");
      D& ?/ u, f' z$ }/ K& U& v
  20.         
    + C1 o7 ~. j+ w8 W
  21.         struct sockaddr_in srv_addr;
    & A: d) W1 `+ P" M6 y0 }8 K
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    2 h/ G. ]+ Q! l9 g
  23.         srv_addr.sin_family                 = AF_INET;" e9 q  g' v5 @7 \9 t
  24.         srv_addr.sin_port                         = htons(8888);
    2 E. T, r1 K3 }+ s
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    3 U8 I3 o+ S8 M$ `
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))5 Z7 ~5 W6 ^! V( G4 d+ D$ E
  27.         {. J5 U6 X' Z2 H; b$ @% z
  28.                 perror("connect");* ?2 A) D+ {/ g
  29.                 return -1; //exit //pthread_exit& j, m* Z4 P, I% Z7 H) s. b
  30.         }. c. D- ^0 O; m; ?
  31.         printf("connect..............) d1 |' `5 J$ G6 \
  32. ");" Q  p7 q+ f9 a& C8 V5 h& t
  33.         char buf[100];- d' k! g6 r* b; \# F5 |
  34.         int ret;7 y7 a/ s$ n) V$ J/ P9 }7 }! y. l
  35.         while (1)8 Z( k6 t0 ^4 P
  36.         {, t) ]9 a. B. P; k, V" L) j  |
  37.                 printf("send: ");$ X1 P+ o5 W  `
  38.                 fgets(buf, sizeof(buf), stdin);/ U. w( S3 @! m. o- y$ H5 ]' b# W1 s
  39.                 ret = write(sockfd, buf, sizeof(buf));6 A' o5 C: {0 S; m" a: T9 V
  40.                 if (ret < 0)+ b( ^$ Y' P/ O
  41.                 {6 J0 ]# ^5 a3 l% s4 V/ [+ H
  42.                         perror("write");
    - f; m" U8 U" O5 O0 P9 H
  43.                         break;
    # {& P' Q- _9 D; y* q
  44.                 }
    # b9 [# j3 b, j$ N
  45.                 if (strncmp(buf, "quit", 4) == 0)
    * A1 @& L9 g% A! x2 r$ E
  46.                         break;
    ( O- D7 {" e# p1 ]- L  c
  47.         }
    / p. l# a; R$ O+ F( C/ d
  48.         close(sockfd);
    5 P) J* P# z" m" q6 T! C7 ?+ b
  49.         return 0;
    0 Q6 x7 s4 S/ |4 y, u, [% j0 z
  50. }
复制代码
, H% O, P" E" Z* u& R  ?6 v/ r

7 r  v- I' \, d# S, T+ \6 W
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-17 16:10 , Processed in 0.079842 second(s), 23 queries .

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