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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
; _0 D0 h8 M' ^& L$ @- B/ W1 I: u8 v! Q
. B" N6 Z% D5 B: K
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
3 [/ i" T+ `* D" B, D" K: b# [; e1 @: F. U- |2 n# Q+ j" c! z* ~

  ?' D: m! d. I& h5 \: _" zTCP协议
2 @  v2 w* O' v" bTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
8 s0 v$ L8 l. p4 B
# `7 g. L! Y+ Z* H) x

6 u" d( i3 R$ h. K) X) P, v关键词:三次握手,可靠,基于字节流。
  m/ V4 E1 D+ w' e: }) i# E( z, H

, s6 X6 J6 F, i4 l8 z& b) z可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。8 j6 G$ l: A  T( z5 e

5 K+ b8 T) K* \- A9 G" lTCP服务器端和客户端的运行流程
( I# A& `& }9 d* z  o4 F$ g
! y7 c# _4 r( L# r1 t/ B2 y. P% ^

' U" `  ~3 @$ m& }& z5 m如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
& d2 R1 O9 W; ], @3 u
: }) V9 n- c' p/ e- I
9 M7 E' |3 y, r2 K2 F
1.创建socket9 o; Z% a9 w2 k- a
socket是一个结构体,被创建在内核中* R$ a. u# {8 p& r4 O& A6 v  Y
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
1 u' [2 e1 R: H  f
6 u7 s; F3 R  y# M: b+ `

) |: j. e! q+ g0 p) \+ h2.调用bind函数
/ h; ]; C5 u. m% @/ P8 B5 D 将socket和地址(包括ip、port)绑定。8 r3 h) ]0 {' y7 `) s6 q( f3 {/ E
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序) l1 K" C6 Q( x; Z% w% i5 E1 X2 W
struct sockaddr_in myaddr; //地址结构体, j2 t! g3 N" L; H
bind函数
3 |: J6 I: f4 o: Y8 h2 t bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)): z" G2 G7 P! T2 G$ f2 K

