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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

8 y! B6 ^3 z" s, U

' h. _" M& C% T5 Ysocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
) p7 G" v9 V' O8 c7 @' a( }: {
" E' Y$ R+ u! _. R9 `0 X

; U2 o4 G8 l/ O8 d! _2 kTCP协议1 H! m, W2 ~. @
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。7 O" O5 c# ~: c
% l2 K) J9 T1 D! i1 R7 K
) M3 u& E4 o8 B3 F1 I3 b* \+ x
关键词:三次握手,可靠,基于字节流。
& ]$ p5 S* v2 }% W8 w
% E7 D! J0 J  F+ J, B
; c& t# m1 P% y3 v  y$ J& q0 G
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。( R4 Y) o$ s4 _9 V" `! a/ _) ?

5 d8 b3 D3 L" G: MTCP服务器端和客户端的运行流程) ?3 {% Q4 ^% ^' x/ v% @5 s

. `" d& M$ G1 Z  T
* [7 r. B7 W( l; c$ w
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?! m7 A. X& ?0 C7 f9 `5 i

4 E1 p2 B8 |. f$ G

2 B7 c9 V" `# E  W8 D% d1.创建socket
' \. Q7 J& Q- D8 M socket是一个结构体,被创建在内核中" @( \4 y! r+ e3 ^1 a- h
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议# G. d$ w6 L/ ~6 ?; m
( S4 [3 x0 y9 b
8 E6 A; o/ N% u* p4 q6 c8 d
2.调用bind函数! g( a5 r1 ~/ L: L6 ^. m  D9 v
将socket和地址(包括ip、port)绑定。
( e/ t' K$ @1 m4 k8 H 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序3 U. @" i* ~( j" S4 E' [  z7 \
struct sockaddr_in myaddr; //地址结构体" D9 d' C9 h* p2 v7 ?: X: t' U
bind函数1 T! [! E+ \$ U
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% c8 V5 ?8 T% r& c' C3 w1 n
9 l$ ~$ H& t) g% \. L9 b! Q
8 s3 u$ D; l# n
3.listen监听,将接收到的客户端连接放入队列
9 \, @) b+ x3 M* a% d$ w% \ listen(sockfd,8) //第二个参数是队列长度  i7 ?# ]: X$ h, l* }' K
  P; u9 b- n+ S% d3 \

; B+ Z. F: Z: S1 ?' U4.调用accept函数,从队列获取请求,返回socket描 述符. W# k3 d: `% ^; M2 i
  如果无请求,将会阻塞,直到获得连接) q% }& g1 P. {1 V9 E2 f
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
! K; k  j2 h! A! u! G# D
3 i- y: e$ E6 Y1 [+ g" p; O
+ Z+ y* f: g- \
5.调用read/write进行双向通信9 D' j  q: F4 x  N9 M, K5 q! e
$ O7 O6 j+ C  U' H- h9 Z% d8 t
9 V3 m( e1 w) p" O$ c7 R
6.关闭accept返回的socket) @* _) P' z! v3 b0 L1 D
  close(scokfd);
2 N2 H  s6 x+ c4 c' Z) Q
8 ]9 D. J% E- j" a- F

- J- }$ s$ P# }% I  M, F8 F& p& {1 Z; i) p# H4 i$ U7 r. b

