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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
( I) G6 }4 K: ?) M" O8 S8 j
& P& _0 Y" h/ t+ G2 ~

  c7 w1 e# E6 S! i! m; R1 \socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
* {0 ^' h/ \# J- z
6 j1 ^0 X2 k0 U% c, K( q
( Z/ I6 K5 Z- U2 s' H$ l5 n
TCP协议7 |; Q3 f; x* z; n  {7 S3 v) @
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。; m' [% c! \$ x! J& V
4 k, r3 [* u) M/ T7 {, t  Q7 l

: O0 z* X# E- ~) N# ~8 G关键词:三次握手,可靠,基于字节流。
9 y) w( a" G% E3 {; J4 l" y
* t3 f0 c& _3 g$ b" {

7 m& K8 X3 I  y. @可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
7 L% k9 l; ]  u5 V. d, i/ i0 h
+ H1 u5 A' Q; h; ^. cTCP服务器端和客户端的运行流程
5 P/ w& e1 \7 m/ `4 ~, V8 f  J. M, k+ G
( m3 e4 u' ~$ M9 x
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
, T2 S6 t: ^( H: i" h& r+ s: C: D
6 d5 q7 y* |& z! R) I

' m0 W4 _& l" @8 {' e, b& \+ C1.创建socket8 {$ r1 E* Z0 }: f% S" [1 b! u! [
socket是一个结构体,被创建在内核中5 [9 ~7 q; e- M9 _; X
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议8 ^. x+ W. S' q  G* S& m. ^  l
, b' y' P$ {2 |

7 W: i1 A5 N( x2 o! p0 ?* O1 J$ f" V2.调用bind函数
) ~, W9 E3 i: } 将socket和地址(包括ip、port)绑定。
7 V* T. N% F" o. \: L. `1 } 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序( j. O& J: m7 I5 c& p8 i, F
struct sockaddr_in myaddr; //地址结构体
: d6 `( {0 D' [* t bind函数2 ~$ n: _* b) g  h! S* d* y, Y
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
. n* Q/ X; L0 M( {$ e1 {! T5 Y, C4 g. S9 P
* V% l! Q2 U5 }8 C% Y* {8 _
3.listen监听,将接收到的客户端连接放入队列0 @6 F/ D1 b) J( S
listen(sockfd,8) //第二个参数是队列长度* ~+ I0 Y& w: j4 [5 X
5 l7 C' D8 a& s: z( Q6 L

" z& S  H% ^( p& v  s: v4.调用accept函数,从队列获取请求,返回socket描 述符0 |- P$ h0 e) J% C
  如果无请求,将会阻塞,直到获得连接1 R; C  V. G* R! Z; L, X  {
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数. d6 ]) @0 _8 H& r1 X+ R5 j0 }9 v

; z% A+ i$ A& e' m7 i7 ?7 J. t; n$ t
( k# T6 h* ~$ f* Z8 K6 g
5.调用read/write进行双向通信
; D7 D8 F3 a" j6 `' ?6 _8 v% K7 r5 B9 r$ G8 Y7 ?" e

& D" z" E3 K- y2 ?% _7 ]6.关闭accept返回的socket& ]0 ?! L, k6 _9 z" \  r
  close(scokfd);
/ G1 u4 h- Q% H! x3 ?
5 `+ d. C4 o- J* b

' o, D+ f. r' R* a4 O, B; x: b5 B0 d" j" A, f
+ P0 m8 v, H0 A2 a$ i9 k
下面放出完整代码
' g6 W& u& Q$ e. o% G
1 h! T2 [$ k9 m" K  L5 T
  1. /*服务器*/
    6 F( K  o3 q2 u- z  o2 I) t1 v
  2. #include <stdio.h># a+ B* p. u1 o/ ]( u: l9 h5 U
  3. #include <string.h>7 |! Q& O' \4 {5 ^/ X2 N3 X  l8 Q
  4. #include <stdlib.h>& ~# H- C( R4 D8 j$ t
  5. #include <strings.h>6 ~+ j: |( [: x% |$ P8 a
  6. #include <sys/types.h>
    , b2 P$ l) F' E" s0 Z/ \- |. Y
  7. #include <sys/socket.h>7 ^. H5 u; S$ t# S$ k1 x
  8. #include <arpa/inet.h>9 G) m( \8 m" k3 j
  9. #include <netinet/in.h>0 f, h) n; O# ~/ q. t* s8 C
  10. int main()
    4 t& _  A) g6 ~6 _) H! |! N
  11. {
    5 x( p$ Q+ ~; R
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    + `5 [! w1 v  j' }- L( l% `8 r* u5 X
  13.         if (sockfd < 0)
    - i. h4 A9 j, i- K$ d5 o; f: N1 p
  14.         {! q$ r. A& c- u; s
  15.                 perror("socket");
    / m( r4 s' T! a, Z$ L
  16.                 return -1;- t* h+ b6 W" {8 N, V# J
  17.         } //创建失败的错误处理
    ) |4 D$ [; y! Y% U
  18.          printf("socket..............
    $ M) j: m$ h0 F2 _$ w. j3 S1 P% T3 }
  19. "); //成功则打印“socket。。。。”
    : A* O2 G0 ]  U3 p
  20.          0 y0 Q. b0 b: ^2 d8 p7 e$ N0 h
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    3 S  _* W* ~# n' F# E3 _! E, F
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    0 B5 }4 c, z! H' d' C% J+ S
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型/ C7 I$ J! j: x$ ?. T. B
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    : C, C7 f6 x6 @0 N2 g! g
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址9 n" R/ [7 H5 Q2 D: l+ _& w% ~
  26. 7 z# ?2 S) s4 x( N
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 z+ [9 k9 N5 n9 P
  28.          {
    6 L0 s* Q+ p3 u- o
  29.                  perror("bind");6 o5 E7 X! u# m/ b( y
  30.                  return -1;
    : \1 P& E/ ?$ p
  31.          }' N- D# B* _: F  o0 F/ B
  32.          printf("bind..........
    # o* O: X8 I, X6 U! l3 b, p
  33. ");; w& t0 o( F5 q  N% e  W; d
  34. 4 K7 J1 b4 {& E+ _
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听) k7 X! x8 c9 e1 |- S0 [
  36.          {$ h3 f( M5 [6 u- y
  37.                  perror("listen");
      {0 J, j$ U/ Y/ K, o( T
  38.                  return -1;1 |% ?2 w7 O( r* T8 B+ G! U' g
  39.          }
    6 E7 O+ f4 ^( m' ^  h
  40.          printf("listen............  S) }8 d7 G0 Z+ u- I7 V9 `5 J7 a
  41. ");
      q8 l; `6 e! R6 k
  42.          
    7 [& j" |) p+ [/ n% V" S) X: r; G( Q
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    0 |  `% a2 B4 S( S
  44.         if (connfd < 0)
    $ Q2 `) c4 V& g, P! N0 A
  45.         {
    & s& u$ l" b9 v
  46.                 perror("accept");
    7 @2 C; V7 p; Z; ^0 s+ |
  47.                 return -1;5 e; U, Y) [" S* z
  48.         }% O/ p; k% S$ N+ ~& l+ n' o3 |8 U
  49.         printf("accept..............; S2 D: e6 Y( T
  50. ");
    * G9 f: e7 k3 t! w9 w
  51.         char buf[100];//定义一个数组用来存储接收到的数据# ^/ u; w" v( i* Z# I
  52.         int ret;- ]8 V$ \  `7 z. r! _
  53.         while (1)4 |% E- i. n$ c7 k3 J& x
  54.         {1 W2 E: X$ U$ M( b& e4 v. A
  55.                 memset(buf, 0, sizeof(buf));
    - Z( l+ q; B+ k; `# J& k/ }
  56.                 ret = read(connfd, buf, sizeof(buf));# n+ L4 ^4 a0 O
  57.                 if (0 > ret)! Q+ w5 [- d2 \  ^" @
  58.                 {; v# M  [& D9 Z: w" [
  59.                         perror("read");
    : r# m. [8 T, b  q$ R
  60.                         break;# T( Z  R" D9 y! n# D# [
  61.                 }//执行while循环读取数据,当$ w0 i# k9 r2 d! r0 X6 h
  62.                 else if (0 == ret)- w) n' z) E% l' i
  63.                 {  w8 J- D8 o$ R  A3 `
  64.                         printf("write close!! o% K( X/ l) m( ]9 z8 a
  65. ");
    ! z# W1 t% N% P1 I
  66.                         break;1 j8 Y. j  B& ?7 l2 d/ M; N
  67.                 }8 F: T* o; P3 f' b8 R
  68.                 printf("recv: ");
    4 L! N" J5 c7 N( i& P
  69.                 fputs(buf, stdout);//打印接收到的数据3 e2 [9 M9 M( d2 |8 q8 L7 b' B4 ?
  70.         }
    4 h4 f( b5 u5 H( i. |* h
  71.         close(sockfd);//关闭套接字2 u% }( Y9 }) F+ s% T/ Y* ^
  72.         close(connfd);//断开连接
    1 U* y0 H: K/ ~, k  z, i! L. f
  73.         return 0;
    6 B$ A! ^  N- o) L0 |, j
  74. }
复制代码
6 ~6 q: Z) U) G1 J# `/ a
: l, C* a( A% O
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    : E/ V& M, u0 i) a
  2. #include <stdio.h>
    , W6 f# s- _% n, a' e: Y. S
  3. #include <string.h>3 j! S9 o3 g3 Z
  4. #include <stdlib.h>
    ! A8 @) k/ l: i" G  @- |9 W
  5. #include <strings.h>  A# ^. t! c# h; R! @
  6. #include <sys/types.h>
    / X5 i3 r  ?6 j' E( a' |- M2 a( T
  7. #include <sys/socket.h>- c1 {1 c. Z5 N/ S7 E! ^
  8. #include <netinet/in.h>- R  H, t' N) i4 K- \
  9. #include <arpa/inet.h>
    ) z8 h6 u0 ~9 D
  10. int main()8 g$ e/ f0 ?0 J7 D
  11. {6 r" j3 H/ o& }1 t/ r. V4 T
  12. int sockfd;' N1 a: Z& U1 s8 v* a3 ?
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ( F2 n2 V1 n1 ]
  14.         {
    1 @/ \) h+ \& A  ~2 U( C# b
  15.                 perror("socket");
    3 D# s, i8 M8 v
  16.                 return -1;
    5 E2 j" F% `" A, c, G/ Y# F
  17.         }
    1 \- [: V) ^  ^# u- l! @3 \
  18.         printf("socket..........., F/ D8 C* U* f% a; S7 _( m4 A8 k
  19. ");. g- S3 O+ P0 E) j5 d
  20.         
    2 V  l4 j( W0 B6 l% `+ O* ^
  21.         struct sockaddr_in srv_addr;+ ~$ z4 U( K& Y# R, x7 k, d
  22.         memset(&srv_addr, 0, sizeof(srv_addr));6 s* G, n) B2 ~& a  P) o
  23.         srv_addr.sin_family                 = AF_INET;6 h# g. _. r/ j/ x3 ~
  24.         srv_addr.sin_port                         = htons(8888);$ d  M1 r3 C  }% c0 Z
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");% `" R3 s# t' a# M* I$ z: O; @5 i
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    4 ?3 j0 V8 ~/ u8 Y, V; g, S
  27.         {
    9 j% {+ ]# y3 a# O9 n5 q9 S/ }8 Z
  28.                 perror("connect");
    # z2 I6 f# `1 V" }# L
  29.                 return -1; //exit //pthread_exit! y$ Z+ j; Q) ~; [( j' J1 r
  30.         }9 T4 F+ r" f. H5 S, p: U
  31.         printf("connect..............; y+ N% ~4 W  z3 C8 m% T. X
  32. ");' B: a6 n7 ~$ K
  33.         char buf[100];* z4 {) _" w5 S0 A
  34.         int ret;
    ( Z$ g& D4 ^& t4 W( h
  35.         while (1)
    ' L* ~1 @6 ~& V4 u% Q; S: G
  36.         {$ \; v. F3 w3 o2 a' H7 |- y' m; |
  37.                 printf("send: ");# ~* D! w5 E( Y$ l7 q! }
  38.                 fgets(buf, sizeof(buf), stdin);3 v0 X5 q9 [' A7 V8 i1 x6 m0 y- ]
  39.                 ret = write(sockfd, buf, sizeof(buf));
    5 R( f# I" S  Q5 }) V3 e
  40.                 if (ret < 0)
    4 _2 K' i/ s/ d5 A$ J
  41.                 {5 f% ^. h& m: G& z
  42.                         perror("write");) }- S8 y) L3 T5 x( i" c+ ], {% `3 r
  43.                         break;8 I% \& V3 M! b3 A: d8 J  \
  44.                 }' a  {- `! S) |  g* ^- ^5 v
  45.                 if (strncmp(buf, "quit", 4) == 0)
      W+ a2 G  q3 T2 L- M# j
  46.                         break;: d# }1 b& w& J' q% F
  47.         }9 L; G5 i7 N6 Z* a
  48.         close(sockfd);/ |, N5 ^) Z0 u! J3 J1 r$ X
  49.         return 0;
    + @4 P2 T2 a$ d
  50. }
复制代码
" [- j; Q) Z) r* ?$ k( }/ x
5 b2 z" C, D: p  |9 p6 I
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-19 22:29 , Processed in 0.060898 second(s), 23 queries .

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