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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。5 R# \1 T! X) J  v! J  u! [

/ D  a3 T; S6 @: e: m; t9 Q5 a1 @

# g5 R0 w& r% X, \1 K7 F: dsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
2 K; b$ {- l, C/ s$ v; `- ~/ _# @1 A3 F6 H' ^/ C# T2 W
- ]2 s1 M2 |, ]) v0 D1 ^7 S
TCP协议
6 P1 C# B) E" D+ _' x: S) Y5 |TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
. {) x3 w" p: Y; c
8 b$ m7 ^. t3 E, f) `. V

7 A- X% P, k2 k, _关键词:三次握手,可靠,基于字节流。; R4 n5 v/ r7 F7 i6 T1 h. U- J

8 x! J; w' Q* u" G

# J6 G2 F% l6 g' `可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。3 U* e: T0 A8 {8 M/ W/ v
7 e8 g& h+ C7 `& a/ t) f( X
TCP服务器端和客户端的运行流程1 ]* ]1 s3 p% c$ n: Y( @
3 @0 L: S& V7 S& q7 K0 a

! R7 l) W6 h7 Y如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
! Z9 |  w  f5 e: F: s. F- T- j' Z9 ~. Y
" p6 x, g0 d+ T. k
1.创建socket9 [% x% {! T( h, Y
socket是一个结构体,被创建在内核中  r  Y- N/ N6 [5 ?" R: b
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议% I4 _: n4 j' I/ r" q

2 g( w" I# e6 z. Z0 y  A

& d0 {, ]$ f1 V& d* h& Z* t2 ?2.调用bind函数2 s6 H% g. |# ?7 ?5 y' i
将socket和地址(包括ip、port)绑定。& |! K+ J  {+ R$ i0 ~) j
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
. O2 X2 e) `# M9 U3 z- s6 ]: x struct sockaddr_in myaddr; //地址结构体
% s  a" f& ~  d# }6 ] bind函数
5 Z9 j* v0 |5 U& y8 }- i bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)), Z/ r' }/ L. @2 V