; @0 r! h8 X$ r) T7 o( k下面放出完整代码5 [+ g9 d8 H* t/ F! [* J5 g8 c

+ _# f/ M4 G$ X$ j/ o
  1. /*服务器*/3 y8 t8 f$ G6 l# l; h  F5 C
  2. #include <stdio.h>
    & L, d! f5 d7 _4 x9 y" v' m
  3. #include <string.h>
    % B! ]; ~0 w* g5 p- A
  4. #include <stdlib.h>- V! a9 p: S  a, q" n6 T
  5. #include <strings.h>: }5 P7 i  J7 |4 ?: l& M2 t
  6. #include <sys/types.h>* J3 [) p) p8 j* s' t1 ?% E4 G# k
  7. #include <sys/socket.h>$ G1 p. P/ k5 V$ j! f
  8. #include <arpa/inet.h>
    " c/ h' k) A8 Z1 X+ D
  9. #include <netinet/in.h>9 t& {+ }4 @+ r2 o5 [
  10. int main()
    3 O) [: e8 v' F* X* v( R2 v9 w0 e
  11. {
    0 c) G2 H" i9 l/ r+ V' p
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字6 Y9 V" z, p. a7 S1 K; r
  13.         if (sockfd < 0)+ f7 a# _3 j3 w; G
  14.         {
    : ^! a% G- Y/ c5 T
  15.                 perror("socket");- I; w" A/ M% K9 f$ O& f, ?
  16.                 return -1;
    * c4 U6 i9 c6 s& a2 r7 M9 z
  17.         } //创建失败的错误处理
    8 ?/ e$ |3 `4 X4 r( ?4 d* K5 o4 J
  18.          printf("socket..............% @( [% I! B" V
  19. "); //成功则打印“socket。。。。”% m3 N# E% y/ \/ s; L: R! w9 o
  20.          " _( ?2 F4 Q: n) L6 _) J) G
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ! q- T% o7 s; E. g% L( m+ N
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)* `& s' E, o9 I
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    # o# R4 _) }" H7 |! m0 D
  24.          myaddr.sin_port                 = htons(8888); //选择端口号- j; K4 q. |4 J# ?. `, h
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    - G( T8 @! Y. s, |7 b' x' l: B* {% M8 a0 ?
  26. ! }" B2 Y4 V5 x& ]0 c
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字- f) O" `( j' d3 |7 }
  28.          {3 _. W) _( s; ~7 h
  29.                  perror("bind");8 J0 Y" \' m% p  d! A' l
  30.                  return -1;
      j' d1 `, a- {" ^9 R/ T( N' c
  31.          }
    7 `4 s( [* Q" Q' ~2 B" ^1 q
  32.          printf("bind..........& q# t# H, h/ |+ I4 r1 X& d$ e
  33. ");' C8 R  L& @5 n0 o0 ~; G0 i

  34. 6 e1 y0 `, Q- k3 w9 G1 g) A
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    2 O6 [9 b5 m7 Y( s
  36.          {# n  n" S; I+ {6 M9 i9 B4 d* s& W
  37.                  perror("listen");* e, u; |( H$ _9 D
  38.                  return -1;' w$ H  M" R% _
  39.          }
    5 e- I0 E; o0 H! s: {; k9 q. r( |
  40.          printf("listen............5 E4 L; M6 \' c) C" V7 z
  41. ");6 @; `6 P  p6 O9 s
  42.          ! b0 b- e  i. C
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    & t& D1 [2 C0 W# {2 o2 D9 e4 P
  44.         if (connfd < 0)
    * m- B0 E# T  k+ v# m
  45.         {
    / g( ~( A. @2 R) \1 T7 f
  46.                 perror("accept");% t1 }4 A" N6 k. L7 ?  x
  47.                 return -1;- R7 ^5 Q& Z" v* t( l
  48.         }
    % f2 |. U5 C  Y
  49.         printf("accept..............
    ( X1 m/ W' Y- F: N; H' ^3 [) \0 j2 L
  50. ");9 N  _9 m/ G& }) E
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    . n- P0 J. R% S2 s& V2 W# w) p
  52.         int ret;
      g, C$ c" w# ~) G
  53.         while (1)
    % @' \; V; G! q$ G+ ?' e- f0 Q  k
  54.         {% x; x$ c8 U" I& y
  55.                 memset(buf, 0, sizeof(buf));" ?, M% B. w) ^4 Z" s
  56.                 ret = read(connfd, buf, sizeof(buf));4 i9 j9 G: L' i$ q
  57.                 if (0 > ret)
    3 ?* F& S8 o% d: j. Z# h! T
  58.                 {
    2 v/ j% C  j2 Y. w. u- N# u: x/ p
  59.                         perror("read");
    ! w5 c; l& R# a# S7 X6 R
  60.                         break;- Y0 C: x& G- q9 F2 A
  61.                 }//执行while循环读取数据,当
    * f! O' Z# C3 @6 w) n6 H
  62.                 else if (0 == ret)
    , }5 e9 _* Y/ M1 T+ @+ f# ^4 |
  63.                 {8 s/ u, Q* g% D. X# _: \6 c
  64.                         printf("write close!; P1 ~( C- C3 U' P4 l
  65. ");, ]4 ]: ]: D) h' Z9 B
  66.                         break;
    9 `2 J% l" l9 o6 y1 ?4 D
  67.                 }
    * v8 p1 w6 X. K6 v% V
  68.                 printf("recv: ");
    % M0 M" |3 T6 G, E" s* _3 d- P
  69.                 fputs(buf, stdout);//打印接收到的数据3 Q$ O4 a1 E2 c/ i/ k. K
  70.         }
    8 S9 g) R. C5 r6 a* t. t
  71.         close(sockfd);//关闭套接字/ V$ T4 f* ^2 p9 j; S8 R
  72.         close(connfd);//断开连接4 P+ R% o" P1 b( F" X
  73.         return 0;
    4 w( q( |9 T8 S2 |& B
  74. }
复制代码
' @0 b3 B. v3 K  ?2 {: [8 S1 {

# e% B/ h. E. u# q
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)% N. \+ y$ e' _  S0 g
  2. #include <stdio.h>
    # I# [- q1 n" R* h0 P; a  o  g
  3. #include <string.h>; ]  _/ S7 l* m" N& F; G: b
  4. #include <stdlib.h>
    6 V% [6 Y  g( P* X1 S9 r; c4 X
  5. #include <strings.h>
    & d( J8 n5 F6 u3 t( O8 N' f
  6. #include <sys/types.h># H: W( o7 e; Q9 C; v. l, r
  7. #include <sys/socket.h>1 M: O+ _6 q  N( `. I
  8. #include <netinet/in.h>
    9 x3 U/ D; j7 q$ ]
  9. #include <arpa/inet.h>
    ; d7 H+ n# ]( ]/ {4 ^
  10. int main()
    ( R/ V/ x$ v) `
  11. {
    1 B6 N4 M  @0 `! B% E
  12. int sockfd;  J+ L! }2 I) t+ x9 q
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    9 J  _( Y5 V& l, u8 f- H
  14.         {6 O' j3 p7 T, R+ {5 H5 u- Z# g
  15.                 perror("socket");
    . p- W. N% R* H1 {* |1 A0 D: H+ [: ^0 l& K
  16.                 return -1;
    6 I: e( I  v3 F$ D4 O8 v
  17.         }% m7 {: e& T0 ^# X
  18.         printf("socket...........
    9 b& U% e4 v: s2 ?+ F
  19. ");3 A+ J" m3 C6 }; A
  20.         
    , Y% Y/ t/ g) E4 ]( S
  21.         struct sockaddr_in srv_addr;' A  e9 ^7 Q# p* L- h! Q# i2 h
  22.         memset(&srv_addr, 0, sizeof(srv_addr));/ I, ?7 u( v1 A4 z, w
  23.         srv_addr.sin_family                 = AF_INET;
    , E( T4 d$ p- k: \0 Z. s( X% x
  24.         srv_addr.sin_port                         = htons(8888);' r4 d8 m! v2 i" I2 ~; z
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
      X, z7 E! D7 O- I
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))  ]' W: H* p/ H6 B
  27.         {" ^7 c2 |+ v; S' a/ I) y
  28.                 perror("connect");
    7 G( H% `$ D9 |& T' _- K2 s9 z3 U
  29.                 return -1; //exit //pthread_exit$ e, n& l- q+ z# x; N
  30.         }
    4 Z4 ?2 ~8 C1 r  p$ H
  31.         printf("connect..............
    5 r  x9 F! n6 p  `: a2 @
  32. ");
    ) Q! J5 B. A& y% e1 Z
  33.         char buf[100];+ \7 V4 C& O; a5 P
  34.         int ret;  r# A# t2 a7 M) b; F
  35.         while (1)( F9 n/ b( [* w
  36.         {
    2 A! \  A0 @! X
  37.                 printf("send: ");
      _8 m) c/ `. A7 J6 L, c* ^
  38.                 fgets(buf, sizeof(buf), stdin);
    + `4 ^& @/ h- h% M" m; N
  39.                 ret = write(sockfd, buf, sizeof(buf));
    2 T* L) ]* h; `/ m# A( S$ r
  40.                 if (ret < 0)
    0 W0 d4 w/ A* u, K. }$ s
  41.                 {
    ! T9 Z4 G$ A7 U  ^3 p- s
  42.                         perror("write");
    ! [4 D" a& x8 ~2 ^
  43.                         break;
    # g: \! K3 e9 G: z& X& R; d8 J
  44.                 }' g( ]; N" ~5 b7 ?0 Z) L- P
  45.                 if (strncmp(buf, "quit", 4) == 0)
    . b& N0 N% M7 I
  46.                         break;
    ; A7 f2 F! z  G( ~
  47.         }
    1 G4 X' s1 I: j8 l. F! \' N
  48.         close(sockfd);
    5 Z) i8 d$ l4 _
  49.         return 0;  ~+ ]: I# X; J5 Z% Q- Q8 Q
  50. }
复制代码
+ P5 }- @0 H" _! Z
# h+ P2 e5 c& d3 u0 T/ l' T; [
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 18:18 , Processed in 0.117845 second(s), 22 queries .

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