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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
: n( l& Y6 W# g. D' I4 m
* `2 Q3 z6 Y- j5 D! U2 K
3 s) V9 n% a" W1 v' y6 ^
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。4 @. r& U) U) u: B3 R2 M5 N! j
. U- `- M3 U/ [2 [& g
8 a; n) p6 W! k4 @& x
TCP协议6 D5 f6 e6 p; Y0 k9 A3 u
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
: ^/ g" S; C3 f9 b; x3 y$ y. W6 c8 f3 W/ C

/ r4 C& h0 |+ m; y8 J& F  N: @& E关键词:三次握手,可靠,基于字节流。. b- r3 n. Z, _, E7 p" b- Y

2 {& z3 u0 I7 R2 I4 `9 D+ d
+ Q# f( t+ H! P6 C
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。- L4 ]" `. v8 I/ ?8 {$ L( K8 e8 t. v

5 f3 d- G( o6 ]/ ]% kTCP服务器端和客户端的运行流程* @* O+ ?# Q+ L

& B- Z$ d6 H; [* r6 }
$ w5 _9 [# R3 h4 B6 h8 }
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
7 T2 e& w3 y8 H, V  M2 g" D5 I1 f8 ?3 X6 Q/ L6 {  P
5 ?8 d/ D( H8 X, a
1.创建socket/ o9 X& \: X* E% Y0 G$ W
socket是一个结构体,被创建在内核中8 |' [7 e. r9 _9 ?/ I  E8 d
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
. r; Z+ V  p$ K3 r9 b
# V7 A9 W1 T# f. C/ u) C3 K( l
8 S$ h9 E0 {* R9 r4 Q& {
2.调用bind函数
0 k  {! j0 I2 ~5 A 将socket和地址(包括ip、port)绑定。; n! V1 m4 R; y
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
; B  S% n0 P+ j struct sockaddr_in myaddr; //地址结构体) b! N; {. q  C
bind函数
3 P( u) z5 B- a* Z! e4 M bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))& n- n- k- F8 q5 h# X7 i( U

. V: C3 D# g9 N- @# a* x1 ^# e/ Y
! ~: L( s. M' R+ R- }; Y
3.listen监听,将接收到的客户端连接放入队列4 u1 R: @4 r* y1 `. Q& X
listen(sockfd,8) //第二个参数是队列长度4 N7 B4 `0 F% l% j( p. O0 [; X& k

. Q0 i) M! ^2 |
0 A( I# n: s( d  w# Z$ s6 G; _# g1 {
4.调用accept函数,从队列获取请求,返回socket描 述符% J1 K/ {8 q2 X1 H
  如果无请求,将会阻塞,直到获得连接
6 L- B5 l# i6 J  d4 V- m  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 V7 U+ U% p9 x% U: T
( \" |7 [; i; Y0 `0 g  o' P
$ E' ~* A8 I! V- _# H' h: U
5.调用read/write进行双向通信
$ a/ q* E+ K+ W+ y! R9 J$ z3 G- M( _- N( s

# M# B2 _# s. X  a6.关闭accept返回的socket
, _2 u$ q$ I! Z! l/ r  close(scokfd);
8 _: ?2 q9 Q* o2 X- F$ _; ^8 q; |/ O& T: D% ^/ Z" A
7 Q- t6 J: c) {3 n2 b

- O% t$ p. P& o. V7 U# D
% B( N6 z% A. F
下面放出完整代码
: k- v7 L* z: `; M
3 a6 N2 S/ X9 r' c+ f5 n
  1. /*服务器*/: E# R& A/ {( w& u; L+ l0 J
  2. #include <stdio.h>
    # I# W+ Q/ Z) v! u) {% U) _
  3. #include <string.h>3 k- u( K/ R$ O0 N/ \
  4. #include <stdlib.h>
    3 D# F% R: H3 h+ g  i* C
  5. #include <strings.h>1 ~3 K# N0 ~: u& N0 C, M. q6 x, m
  6. #include <sys/types.h>
    9 H2 D$ j" }# e) m& a) q
  7. #include <sys/socket.h>+ \; {; Y3 ?9 |  O, h, m
  8. #include <arpa/inet.h>
    9 W8 n" C1 N8 a( g
  9. #include <netinet/in.h>
    6 n6 B3 [4 k2 G* D
  10. int main()+ O; [- X6 a3 }
  11. {
    5 }: D$ T% r! `. i
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字+ f& H( j: j3 x: M3 p5 N: i3 b
  13.         if (sockfd < 0)$ r8 z- L) f) s
  14.         {
    / l6 _$ L1 x9 K# ~2 I' Y5 c9 t5 F
  15.                 perror("socket");
    ! O( k9 @* g! f6 ?6 K
  16.                 return -1;% ?  x" h% L* A, x2 }
  17.         } //创建失败的错误处理! M, N. Y) ^" }. m% E& Q
  18.          printf("socket..............3 f1 H2 y, w6 ^
  19. "); //成功则打印“socket。。。。”
    ; C7 |" ?2 y! R) O) L4 [$ c
  20.          5 Y. L# @& ?% u# h6 _, T- h  |
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体. m. O2 o. M" u2 T$ }
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)$ U3 L$ Z/ D9 J9 J9 b
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ) b, E5 ]- q7 V0 V1 t
  24.          myaddr.sin_port                 = htons(8888); //选择端口号6 p" s1 _6 Q' Z$ W7 H) m6 e7 D
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址* W) e6 u. e& I2 w

  26. 7 l( w! P! J3 W, _
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    7 X4 G& l8 w  I% \
  28.          {
    * W, [5 m# {/ J) i/ P/ [
  29.                  perror("bind");
    ; i; `7 I1 f; \% L9 i
  30.                  return -1;
    ' p. ?5 r; t5 A9 v: n
  31.          }- C2 H- e2 F# C
  32.          printf("bind..........
    ( w1 g0 n3 n5 ?9 X! }- n
  33. ");
    ) b! h  {) B7 o3 v0 @3 i! T
  34. 0 N1 [5 V; k$ O+ U  ^( k, ^
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ( K! ]. e# {; Y$ F9 q" G
  36.          {1 T6 ^; N+ I5 U* Z& j6 T1 v0 b
  37.                  perror("listen");1 V' f0 n, d4 f. J
  38.                  return -1;% C9 F9 X- G$ K. O: S
  39.          }
    & B- R4 D0 G" c; ]: O
  40.          printf("listen............
    ) B; s2 o' V7 ~4 i
  41. ");9 L2 O9 W; n, o' F) }+ q& q3 z
  42.          " |* l1 u' G; Z
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ; _  I3 P% ?; Y2 m
  44.         if (connfd < 0)+ N3 A; O3 T6 T& l' ~8 N
  45.         {% |/ n5 X: l2 Z* u& f
  46.                 perror("accept");$ ]& P3 J; t/ E# {5 U
  47.                 return -1;
    3 G7 h) h2 H7 a: f# G% m
  48.         }1 s6 K. L1 N! q6 ~4 K
  49.         printf("accept..............
    % ?* E. n6 ]  O* z! q; {
  50. ");
    : b- ]& ~6 J5 a" O9 w: H# V6 K
  51.         char buf[100];//定义一个数组用来存储接收到的数据) n) ]- Y9 ~& E# t+ B3 ]
  52.         int ret;
    & E9 P( U! B5 ~  V2 w5 P$ z4 w
  53.         while (1)
    " U$ v1 n( V) j& r! D! C  u! R
  54.         {
    ' e+ [1 v. Z1 `$ |
  55.                 memset(buf, 0, sizeof(buf));8 W) U8 _( U1 `* ~9 o( a. J
  56.                 ret = read(connfd, buf, sizeof(buf));6 h0 U4 d! {/ r) M& e
  57.                 if (0 > ret)* E, u* l+ N8 _8 m( v' M+ n
  58.                 {5 E  V6 n4 o% q) _- \  Z2 ^
  59.                         perror("read");" G- [! w' Q, y, H
  60.                         break;0 ], {7 ~! k  ]; f
  61.                 }//执行while循环读取数据,当) ]0 x! ?7 [! ?0 T6 H
  62.                 else if (0 == ret)/ w$ L0 V* q4 {0 s4 I0 L4 W4 J
  63.                 {$ T" A" S7 E$ ^/ e
  64.                         printf("write close!2 z% a- L$ ^0 E  ?7 S
  65. ");
    , e1 {, v# n+ X, B. J( s
  66.                         break;) [* Y2 u. G* v" M2 a6 Z1 \
  67.                 }4 Y6 d% \9 X8 z# o
  68.                 printf("recv: ");
    1 r( b5 u: V5 ^1 }) q1 x! U( Z
  69.                 fputs(buf, stdout);//打印接收到的数据
      v' B- @, s% W4 T: w7 {
  70.         }8 Q: n; I( l) n0 U0 M
  71.         close(sockfd);//关闭套接字3 F) X$ W( E0 }5 ^* U& [! r$ e
  72.         close(connfd);//断开连接
    ! j) n% _# [" G1 k, q7 {8 y
  73.         return 0;
    * p9 C$ H9 ?2 y4 s& o. i
  74. }
复制代码
: B* g; @1 \- F0 j0 J% _

* L; T+ Z8 D* B' T' }% v
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)% M: A3 c. b. q4 ^6 r7 c- M
  2. #include <stdio.h>) T  Q8 A: d* X
  3. #include <string.h>
      X" s9 C7 h! j6 b# Q' s- x  y
  4. #include <stdlib.h>) p9 I8 k6 U! X2 w
  5. #include <strings.h>5 Z. z! u7 k* Q& e
  6. #include <sys/types.h>. q  o" M/ G) ~/ \! j
  7. #include <sys/socket.h>( B! P9 |5 I8 m7 }! n* \9 W) p
  8. #include <netinet/in.h>  e1 @1 X: X- R) F* t5 R
  9. #include <arpa/inet.h>5 J1 {1 \- i4 B+ u0 L! k
  10. int main()
    / Q: z' ^; p5 J
  11. {1 D% H2 P# D- p& m
  12. int sockfd;
    / k0 L; }6 ?5 @
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    / W  F- Y% C& }7 l2 p& F
  14.         {
    - k! y, I2 `  \, s+ p
  15.                 perror("socket");9 ], q% Q8 S# u4 X
  16.                 return -1;
    + L- _( F6 _: i4 n9 J
  17.         }" h0 ~0 i0 A3 d# E& ?
  18.         printf("socket.........../ f9 ?- `3 Z5 W5 o( ?, b; y
  19. ");
    4 r" Q6 D- z3 H# P( r5 y6 }1 Z
  20.         
    " t( t  G4 T# I
  21.         struct sockaddr_in srv_addr;  K$ I; g( B2 u8 X) t  v" P
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    2 ?  p8 q; y1 v$ V- k, L
  23.         srv_addr.sin_family                 = AF_INET;' R; y0 L3 @+ E7 C' H- {( @" y9 U
  24.         srv_addr.sin_port                         = htons(8888);$ t1 O; _1 i5 k6 S
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ) _6 `- l8 B' {! \: x
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    + G7 E; A8 K; m7 Y- k  V& u1 n
  27.         {- t3 e1 z" G6 }& |" H9 ]3 [' C
  28.                 perror("connect");" }/ I# j6 [2 s7 X
  29.                 return -1; //exit //pthread_exit
    # i) D$ d- X) U: S$ H; q
  30.         }0 A8 Y% ~# x# C! d9 w, N
  31.         printf("connect..............
    2 n" X# e# N& ]& _& i8 G9 p( @
  32. ");4 U/ `# R# t" `* C
  33.         char buf[100];
    . {2 Y: l& I  d8 {
  34.         int ret;
      l% E* X' h2 ?5 R
  35.         while (1)
    : A% `! |$ h# l+ Q1 S$ [
  36.         {$ X' u9 E2 m! J  l. A0 ?; g
  37.                 printf("send: ");7 [# C' N# ]$ {0 [
  38.                 fgets(buf, sizeof(buf), stdin);, ?. i* t0 i9 x+ V  M
  39.                 ret = write(sockfd, buf, sizeof(buf));
      s" V" p6 U0 b$ x4 ~
  40.                 if (ret < 0)( w6 A9 n' R* Z& y
  41.                 {
    - F! s6 f1 F0 e. n/ W1 R/ F  L7 f% r
  42.                         perror("write");! B8 Q# N5 O5 U( c7 C2 @
  43.                         break;2 I+ n3 P1 ?) I  p7 G
  44.                 }( F  B0 z4 H$ a  f
  45.                 if (strncmp(buf, "quit", 4) == 0); P  b1 Z( T1 `1 P; g$ O
  46.                         break;
    , I) Q( ]$ W" u' l( k$ |6 l( u
  47.         }2 h" H& }1 W. |
  48.         close(sockfd);
    3 p' w/ ^" C, g  |. q% {0 l4 S
  49.         return 0;
    " P, d: u& E& m7 i; J' v
  50. }
复制代码

4 |9 B( |& T" X4 t" R0 `8 f
9 i  |  g7 c2 I) d
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 14:32 , Processed in 0.063905 second(s), 23 queries .

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