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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

6 N4 r  t4 i5 Y

  u+ X& a# G9 L7 {! Asocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
3 r: \( H2 m' n& i- ]' L& K* o
' k7 ^$ K2 ?3 q6 ?3 `4 t) z
+ w: W: r# d! @2 G
TCP协议
1 I& z" J3 X- qTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。6 ~: Y: f# |+ `, [0 k
. b" m% S0 h! A

4 {, ^3 ^- S( \  O1 D关键词:三次握手,可靠,基于字节流。
% {" g% h; [6 H& e, }5 O
) a0 I! {; w2 k9 ]8 r7 ~" Q
2 c, S- \8 ?- p4 x: g
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。' e: n* O& Z0 w) C$ I
8 v/ U2 \4 Y. |; I7 A
TCP服务器端和客户端的运行流程" ]$ T- ^) e9 b/ A8 ~
: p" `- C* ]3 E- @7 [" K4 Z5 W# @

: _6 u, b# N4 R4 v& x, a如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
$ \) }0 g1 a0 x3 B) D* c* ~, }7 i5 y6 I" s
, L' E9 h+ t( G  q7 e
1.创建socket
0 Y+ W0 R0 m: | socket是一个结构体,被创建在内核中' q4 ^, B0 r0 s+ q# I
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
6 g3 C4 G7 u% |8 ^/ ^4 x0 x& t* p" p* l
: ^) w% V, p/ e, L5 Y
2.调用bind函数
! \) j7 b: n0 o2 z: k' i- G 将socket和地址(包括ip、port)绑定。
3 @7 x4 z) k% w6 C3 W 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序; R  e& {8 ~% y; c3 D
struct sockaddr_in myaddr; //地址结构体; o- x  d( i2 y2 |4 M1 [) t( [
bind函数4 e+ [0 K( ~# t4 V
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))0 K0 X- x; ^, Q- m; q
' H5 ^. j* b" Y4 y7 J3 ]

( h5 z4 C  j) E8 A: C' x3.listen监听,将接收到的客户端连接放入队列
( K! s) R  a% R, y1 n# B8 r listen(sockfd,8) //第二个参数是队列长度. [# J4 d: I" q; H, C
+ L4 x1 e) [; [0 O7 {0 W' K% b

- v$ ~9 v/ c* E; |$ M1 ?4.调用accept函数,从队列获取请求,返回socket描 述符
0 q- b$ W" u! E! U2 Q  如果无请求,将会阻塞,直到获得连接0 m, E  s/ Z+ ^$ w  d
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
$ v+ I' ]6 l+ ]8 r
9 m7 r) N! U' K2 e8 n" f
3 o( M. g; `3 S& ~" U1 o! U
5.调用read/write进行双向通信
: r- L' l6 F; w  D% R! W
- e& m& L" r: B4 u( v

" {# U) B" C. x; Y9 R. L# f8 ?# ~6.关闭accept返回的socket
$ X# M+ X! Q$ p7 m; f9 p& ^  close(scokfd);
# p0 A* E' x1 u; L  I; m
# ^. ~3 k+ z1 L5 `3 C, K3 @- {
5 A$ B3 n( D, C% I# {3 ^; ?
0 `& i, w/ z$ k* b2 W0 F
$ ?' s0 z2 M! ~; a
下面放出完整代码
$ M; }: [; W0 c9 c# r6 e* S5 @7 E4 [: A; B
  1. /*服务器*/+ @( E4 {; W8 S, M
  2. #include <stdio.h>
    " E- e- i- s  L, n
  3. #include <string.h>
    , p# K; k& \. f# @+ w3 L
  4. #include <stdlib.h>
    % [; z3 K( g. Y; U3 t4 B" l: X* Q3 G
  5. #include <strings.h>
    ' _4 G' A1 m6 s2 C1 @( }; A. ?
  6. #include <sys/types.h>' ]7 D* e, k' Z! R4 p" m+ s! S
  7. #include <sys/socket.h>/ p1 K6 o' n4 l4 p0 R4 a0 F" E. ^
  8. #include <arpa/inet.h>
    , s% i* \' U4 c5 x0 B& W
  9. #include <netinet/in.h>0 {; W2 w) ~/ f
  10. int main()$ m" A) E$ c5 J) l2 c. ~% ?
  11. {3 X# W% \; }* c; u
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ! U( C2 n! d: G  k3 `4 w& U
  13.         if (sockfd < 0)
    % y8 J, o8 f! @% f9 t  c
  14.         {! P" z6 g; t7 r6 B; }" A
  15.                 perror("socket");" `7 E$ ?. H0 Q# A
  16.                 return -1;( u. [# m  b+ Z
  17.         } //创建失败的错误处理+ d# ?! n5 N. X  S, R9 H
  18.          printf("socket..............
    ( z5 k- Q& f8 C) }  M
  19. "); //成功则打印“socket。。。。”
    0 X% `6 b! Y: ?5 r1 R1 J* [
  20.            f( m6 M6 l- c
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体$ d6 n7 B/ l; \0 Q7 e, V/ J/ f. R
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ) l$ n' D+ d' y, G
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型; `, f( I. u/ P4 g: N, v
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ; G8 ?- c/ e1 x- x& a4 T7 w! C2 Y
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址& `' T6 _7 ]4 z' C; Q0 C( p
  26. 7 E! M$ d+ d3 @2 H
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    3 J7 G6 A" O' G( z" U: O  d2 o: |
  28.          {
    * W6 ^  s4 o5 ^- e
  29.                  perror("bind");( b: c" w) G9 @! u
  30.                  return -1;& V+ }0 I1 R+ J( c1 U
  31.          }! D- v3 Q' Z2 ^) Q/ U2 D$ m
  32.          printf("bind..........
    % ~# P% w. N7 _0 k1 o3 e: o+ E! x: g
  33. ");
    3 s2 n2 p0 V! @9 X& L

  34. " r$ C+ ?' B6 V
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    7 b  Z& z. Z9 N; e9 E1 B8 F8 y
  36.          {
    3 L$ D. Y7 D7 b6 k, \' d( G3 [- y
  37.                  perror("listen");
    / W8 ^3 b2 n0 Q. K: z
  38.                  return -1;
    ) P+ ~0 n4 U0 X
  39.          }
    % R3 V& q9 r" ]9 n$ c/ ?
  40.          printf("listen............
    ! H, U0 _7 r0 g" h. q
  41. ");
    . S' v; e; T8 H1 T8 B, G
  42.          + l1 ~/ q7 q9 z; \; d0 c& J
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    4 _1 R/ Z/ q, X
  44.         if (connfd < 0)
    . s1 O1 X. B1 _
  45.         {' j2 I4 m3 Q2 b- u* }9 O: u# x
  46.                 perror("accept");. n/ I) p) q; c) J" q# A2 d
  47.                 return -1;$ e7 G+ |0 B' |6 a8 Z9 ]1 u" g, [
  48.         }1 `3 g! M! S0 O
  49.         printf("accept..............
    ' f# }" V* C, }7 a, k
  50. ");
    . v; W  j0 ^+ v: p
  51.         char buf[100];//定义一个数组用来存储接收到的数据' ~7 Q# U/ H+ {$ d/ a# x6 k
  52.         int ret;
    , q/ s0 S& t1 X! E7 @1 y
  53.         while (1). |6 s- z, K; u6 d' M8 d& s+ O( K
  54.         {
    5 z* Q5 B8 A* k8 ]. t% k
  55.                 memset(buf, 0, sizeof(buf));! a  N1 u$ b2 Q8 x6 w4 d
  56.                 ret = read(connfd, buf, sizeof(buf));
    3 X9 x) W$ M# D. t) H, L* `( ~
  57.                 if (0 > ret)9 I' x( m- L2 @7 N- w* Z1 H
  58.                 {
    $ Z: n6 b: r7 H6 F: B
  59.                         perror("read");
    1 C) n2 |1 Y* P& |# D7 x& ~
  60.                         break;
    & a% M: Z9 R+ m+ D, W, g
  61.                 }//执行while循环读取数据,当" a/ S6 J. N# T6 J7 x
  62.                 else if (0 == ret)
    5 p0 X2 |, a  d* h
  63.                 {
    9 E4 b6 Y4 ]1 \9 N. A
  64.                         printf("write close!
    & O7 w. C# A/ d" z
  65. ");9 R3 l5 W6 Y, v; G) l
  66.                         break;$ b) U% r3 t5 p
  67.                 }
    1 K( r* V. F" P+ \
  68.                 printf("recv: ");
    2 E3 ?; ^. y* z5 x  k# M  V
  69.                 fputs(buf, stdout);//打印接收到的数据
    1 Q- N% A# G6 j$ Y5 e- v
  70.         }, }( v" b1 p8 G3 p/ g0 [( D
  71.         close(sockfd);//关闭套接字
    % R4 c& f0 g' J% M# @; V' E+ Q, e
  72.         close(connfd);//断开连接
    $ S) J: k- b2 q6 J* B6 ?0 x( |
  73.         return 0;
    + A' `. k2 C5 K" I* m  ~
  74. }
复制代码

5 @+ D2 u- E0 Z  e
1 G! |% @: o' s4 Y' K* h+ L0 A
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    : _# I& W$ [# }  G% b
  2. #include <stdio.h>$ L6 J# U* |! ?& E
  3. #include <string.h>3 F8 a+ B" X& Y/ V
  4. #include <stdlib.h>& D* @: _: e* _
  5. #include <strings.h>6 N6 L$ Z% G6 z$ s
  6. #include <sys/types.h>9 P5 G& ?9 {$ M% j
  7. #include <sys/socket.h>
    ! ?$ @4 x) h0 ?8 l( D# g' V
  8. #include <netinet/in.h>& w, _. K5 A1 e+ N; c4 s) E. T
  9. #include <arpa/inet.h>
    4 c* U$ P0 E* }3 P4 w  c$ B9 V
  10. int main()
    # v% k+ `, Q7 X9 `2 r( ~1 a
  11. {0 I, A0 Q# Y0 U2 ^* x) u
  12. int sockfd;
    : R/ u% e, v1 p8 y: }$ }
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))6 y; |9 S* k% X2 m  f7 w
  14.         {
    % M( f! n9 Q+ ~6 e# O
  15.                 perror("socket");
    9 U" A* h1 J4 m5 j2 ?, v  L# S
  16.                 return -1;: |# r- w- P& C5 L
  17.         }
    , f4 f$ R; ?; n% ]) j; i! |7 g
  18.         printf("socket..........." S& _; w& h1 ?
  19. ");5 y9 Q& m1 _0 k. }  w( x0 t
  20.         
    3 d  L. }9 M3 z/ D
  21.         struct sockaddr_in srv_addr;% ~2 E- ]) f* J2 C
  22.         memset(&srv_addr, 0, sizeof(srv_addr));& _* U' ]7 l5 E. \2 `# ]; h5 R& }& f1 C
  23.         srv_addr.sin_family                 = AF_INET;* n8 R1 `. n9 P3 q
  24.         srv_addr.sin_port                         = htons(8888);
    . [" j- K/ S' `, t' s- p9 c* k
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");  B6 w% [1 R! R% `9 p8 l
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))2 ?) Z' C$ D1 ?: c/ y! \6 L; \( O: s
  27.         {2 r: }7 n8 P: {+ |2 u% [0 P6 j
  28.                 perror("connect");/ l) r& O9 A: g; S, X7 Q3 @3 C
  29.                 return -1; //exit //pthread_exit+ j- b. s/ ^, y2 G& u- ~
  30.         }/ c3 M; E, O/ _1 k( _2 Q- D7 l0 Q3 Q
  31.         printf("connect..............
    0 |- M7 [0 p' f: X
  32. ");
    + Z+ `$ U' K4 E1 D- S+ c
  33.         char buf[100];
    , q) u! d$ M% H, w2 d* }% V& P
  34.         int ret;4 u9 e) c! @! M$ e5 I: D
  35.         while (1)
    % e! W! j/ ~1 C/ u9 L/ E* Z
  36.         {
    $ j0 M. m$ F& k8 J8 @
  37.                 printf("send: ");
    $ J( z# K" P! Z; L! U7 p
  38.                 fgets(buf, sizeof(buf), stdin);3 z& y6 O- R5 Q$ S5 _$ T
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ! m2 w; }6 g& a/ V% V
  40.                 if (ret < 0), m3 k; [5 ^) i
  41.                 {" Y  l  m- O& ~( C
  42.                         perror("write");; j/ {0 M! }  z/ y3 I
  43.                         break;2 ?' S! s5 f7 M* Q2 m# w
  44.                 }
    8 |" L, r3 q) H5 k: o
  45.                 if (strncmp(buf, "quit", 4) == 0)
    3 L4 j; K5 c; r: x& d
  46.                         break;) w3 ~+ `2 C$ ?/ F
  47.         }) ?' ^9 B) [: \7 c) @
  48.         close(sockfd);
    / i% a" o3 }& _" Y$ M& y+ i9 a- b
  49.         return 0;
    7 a2 o' `# c, p2 {: E- f
  50. }
复制代码
) m* C) w2 x+ v2 M, j

! y% ~, {+ O3 i0 q. ?
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 00:13 , Processed in 0.059520 second(s), 22 queries .

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