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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
: ?$ b" C0 c* s) d
& T+ G8 _  z+ w' O
6 g. G& H% v  z
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。# B3 k# \; p+ ?0 m% z  A1 [/ I
4 K: z3 j6 D; S6 Q4 x

3 q/ ^: W4 c( P/ F" v+ PTCP协议1 F$ L3 H2 J2 w; u
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 }9 H5 D; }# t- r1 Z
4 l- @' o- \( h0 {6 ]
( C/ v  o" Z& }- i- d# j
关键词:三次握手,可靠,基于字节流。- Z! ]+ w7 }: m; g
3 }* Y5 S1 N8 Q. `/ Q1 x  g

* h: ^0 Z& P* A  y0 }可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。: |" I9 C+ D& ]8 Q

% ~, D% i6 }$ x6 a" }TCP服务器端和客户端的运行流程
1 q! K4 R: i6 d6 d3 l9 X5 _
: d# m. V3 A9 y# l  N& [! s
) f* x0 r  Q& g, j
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 K% J$ l2 b0 O% ]
1 t2 \1 o: E7 j$ o) g
: l! B( ~, k; t4 f
1.创建socket, ]: `( s1 Y. P( o' `, [
socket是一个结构体,被创建在内核中
  h4 t5 i; i, x* p: Z! V sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
! H* \6 r* h% l5 ~9 u
) @9 D" r; z- }! j0 s& V4 U
- I3 A0 \* s' P7 f& R; \. C
2.调用bind函数
) r3 l0 @4 c# C3 b 将socket和地址(包括ip、port)绑定。( E4 j2 S) w1 K2 L5 n4 }
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
: S5 I( d2 W% o7 x4 c struct sockaddr_in myaddr; //地址结构体
$ a! M' K. D! l/ V bind函数
  ~+ \6 h# y; I6 Z5 Z0 G+ c5 Y bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
6 s2 h& v6 j* {- N+ C( k: x# Z" ~8 u% @6 n. s5 b7 W: @3 {" A

% B- T! z2 ]6 n; O' z$ y6 e3.listen监听,将接收到的客户端连接放入队列
' W( Z0 c  ?8 _ listen(sockfd,8) //第二个参数是队列长度
. q! J: V# ?- k$ ~' g" g
, c0 `4 \+ h$ {' y" r

: {) n$ ^( V' |5 W4.调用accept函数,从队列获取请求,返回socket描 述符
0 x5 |# X- z( a) R7 v  ~  如果无请求,将会阻塞,直到获得连接. |4 j5 ^# N: H$ l6 l6 v! T3 d
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
! y; H* g! j8 ?/ b. N  B5 n
* V8 ]3 {. t. [" ^

! q  S% E" v3 p& `" j; ^5.调用read/write进行双向通信
1 O9 d, t: O+ S. G$ x, r7 T$ Q( o/ `2 X* G- n9 u' @5 k
2 Z! ?7 G/ L: L8 d2 z4 `8 N
6.关闭accept返回的socket8 J1 u2 k/ s( t) `9 r
  close(scokfd);7 _" h6 S6 l8 d$ C# o  E

- ]* ~% f" ^* ?7 j; Y( _7 |
! ~$ i3 E! H  o! V3 l

2 w' n1 k, I1 y, R+ i1 Y
; v7 x5 I/ A2 z) M) k
下面放出完整代码
  D6 V& ]# `  j/ L
" g6 L" p* W6 D! R; e& r6 m
  1. /*服务器*/
    7 w( u2 [" W, V$ P
  2. #include <stdio.h>
    " J5 f) f( G5 v* ^6 u+ r; U9 y$ b
  3. #include <string.h>: l) L: W& Q( P3 K
  4. #include <stdlib.h>6 t7 U- }7 e1 ^2 K1 P6 ^( T
  5. #include <strings.h>3 J. E, m: W( g" e* |+ O
  6. #include <sys/types.h>$ U: Y+ U: _+ u9 ~
  7. #include <sys/socket.h>
    ! T7 I$ U0 v. s$ u
  8. #include <arpa/inet.h>
    5 b3 C& @% Y8 x$ t
  9. #include <netinet/in.h>
    9 r! r/ I) f9 f) r1 C  s
  10. int main()/ G* {% O: c* ?2 L: {+ M; ]4 k& O
  11. {
    ! i. H/ t( N# ]
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
      S' s& c9 ?  T& D( C; u
  13.         if (sockfd < 0)
    " f+ f5 W( g) V4 o
  14.         {. i8 b* D( o9 O) z% b
  15.                 perror("socket");
    - ]/ `' l; w1 u
  16.                 return -1;
    # N. q6 ~4 J* M& b  o2 R1 O1 L4 K
  17.         } //创建失败的错误处理
    1 }, \  F* R( _6 T9 }
  18.          printf("socket..............% k% `  o' [' s( \# ]! t3 ^! z
  19. "); //成功则打印“socket。。。。”- a/ }' G# K& t4 b# A+ b
  20.          
    , w9 U$ h* N" z/ b# A% _, {
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体1 y: a) q7 E- H) f# P* t5 u
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)8 j2 N, r7 M% y$ w2 Q
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型' L: Q. u- p9 k0 E7 X
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    % r) i+ o; x& k$ R1 q
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    9 Q# Q- R% f- b

  26. 0 @, _4 r- C$ i$ `0 A
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字, F$ m# i8 {$ w$ v0 V/ d
  28.          {
    ) z0 i5 J$ j( H  u" k1 w& t1 c
  29.                  perror("bind");  X* p) @6 K* w0 p; G5 y$ K
  30.                  return -1;4 p) [# Z- R- @& H
  31.          }
      w& g- [. \9 A! L* |5 \
  32.          printf("bind..........
    % ]0 v; B" V7 ?
  33. ");
    8 S8 L' I5 }0 V# ?/ T0 h9 ^
  34. " d# M* k9 D5 h+ u, \5 A( \$ K& k
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    8 y# g" X) `& w2 _4 }
  36.          {) a, D& l4 B% Q$ w" Y' _
  37.                  perror("listen");8 @3 ~, j1 b. k9 j
  38.                  return -1;
    : W5 R$ R! R4 H
  39.          }3 I6 {1 ~: T" c" K+ }2 p
  40.          printf("listen............8 a: r, H: Q/ ^* }
  41. ");
    ; Y: v2 }; ?+ x$ K( F2 v- ^1 T8 F
  42.          , q6 V) E, i% [" V) y% P
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求" d) B* Z7 @; [$ D% a  b& `4 B
  44.         if (connfd < 0)
    . W; Q  F7 r; M$ t! R
  45.         {" u+ b0 y1 T" `* b6 x
  46.                 perror("accept");
    8 a( @6 D  F6 x3 `
  47.                 return -1;
    - M7 X: [$ ~, U' P3 ~
  48.         }/ x, E9 x" F8 I
  49.         printf("accept..............3 i4 |% T$ z" L( B
  50. ");$ R! w) y2 I. h7 X
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    ' T1 q4 X1 {2 }  N* [: I
  52.         int ret;
    0 A7 _: L# ]! Q' i
  53.         while (1)
    8 I# ^* ?  s3 b: X3 B. D
  54.         {
    . f. k5 _) F4 i' c
  55.                 memset(buf, 0, sizeof(buf));! u9 P5 F7 [+ O6 H5 K
  56.                 ret = read(connfd, buf, sizeof(buf));
    & T4 h7 R5 P7 k( N5 d* u8 l
  57.                 if (0 > ret)1 g1 J5 t+ K1 k+ p. X% t
  58.                 {
    ' {  N$ w% y& O+ G! j8 U7 j
  59.                         perror("read");4 r. ?* }, C6 w5 S/ f& m
  60.                         break;
    / ]" T- N5 l6 t# ~% T& C0 {6 p5 W
  61.                 }//执行while循环读取数据,当
    % C" P  |. ?) ]( o3 w4 @8 G& S- m" B
  62.                 else if (0 == ret)
    5 g1 @, |6 o& U3 U/ f! O* \
  63.                 {2 W0 j% H) b. V7 t
  64.                         printf("write close!3 C& }( Z' T' x9 |
  65. ");
    9 W# ~3 \8 ~  ]- ?* p' e! G2 |
  66.                         break;  C7 ?6 s# C. l6 n9 p$ ]& H/ O; T
  67.                 }
    ) L2 i9 M$ B. u- ~8 b' Q+ Z
  68.                 printf("recv: ");
    - r% |( z; ~) W( }8 ^0 K
  69.                 fputs(buf, stdout);//打印接收到的数据9 z! I' M' v- ~9 v6 d9 Q
  70.         }
    ! P( M2 q1 B" C  ]. f
  71.         close(sockfd);//关闭套接字3 J- p9 i( X# B- \; J9 f6 q
  72.         close(connfd);//断开连接0 ?/ ?$ K1 P2 n6 ~; k6 m
  73.         return 0;
    . i* t; V- s" q  i: _& g3 `
  74. }
复制代码

, q& X- X1 y$ i5 @3 I* T; O: O8 J+ G! s
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)2 m6 d* o! P2 Y; A- E
  2. #include <stdio.h>
    / J# W% s( W( x
  3. #include <string.h>
    5 k2 Y% z6 N( Z, ]
  4. #include <stdlib.h>- J; u6 ?, _8 c: G
  5. #include <strings.h>
    5 H4 C* ~6 U. ^" k7 c# x( o
  6. #include <sys/types.h>
    7 w. b7 _) k, k6 ^& ^
  7. #include <sys/socket.h>
    : t  y& [' Q* _% A
  8. #include <netinet/in.h>
    $ x/ j2 t0 ~( ~
  9. #include <arpa/inet.h>
    , O0 }- j/ I# y7 A, n% c
  10. int main()
    3 a: Y2 u! o: E, J0 q
  11. {& l. k' ]4 ]# X& ]
  12. int sockfd;$ h2 U/ S& R& v
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))+ d  g' E+ g/ H' i
  14.         {; r! S/ f) q: T8 }. L" L
  15.                 perror("socket");
    : q9 V( [6 M4 ^' |8 ~  y
  16.                 return -1;# |7 t8 V9 F  a9 I, e: T
  17.         }
    0 ^# k. H' N# \% F( l; q1 k
  18.         printf("socket...........6 \( J9 I2 G1 Y. m! s
  19. ");0 ~7 Y) D. D$ u- {6 s
  20.         
    8 q. Q5 b" V/ i. e& i9 k5 {4 N
  21.         struct sockaddr_in srv_addr;
    ' a3 l# Q) W* {8 Z
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    / ^* e' w4 F$ O* F, A- D
  23.         srv_addr.sin_family                 = AF_INET;
    - F; m" F/ [* ]- c6 N: s: C
  24.         srv_addr.sin_port                         = htons(8888);( z$ F1 @6 H) o. f" O
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");0 Y, _5 }! P5 I8 v" k
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    / w+ B+ x# d& i3 S8 D; g
  27.         {
    0 @) t2 }! N5 x" A
  28.                 perror("connect");
    0 T) O$ K4 j/ A; p
  29.                 return -1; //exit //pthread_exit
    ! ?- p2 @4 p# x' }
  30.         }) z- Y. C! p/ n: s1 [% g# X
  31.         printf("connect..............
    7 q' q6 o# \3 Z& A$ V
  32. ");
    5 Z8 o5 q' [1 U: ~0 M9 d; n
  33.         char buf[100];
    + v7 [# p' r2 y* |
  34.         int ret;
    8 b& @$ V0 r6 z* W; x) U
  35.         while (1)
    ! @5 o" n( C  r# g2 ~3 a) U
  36.         {. ^9 F, w8 g0 ?- y' P* a) G9 w  ?8 ^
  37.                 printf("send: ");
    ! ~0 S: r0 h& Y1 O( t) x9 s. L
  38.                 fgets(buf, sizeof(buf), stdin);. w0 ^8 }1 }5 N  z. j& D+ |
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ' v) M# e$ P. C; J4 O! j
  40.                 if (ret < 0)
      r4 Q6 p1 W/ o+ V/ B/ Q, }* S' f% O
  41.                 {7 ?' y2 R) R/ h9 I% D
  42.                         perror("write");
    1 s4 e: S8 z+ L
  43.                         break;
    " J- U! P5 l1 o3 [
  44.                 }+ u  e! C, j( M+ d
  45.                 if (strncmp(buf, "quit", 4) == 0)7 @& K/ N7 O/ K: D  B
  46.                         break;
    2 \+ V+ I" d/ @2 u' \* r
  47.         }
    4 P4 ^% q, q, y+ K+ p% z  C$ |
  48.         close(sockfd);
    4 }0 D( {) x4 u# f& m  S# K9 I
  49.         return 0;
    & o8 \3 \; z, y) K
  50. }
复制代码
0 r* R$ G4 r' g/ r  w! `
; \: Q( a4 C! s: J
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-4-20 07:20 , Processed in 0.141320 second(s), 24 queries .

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