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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
. L+ v, ]2 X2 Y6 ~0 {1 ]& s
" h4 f' J5 U! j# r9 K% H  S9 ^
! `5 H, s0 i9 {1 s' E
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
- b$ H# X7 n  S! o( ]  i8 Z3 c# g' H  N; D5 G* R
3 r- m8 ^5 u2 w- o. I" o
TCP协议
. |; D8 \( M3 c: R. `, B" ATCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。; U4 _9 H& Y- h% @
0 K" t2 H) f+ t8 u

+ W! c: v. S3 o0 h关键词:三次握手,可靠,基于字节流。
- {# U( T" V  Z. F" ?1 E9 b4 v  [5 J: k; K. d/ H: g

4 ]4 n& S0 @+ T# U可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。$ x" m( ]6 ?! V8 ^  f

  Q7 n% X5 ^! J1 x* NTCP服务器端和客户端的运行流程
/ `/ ?( H: X0 Y6 d
+ @+ G! z0 [+ {9 o
) K$ F5 U. ]8 ]3 F" }5 F
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?% W( a0 }: I. |

) E( K! q" B3 w

6 i; x! _" W2 X3 `+ B! V6 P1.创建socket
. C( X% d2 D/ l4 X socket是一个结构体,被创建在内核中
( A7 X, X0 i! Q0 S sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
* a, l! G- b' B) @( G; h( W5 O  U0 ~" G8 F

# q9 A( _5 W& k+ \3 i7 x0 d2.调用bind函数  J; M/ c( L0 ?  j4 l/ L+ {
将socket和地址(包括ip、port)绑定。4 {/ K5 `) k1 @0 X  d7 l& x( @, g
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
* `  B, k/ L0 U! D' @& L struct sockaddr_in myaddr; //地址结构体
( R  V! I3 i( Y6 ?& @) P5 ~% z bind函数
" K' H$ ^3 G2 W2 E8 R, v4 i1 y bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
7 e# D3 Y/ I1 W% H+ H. x  s  o1 w% c5 K' I( Z& b9 V: U
# c) T7 h8 H* _" J
3.listen监听,将接收到的客户端连接放入队列
7 h* K% F6 U' q* B: \, _ listen(sockfd,8) //第二个参数是队列长度
; v4 B# e7 [2 c- a# y# j8 V8 _- F8 p& v
( ~, b- L; _; ^3 Y3 V
4.调用accept函数,从队列获取请求,返回socket描 述符
# u6 y3 _5 b  _  如果无请求,将会阻塞,直到获得连接7 G: w" o! v( y
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 g+ \4 q$ E1 M6 d" ?" b
8 `7 x5 z, ?% X7 }, o5 S
# l  c2 D4 f8 U3 P
5.调用read/write进行双向通信  d+ ?* q! b+ u4 C5 y1 |8 Y/ B, s; D
9 Y% }" }# O" S1 |0 V( f  d9 G7 A5 \$ b

& A0 J6 j) e% ?8 \/ T6.关闭accept返回的socket
) h$ J# H& G4 z4 }7 a) g8 [7 |( e$ o  close(scokfd);
0 e: j4 O& }1 T% M  F9 u# m
; g+ c3 D1 ^% f' O4 O
2 k  u7 I5 j3 ~/ h" _" x
7 J1 T2 S/ \- u9 E: h* X
4 X/ ~& P% Y6 f
下面放出完整代码( M# O; a8 U% D$ A# A$ U9 }  f/ q
( s( F- w+ r9 J/ u% ]
  1. /*服务器*/1 f. Z: ]) ?8 g8 s& F: f
  2. #include <stdio.h>
    8 f% ?2 ~( E6 W( M# _3 Q, [
  3. #include <string.h>
    / T2 y5 n( T# R) }2 }9 X5 d# h
  4. #include <stdlib.h>% w* T% D7 |7 o5 G
  5. #include <strings.h>
    # Z+ t- q: |2 p( z5 V6 D! j
  6. #include <sys/types.h>
    6 g" d1 ~/ R; m9 k* n0 Y9 V2 c- V
  7. #include <sys/socket.h>
    : ?4 d6 @" o4 x" J( ~- @' V
  8. #include <arpa/inet.h>' C- M8 R/ b! L$ i, c, U
  9. #include <netinet/in.h>+ q3 J# k: V! @5 y/ E+ |; w- D
  10. int main()9 c  }0 h! L! w. ^, T
  11. {7 }9 M4 [' O- q  _. H, k
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字$ o4 d& O' z- v
  13.         if (sockfd < 0)
    2 d+ q$ q. L# m* z
  14.         {
    2 p' E! `- I& b+ A! {3 o
  15.                 perror("socket");4 ~2 W/ r) }4 u9 E# g
  16.                 return -1;
    5 S+ M- b0 K# C1 j, n9 B
  17.         } //创建失败的错误处理
    5 c9 R. ]9 B; Z. J$ j' B  t0 k$ E
  18.          printf("socket..............* r4 Q1 z. Y  U; v3 o
  19. "); //成功则打印“socket。。。。”
    4 U- F- S7 t9 @0 j! U
  20.          " S8 ?! j( w  L. n3 A2 ?
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体. a) O5 m6 d  O/ F" K
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    - \. m) W5 v4 T9 g, M
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型8 v9 I3 u" n5 P' J6 X3 D
  24.          myaddr.sin_port                 = htons(8888); //选择端口号$ g( P9 _% K4 R4 G" Q+ i' ?
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址! K% @. O+ J$ z" Y

  26. 6 A& Y( ^0 g- z# W6 ^- q+ s% u) m
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字& |$ i5 m! r+ f# ^/ F$ P! F" u
  28.          {; w; g+ l1 K0 i, ?! q# B8 O. h
  29.                  perror("bind");
    $ k. h" c4 j7 S- e7 X! D
  30.                  return -1;5 n/ _, e: W/ b2 w% ?6 q4 K
  31.          }1 H5 U! W+ ^2 h6 V1 J( I. S
  32.          printf("bind..........
    . B5 h/ i( }+ B5 a9 Z1 l
  33. ");
    9 ?* t; V" u" S5 F- ]$ B

  34. ( ]. D' n5 G) `5 \. U  a
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听# {4 Y7 j' G& M: n( k7 v
  36.          {3 J1 S1 r4 r; w8 I6 J* v
  37.                  perror("listen");
    8 R$ z" n& y7 [
  38.                  return -1;
    0 S) u  u! \6 _5 M! w; c
  39.          }
    8 I0 ^" D! G$ z) z3 \
  40.          printf("listen............
    6 D( e% Q4 x( H6 X& L+ d) b
  41. ");. U- M5 L1 L; A' A# J& Y
  42.          5 ^3 q, G* y2 n: I+ o8 o
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求5 Z& q: L2 Z, o, P6 k
  44.         if (connfd < 0). x. R" ?( j/ Y4 |
  45.         {. C2 w/ B7 v7 U' i, x1 L
  46.                 perror("accept");
    - t' X6 i- s5 `# E
  47.                 return -1;
      }. v  }- \2 S
  48.         }( l6 Y# ?# N$ P' ]) m7 R0 o; e* Y
  49.         printf("accept..............4 j" _- V0 o8 ?4 y. F: R0 @4 s4 s, q
  50. ");1 \) A, ~( {" d+ N; J/ M
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    4 j3 ~7 ]. o1 x
  52.         int ret;$ W/ j1 \" l# k/ p. T- Z4 q6 v& f4 D
  53.         while (1)
    5 A# |! O1 @1 k- i8 L6 H3 e
  54.         {
    * Y3 H4 G4 A+ I3 u
  55.                 memset(buf, 0, sizeof(buf));( ]6 s# H* C- {- P- M! C" P( q
  56.                 ret = read(connfd, buf, sizeof(buf));
    & {5 k2 c& d6 m
  57.                 if (0 > ret)
    2 p6 G0 |  W+ [3 w/ X
  58.                 {$ ^" h' H" x! v* {# O
  59.                         perror("read");+ y9 g4 `, G$ w5 ^6 D
  60.                         break;
    1 q4 T; {: `# p/ P4 A- C" m
  61.                 }//执行while循环读取数据,当  e3 e& ?+ Q( N) F
  62.                 else if (0 == ret)6 R# Q% U% W: s
  63.                 {7 u! j. b4 ]5 g5 v
  64.                         printf("write close!
    9 r/ T4 G3 U9 K# u5 g8 I, c$ f/ H
  65. ");8 Y" |; |+ I! E8 j& Y3 l
  66.                         break;
    ; U: _. @$ g7 S( K+ f
  67.                 }2 Z- k. I9 f2 |/ H2 L. p. ?
  68.                 printf("recv: ");
    & W8 R7 ?* R2 G4 v# |& L2 n
  69.                 fputs(buf, stdout);//打印接收到的数据' V6 r; x) {# B1 o/ }" y5 h. M
  70.         }
    * u7 R  |. D3 ~
  71.         close(sockfd);//关闭套接字
    ) r! y5 a4 j9 y) K0 |& g, P1 j; D
  72.         close(connfd);//断开连接. T# {) t; Y4 Q) ^
  73.         return 0;
    ( @5 i, H+ x. h- Z
  74. }
复制代码

& h$ F9 e# X( D+ S- g5 k& f# ~- W1 q3 I4 }& D  N& w0 j
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)6 Q% Z* a! ]% }
  2. #include <stdio.h>
    + S, k& A- e6 c- C" y& C0 f
  3. #include <string.h>
    4 Z# f: w3 P, u2 H/ H; q
  4. #include <stdlib.h>/ A6 r, X2 S6 l
  5. #include <strings.h>
    5 I1 Q4 f3 \, Y0 B! p/ i  d
  6. #include <sys/types.h>9 ]$ y8 f" R4 p7 e5 f) [5 A
  7. #include <sys/socket.h>
    2 W3 {% v/ t$ f& _8 F( s1 D
  8. #include <netinet/in.h>7 j9 {5 M' D4 t! `/ }/ N# p
  9. #include <arpa/inet.h>
    , ~/ @" o8 I: Y; F+ K  \+ O; \
  10. int main(): x( V& u6 C  S; v: {: v
  11. {
    2 D1 ]9 D; C; O! o. R
  12. int sockfd;. C- e8 o6 \+ v' K( n9 k
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ) k: G1 ]7 _' R7 d% r6 F! j
  14.         {
    1 Q0 ^0 _. e% f  P* g
  15.                 perror("socket");$ G- V; z- [# ~$ R/ P3 z. i3 s
  16.                 return -1;$ E! J7 j6 W9 L7 \* T# T, y& p
  17.         }
    ) i; ]- k1 d1 Z# s- _& ]
  18.         printf("socket...........! K# _& i  H, w$ L$ l
  19. ");+ U) C4 E5 u; ~8 e& n2 f4 g/ L4 A
  20.         2 B6 V( S# U2 ]2 ^( U. {; c  F% A
  21.         struct sockaddr_in srv_addr;
    9 B5 w0 T" J7 u$ R6 G+ a( i. N
  22.         memset(&srv_addr, 0, sizeof(srv_addr));! ]8 t; g+ f3 y$ b; N8 |
  23.         srv_addr.sin_family                 = AF_INET;  i) M( j* u# z/ x' W2 }( O
  24.         srv_addr.sin_port                         = htons(8888);8 M7 U. Y" B2 v6 [; e! V6 ^  X8 J
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ' p6 }( E4 B6 E( `* C
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    1 p# @7 [5 h, E9 a- x, S/ j5 s% G
  27.         {
    # A2 ~2 m+ v3 X. }- t8 G: y- m
  28.                 perror("connect");) L' f- f5 L! I& w2 }/ q
  29.                 return -1; //exit //pthread_exit
    # Q9 C3 M6 n) J
  30.         }8 B' R: V- A) q0 _8 _
  31.         printf("connect..............- _* _# ~( `' u
  32. ");" q. w8 }& \% f* G! |6 v( w, d* s
  33.         char buf[100];& P9 M' h8 B7 U& ~; _8 U
  34.         int ret;
    7 }( S7 ]! N$ U- X8 E3 \
  35.         while (1)! X1 S$ V0 j/ L( n: [
  36.         {
    % Y. L6 q1 Z4 e6 g" B4 L
  37.                 printf("send: ");
    5 D7 n. l  @! p
  38.                 fgets(buf, sizeof(buf), stdin);
    , S% A# B# T8 R
  39.                 ret = write(sockfd, buf, sizeof(buf));
    4 x3 `  k7 Z( {2 B2 {
  40.                 if (ret < 0)
    , _) H: E0 f9 N
  41.                 {1 r3 y# x* l. ^1 w( I# L% A
  42.                         perror("write");
    & H$ k# R$ {/ s+ l
  43.                         break;0 h3 i0 }4 o: }( q
  44.                 }1 B  r3 S  j9 N# S- A0 ]# S/ l0 |
  45.                 if (strncmp(buf, "quit", 4) == 0)' t; a" W3 k% s/ c/ p/ i
  46.                         break;
    " }1 V% X0 k* M* `+ z5 s; |
  47.         }6 P3 v) W4 I2 I9 }
  48.         close(sockfd);" d/ E7 M( Y  r2 q  ]: P* O6 ?( y
  49.         return 0;
    ( m8 d1 f- w! m6 @8 z# l! x
  50. }
复制代码
2 O  O9 ^" E! |) @
+ J% U0 Z* `7 O
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-8-4 07:07 , Processed in 0.059975 second(s), 23 queries .

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