- a7 v* ~% s0 }* S8 {  \$ ~

- f; C* G8 S( Z5 t& R- i3.listen监听,将接收到的客户端连接放入队列
& v  C( Z5 y# @ listen(sockfd,8) //第二个参数是队列长度# F2 f- k( h) f9 o8 B
5 m$ h. r, e$ P: u- n$ f
# w, Q  c2 K; n" s% ]: C( \5 k
4.调用accept函数,从队列获取请求,返回socket描 述符+ a% j' f6 w. j7 o" h
  如果无请求,将会阻塞,直到获得连接  t( V; g. `+ f) |6 R) y9 y# o
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数5 k% k  Z7 D/ t! k, a! S5 m
; e/ K6 x0 f5 `; ?: J$ g

1 M- D7 k0 t3 K# O  \5.调用read/write进行双向通信
8 Z1 c( k( U  {/ ?. B  Q( P2 {# p

. v2 S6 r2 z4 N3 D: ^+ a6.关闭accept返回的socket
3 W7 n, Y0 E& c8 B  close(scokfd);% |2 S9 ^$ K+ j
. E+ L% ~4 C* Z# m" I- G4 C* w4 [

7 d, X2 E7 r3 U% k2 O: A
/ R* z3 I  f& B! y' w$ h" y" a4 W
- J4 Z2 K1 V, d- U% v  b
下面放出完整代码
9 h/ Y0 u' k2 g5 ]! r0 E9 h# P, r$ u/ F
  1. /*服务器*/
    " v: Y/ @8 K/ N; @
  2. #include <stdio.h>+ E* ]+ r7 h' v  D
  3. #include <string.h>4 F/ d  m3 c! N" }% Z
  4. #include <stdlib.h>- i3 p9 h. I* I. @
  5. #include <strings.h>+ U' ^* B  L, Z/ {- H2 d  u
  6. #include <sys/types.h>$ w0 D/ p) R1 P, B5 m% Q9 J, ~% b
  7. #include <sys/socket.h>
    ' e2 L- M: y4 s! K: Q3 z
  8. #include <arpa/inet.h>1 S4 K  f1 D! F. H, \% |, r# X  R
  9. #include <netinet/in.h>; T$ D' E1 h! k5 ]* y
  10. int main()4 A# P7 R* a2 ^, J) W0 s7 ~8 c
  11. {- K- o- D! B/ G8 P5 {$ c& \9 Y/ @' v9 E
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    7 [5 ]6 g9 M5 \. H: e
  13.         if (sockfd < 0)
    # r6 d' l4 u" E# S/ O* O5 Q
  14.         {0 Q; O, m1 x+ j5 ]$ H  u9 i5 h
  15.                 perror("socket");2 g% \- Z. \+ ^/ m9 ~
  16.                 return -1;
    ' R2 A3 A9 C5 m' r2 c* w4 ~2 x+ |
  17.         } //创建失败的错误处理! Q9 T' X5 S' ^
  18.          printf("socket..............
    6 t, f3 ^, W# D' P, |0 S: e4 a
  19. "); //成功则打印“socket。。。。”4 q  }8 U9 p1 I* g! n
  20.          5 x  e! @- ?: v! c+ ]
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体# J# y2 Q4 x) v2 m; P
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ' p7 U5 n& x1 T% _% w
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    - h' _7 m! s4 N$ A: y
  24.          myaddr.sin_port                 = htons(8888); //选择端口号6 z* o. u: c! {4 U
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    5 _# L. z9 |+ D
  26. ) F1 r& x0 ^/ g9 T; j- I
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字$ b+ u' C" }# c5 W4 S+ n1 Z6 i7 d
  28.          {& p& z9 P7 [/ w6 ]5 \
  29.                  perror("bind");  X9 ]) p6 Y2 z, N8 ]2 p
  30.                  return -1;
    / O( V& w( e5 U) t
  31.          }# c+ n8 E9 a& r
  32.          printf("bind..........
    # J: E% ^, ~7 X$ \' X
  33. ");& }3 p" z# h4 A, w( j& s+ k/ l3 e& n) `

  34. 6 [! a. E4 J6 ^8 w
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    - r# h# O' E8 K
  36.          {7 l! I2 N+ G, `+ x
  37.                  perror("listen");* S+ f3 z, `/ K3 D. d  E
  38.                  return -1;
    ) B4 O; g- w. S! m, |% m) _5 X
  39.          }
    - {, C: |- f2 C, M3 @$ R; _7 ~
  40.          printf("listen............$ ]2 U: \0 s9 Y" D8 U
  41. ");
    - f0 r7 F6 [' K* ?+ y; Q
  42.          
    ) A5 h  _( X0 z" Z, Q0 @4 y$ `
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求5 A; Z; D" _' a; N3 l" ]7 x
  44.         if (connfd < 0)
      M4 ?: u- b! U! V
  45.         {
    8 E: g9 W- m/ K* H9 v
  46.                 perror("accept");8 K3 n; l* |+ C, h$ y
  47.                 return -1;
    * f9 U$ W5 e. B. W/ _
  48.         }+ V' @- B; J7 J: s: ?" h
  49.         printf("accept..............
    ) q$ @; F6 z) @+ r* K
  50. ");: y5 I) h6 @# h
  51.         char buf[100];//定义一个数组用来存储接收到的数据' T$ `  Y& W- G9 q$ f
  52.         int ret;
    ' Z. U  B3 V. f5 T6 l( P
  53.         while (1)4 t( g6 Z, p6 }; G; E
  54.         {
    ! Q" ?4 I$ @( G9 ~& e
  55.                 memset(buf, 0, sizeof(buf));' S; ?% S  o& p& g! Y% M
  56.                 ret = read(connfd, buf, sizeof(buf));
    9 e# w% g9 [! D  r+ J- p+ m
  57.                 if (0 > ret)& X5 I, z1 f. a* X# X
  58.                 {
    ( X- S, l" i% q0 j
  59.                         perror("read");
    4 B1 S' J- U9 Q) G; r$ ?$ ~
  60.                         break;3 o/ w$ j& ~; T3 d8 Q+ n& w" ?; \- ~, G
  61.                 }//执行while循环读取数据,当
    3 a. A" w' P5 K) @2 Y; q
  62.                 else if (0 == ret)
    7 S- S6 C7 v1 f% R9 [
  63.                 {. W& f: V8 B  D, ?8 \9 [. y& S$ E
  64.                         printf("write close!8 z& P+ X; {& Z# g( `
  65. ");
    + p& g' g# b' ~; [
  66.                         break;9 e! X$ g6 `% N2 Q1 y6 F' a% }
  67.                 }) o" d8 j9 V- _
  68.                 printf("recv: ");
    9 y6 i; H  M* C3 {
  69.                 fputs(buf, stdout);//打印接收到的数据
    3 R. d8 |; w: m6 p' Q
  70.         }1 p6 _# |- ^' W; H2 M! M+ ~- n' h
  71.         close(sockfd);//关闭套接字
    + X; p9 p* O4 I* Q
  72.         close(connfd);//断开连接
    . T( A6 i2 |% ^& m) g
  73.         return 0;0 p- D: a6 d+ t& N$ r' o# j8 ?6 z
  74. }
复制代码
4 l1 O, A2 s& g
! X! e' d9 W8 z0 U3 M+ B: a
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    4 h# F' Y- T$ e. X: z
  2. #include <stdio.h>
    ! v5 w  ^+ S% q8 z9 s. |
  3. #include <string.h>; Y0 e! Z+ ~6 v) v3 x
  4. #include <stdlib.h># b3 H3 x' R% X9 I; T" V
  5. #include <strings.h>
    , {5 i9 f! P1 _5 l8 F
  6. #include <sys/types.h>% F! t5 P8 |; s) M0 u' z
  7. #include <sys/socket.h>
    6 K' w6 p, h6 |0 z1 @
  8. #include <netinet/in.h>
    0 O' a- r& B2 Y& S+ X
  9. #include <arpa/inet.h>
    # m/ w' h! {' n! O; c
  10. int main()! ~" G0 p5 F# i; B$ i7 N
  11. {
    & b. G3 N; }3 @3 a, n0 B
  12. int sockfd;
    ! H* F( h8 k5 R2 o
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ' N! ^- |# A6 O5 C& i
  14.         {/ m) z0 q! I+ x3 `5 o
  15.                 perror("socket");
    / V4 r3 J- l2 \" ^& J2 b, ?( p
  16.                 return -1;
    " _7 W% z5 d4 f5 t- [" Z. b3 g
  17.         }
    , Y% v8 a0 G; V- {7 s
  18.         printf("socket...........
    ( ^" a5 A5 E! K1 q8 Y* G( B7 x) V
  19. ");1 p/ z* n- b* c" P% D
  20.         3 I$ M$ _. E0 l" a/ L
  21.         struct sockaddr_in srv_addr;
    & r2 r, q' b  n6 n  ^
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    + J# x+ ]8 d  P' A
  23.         srv_addr.sin_family                 = AF_INET;
    & i2 K7 |( W6 B3 V- J$ K6 A
  24.         srv_addr.sin_port                         = htons(8888);
    0 V5 U/ o! Q" J) Y
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");8 c5 q, h; h1 L; A
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    & h8 }% M  ^" q" _/ S8 Y
  27.         {/ T" ]# }# J' y: g! p7 h; ^
  28.                 perror("connect");7 T6 g* g( I  O$ i: }% H4 \/ F
  29.                 return -1; //exit //pthread_exit
    , @$ g. D4 D8 C0 o) u2 ]1 G5 O% Y
  30.         }
    9 @3 O$ f# A' B
  31.         printf("connect..............% Y1 x) F( O! s. f) I
  32. ");6 J, t2 G6 k3 i- `3 M3 O
  33.         char buf[100];* ^# ~/ }& Z9 V. N
  34.         int ret;
    : g- x+ c* y4 g' @2 [9 z/ I
  35.         while (1)6 n) Y& z7 G* k/ {
  36.         {
    : [% ?2 C+ l6 h; z, y
  37.                 printf("send: ");
    ! I6 V0 I( j3 v5 `# R$ x2 R& E
  38.                 fgets(buf, sizeof(buf), stdin);# p/ q. U0 N& a/ a  v8 W
  39.                 ret = write(sockfd, buf, sizeof(buf));
    # R' _! ~# G4 _8 C" V! c2 n! G
  40.                 if (ret < 0)7 p. Y$ A) n5 I/ C3 ~+ k
  41.                 {
    ! f5 u  P3 P, a# B$ d/ z2 W
  42.                         perror("write");- I9 P/ K  X  s% A% L5 s
  43.                         break;
    9 k& |9 e4 _, a
  44.                 }
      V! t; N9 Q. n$ ?/ K( c7 w" P$ Q
  45.                 if (strncmp(buf, "quit", 4) == 0)4 Q# C) H6 A* o/ P0 f" O; j- i
  46.                         break;# Y% v; ~5 U% @
  47.         }
    5 C4 G% n$ L% ~- q' O* n
  48.         close(sockfd);+ k7 C8 r& C/ q: p8 M
  49.         return 0;- L& P5 Q' W  k% b0 n; P- o0 U
  50. }
复制代码
( Y% g' _& @! [; k! q

: m! a  X' F  t9 `# G2 z
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-4-30 23:21 , Processed in 0.078161 second(s), 25 queries .

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