" J7 h0 T$ v/ C5 K. ?; {# L5 J
4 `# Y! f: N: v$ |9 }
3.listen监听,将接收到的客户端连接放入队列8 @1 b, E/ X# A1 r6 ~) ?; n
listen(sockfd,8) //第二个参数是队列长度
# g2 W* u; P" o- E6 x! c) e! b3 ]1 w0 v6 l( n' P3 U

+ F- d3 O( Z! e* {8 ~& I4.调用accept函数,从队列获取请求,返回socket描 述符1 K+ s- O2 V" I8 J0 P0 r
  如果无请求,将会阻塞,直到获得连接
1 Q+ S% H1 ?9 A) Y0 E/ @  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数* f0 I+ q$ j# Y2 s* ^' j

0 u* I, r- _1 K4 g0 l1 C

. x# i, x7 i9 k$ b/ d5.调用read/write进行双向通信
, J' e: X. V) E* F4 M6 z8 Z. P! t4 ]2 s; X
6 t1 W+ J% O- H3 c, G* F
6.关闭accept返回的socket; m4 \) ?- k# J* \. k- S6 A
  close(scokfd);  E- }* P4 a0 H* m& G/ ?" z

# F! J+ b% o; P

5 U) A  L& T* t3 ~0 L: U; M3 t) b: a' v5 A& ]  a) c# E
( \) {, R2 E  l. _9 K# c
下面放出完整代码8 U2 k3 {3 z! c4 @% J. Z* j% D8 C
: _% ?" e$ Q3 P7 a- t# x( ?% f0 d5 G" B
  1. /*服务器*/0 v9 |; }0 C  \5 g
  2. #include <stdio.h>
    & }5 N3 a) n; D
  3. #include <string.h>
    5 }, ]% Q! j. V: t$ N
  4. #include <stdlib.h>! c2 j# ~3 F+ \! Y8 u2 H
  5. #include <strings.h>
      C2 _0 P4 v( f) N
  6. #include <sys/types.h>4 y8 r$ ~/ e4 ]$ v+ Q0 H4 y6 y
  7. #include <sys/socket.h>1 _1 C' D2 k( N) \4 I' o- Y* y
  8. #include <arpa/inet.h>" c" f( d; F) f  h
  9. #include <netinet/in.h>
    ) l) m$ D; P/ W. u  o% Z2 \
  10. int main()6 [: B0 [# m! s0 m4 `0 n1 Z
  11. {. `' b' [; M/ M. u9 L
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字+ j- i+ C4 Z( H* M5 _& N; r/ ~4 D5 Y
  13.         if (sockfd < 0). c* s7 r% g9 F2 G" A( `# T
  14.         {3 U& d5 a+ r7 Q
  15.                 perror("socket");$ r/ U' Q0 K5 j- q( [% v
  16.                 return -1;* T. U1 }  T! Y' z0 n. D) O, k
  17.         } //创建失败的错误处理
    6 e  C2 P* i+ o1 A8 t- b" [: S
  18.          printf("socket..............
    8 A, E( }' Q  w4 u' ^; o
  19. "); //成功则打印“socket。。。。”
    - ?& [' E& M3 f1 H2 T
  20.          
    5 [! L; W1 V7 _5 b
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    4 s3 c" k3 G: Q: ~9 t
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    9 b; C! q) c7 U- t
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    $ d6 p, [( [. U% p1 O3 C- x2 f8 b
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
      i% n3 \+ X  L7 u& n! y! d8 Y
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址& R. R$ K8 R6 ?9 Z2 @
  26. , |# y  C# Q. E5 q0 P" `, n7 K/ u$ c/ [
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字6 V3 y0 w# ^( ]( v( K! g
  28.          {# X6 e; u9 A4 A* S
  29.                  perror("bind");6 @8 M4 d! M6 Q3 I3 N* f
  30.                  return -1;
    7 D3 g; X4 ^% P# u& E3 e& d/ S5 u% n
  31.          }( O* q% ~2 _0 e- j5 X
  32.          printf("bind..........) Z$ W) `4 B/ _# L5 K6 o
  33. ");
    $ R' f0 z+ Y' D0 B' v' }
  34. * S) _0 |% F8 i
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听" g3 X4 y& j9 A5 N4 \
  36.          {
      t/ N7 V6 }4 D$ O* a
  37.                  perror("listen");
    ( A! l  n+ _0 K2 I% X+ H0 N& H
  38.                  return -1;. \* ]; R! f# _- I2 G- w' ^
  39.          }2 q+ }- D* S8 ?2 A9 F. k
  40.          printf("listen............- ?+ V  a. S( G5 f5 f) f+ f+ I" ?
  41. ");
      f0 K* o0 U3 B0 T( K2 O
  42.          
    ! T  W4 U" |9 R9 M5 B: _
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    6 ]0 x. i, d9 F
  44.         if (connfd < 0)+ a8 B; a0 c% d$ l, U5 t
  45.         {' }: K! b; J3 |' K1 h) }
  46.                 perror("accept");
    9 L8 @: u' K0 T" M7 e$ H4 T/ H
  47.                 return -1;. h6 i4 Z3 j$ l
  48.         }
    9 q/ H  X' u+ C) P  q
  49.         printf("accept..............
    1 S1 o7 D- X2 K7 u+ |
  50. ");
    ) @" v! c9 ]4 |  d7 d
  51.         char buf[100];//定义一个数组用来存储接收到的数据# a" F1 T, l; C, H0 ~
  52.         int ret;
    9 h1 f4 O8 Q- o$ h5 O  E
  53.         while (1)% C9 J- m" m0 |7 c$ t# i5 }5 S
  54.         {! ^/ Y. n' D) P4 @4 r1 H" z
  55.                 memset(buf, 0, sizeof(buf));1 A( Q2 E" Y$ q$ ]+ ?
  56.                 ret = read(connfd, buf, sizeof(buf));
    : S% `6 ]* s! a0 q) @
  57.                 if (0 > ret), D  @" i8 F9 p0 k
  58.                 {
    ' j3 P; W: ^1 X: o9 d
  59.                         perror("read");7 u8 }  D. z, R  u
  60.                         break;
    9 Z) X# J- m8 R0 z3 t2 t
  61.                 }//执行while循环读取数据,当* h6 d* @9 k: L7 j
  62.                 else if (0 == ret)+ a  b& W! d" Y0 u- ~
  63.                 {
    ; o; T8 e3 h+ J2 J  o  ^" b5 M
  64.                         printf("write close!
    5 P% A! u% u. o; w
  65. ");( P7 P+ B& m$ O- |1 _$ |4 S
  66.                         break;% n: @3 }/ E' P7 n3 s
  67.                 }
    6 O: l% U- x3 `! m% U* y, @
  68.                 printf("recv: ");
    2 o1 w' Q3 o1 L
  69.                 fputs(buf, stdout);//打印接收到的数据1 P8 L% U0 V9 Z+ I
  70.         }
    / k( {/ Z* f/ Z3 U) e+ d' p7 X
  71.         close(sockfd);//关闭套接字9 i" P  i; X0 y; `# h# |
  72.         close(connfd);//断开连接. g+ R5 y; Y% a6 ^
  73.         return 0;+ g. {* J. t, u4 j' w% S% H' j
  74. }
复制代码
" l: |8 ~: }% C

) C" k; n6 F+ {; Z+ c
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)/ @3 [( V& J8 g* a& i
  2. #include <stdio.h>
    ' k; c  s- U* `) z, x
  3. #include <string.h>
    7 F: x) H, G, w# j* S! f  }+ _
  4. #include <stdlib.h>3 @% F2 l) J' a! ^' B
  5. #include <strings.h>
    + ^: L" p- Y; L0 X2 j
  6. #include <sys/types.h>) T2 O, W( r, g# k+ I# y
  7. #include <sys/socket.h>
    $ ~! W4 S2 S7 C, t# s
  8. #include <netinet/in.h>/ M9 {- D/ @7 A/ d; B5 F" ]
  9. #include <arpa/inet.h>$ U5 W) Y% O) a
  10. int main()
    1 v+ g* u( A/ H  Z" U
  11. {
      T. F0 j  S" g3 r  Q
  12. int sockfd;
    ) i& Q$ i3 X  ^+ _+ z7 y2 A
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))  j% I3 |+ z) N1 a
  14.         {
    2 B2 a; g; h- E0 Z1 V/ [4 _
  15.                 perror("socket");
    9 B8 p1 P( L: X- R3 X9 g
  16.                 return -1;
    / o5 u6 t# s* }6 k( @- I
  17.         }  A# m5 b" A! @0 ]! t$ k* a( {
  18.         printf("socket...........
    - q( U+ R) S/ a6 P
  19. ");: K  y2 x! w( N2 \$ D
  20.         
    ) X# N0 t. Y  H% i4 T2 h$ x& r
  21.         struct sockaddr_in srv_addr;
    8 ]9 u, ?  R& `+ [( I) v1 ?
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    3 q& r& j) _& w
  23.         srv_addr.sin_family                 = AF_INET;
    ' c7 Q: w7 @8 b% ?
  24.         srv_addr.sin_port                         = htons(8888);, k6 d# V, W" j/ T" U" i
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");  a7 M% @2 `+ o2 z
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    & \# q. s! t9 l% k, Z
  27.         {
    & }# ~/ ]( H! P3 H% R. A
  28.                 perror("connect");" W5 x0 {3 M: p$ d. j6 v( c
  29.                 return -1; //exit //pthread_exit
    ( g! S, A& q/ p$ k5 S9 d0 _7 b# P
  30.         }
    ' c1 N' ^3 r. J; C. z
  31.         printf("connect..............+ \0 v: G' D! h, b. \8 U4 u% n1 `
  32. ");. o6 G1 E1 H. V$ j7 {
  33.         char buf[100];7 V7 h, K( z3 j' e7 l
  34.         int ret;
    8 s( W+ |- `, f# J) v* n
  35.         while (1)% s7 ~9 d- U* ^  x: r* x1 Q* E* @
  36.         {
    8 q, a1 u7 z  a2 {/ J! |9 ?" O% Y
  37.                 printf("send: ");- M( ]9 D) x2 ]5 _5 k; k
  38.                 fgets(buf, sizeof(buf), stdin);
    * r: U; e. Z2 l  o7 {/ _. S' B
  39.                 ret = write(sockfd, buf, sizeof(buf));0 p# ]4 f, k8 J, t- ~# U
  40.                 if (ret < 0)# @3 M: r" c4 U) [# Z
  41.                 {
    - S2 Y% Q  K+ H) h
  42.                         perror("write");7 E3 z# C# D9 W# {
  43.                         break;
    0 W# m6 \% k$ [/ k" h* M: h
  44.                 }
    0 q' n% M% @; X/ ]
  45.                 if (strncmp(buf, "quit", 4) == 0)9 O. z5 O" ?0 i2 x$ p  D
  46.                         break;0 c5 l+ d7 ^7 c
  47.         }: A) T& B$ Z, G2 q/ O
  48.         close(sockfd);
    % @) d6 M! F. f9 q, I0 u; g6 `
  49.         return 0;
    + |+ p0 \8 ~; n. u
  50. }
复制代码
; ^7 b( D% M" Z1 i
" |- r3 g. F9 \
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-4-30 15:13 , Processed in 0.084570 second(s), 23 queries .

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