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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。/ m3 l: m8 e1 x, Z* G: L4 i: e4 h
6 q* I3 A/ m5 h: D' n9 B: i6 U

* j; v# m8 d  C$ _; ?socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
9 S" C' K, i1 b6 \6 Z, y
$ }- d4 A4 x2 K) m6 k7 \" c4 @% D5 s8 h

* s1 |* J9 b1 ?% rTCP协议3 Q2 Z) l' U7 W/ O) _
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。4 ?  j# i8 z; y6 M$ |: t$ C% Z

5 Y# K$ {- ^" x: p" M. i2 p
' x1 r; ^, |" s, l2 s: w+ ]$ B
关键词:三次握手,可靠,基于字节流。' V+ Z/ @, w6 J9 V* N  P0 E& C

8 I9 ~+ A+ j8 x' T

* ?. _' Z2 j! L6 I: I4 Z可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
3 a, H$ h& U2 o8 k* O
: u, y9 a) k( vTCP服务器端和客户端的运行流程
8 S% ^  h2 B0 U9 |: ^/ Y( u0 w5 ~1 e5 B$ [7 h: E2 k# C
0 v/ a' R& w% @1 N
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
3 F! @6 l/ E4 i1 N' Y$ k9 k' E2 e3 ~9 |  J8 s5 g/ v

! h, o; j/ S4 j( ^/ A: m9 L! x1.创建socket
, g1 x" O' v) k! n socket是一个结构体,被创建在内核中
2 f& p9 X* n8 r4 l0 _ sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ k2 }' z% |1 D8 }3 }# U! k& A9 D
- x' t# z8 H) e, s& d) V/ o0 c

# w; u( E. [+ s3 J" u2.调用bind函数$ ^! e) L2 T0 m# A; _' Z. T0 B3 ?
将socket和地址(包括ip、port)绑定。
" b6 V6 N, o! }9 W 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序6 y+ w. p6 s! X
struct sockaddr_in myaddr; //地址结构体. y- W+ ~. N5 a1 I6 G1 w
bind函数, r& N& D, x* O" a
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))( V% X; M0 v* M$ H- _

( R) X, c" O; E4 q% i

* A1 T: N; E# E' s0 D9 q3.listen监听,将接收到的客户端连接放入队列
' d# t4 }" o/ E: a; R2 q7 F- Z listen(sockfd,8) //第二个参数是队列长度) a4 f) c6 F8 F

% }! Z5 Q# ?) w/ H, h
1 M. e0 D4 r3 t7 Z" ^: P: O& V' u4 s
4.调用accept函数,从队列获取请求,返回socket描 述符
  Q6 f5 Y/ e+ ~" w1 X  如果无请求,将会阻塞,直到获得连接
3 \+ a: A+ z. b' a! K4 S5 h: C) j, g  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数8 ^- m6 X  J( a* P8 ?$ k0 k

8 |. M7 C7 B3 q5 F

! Q' S, _7 i9 K; `) ?" Y( V5.调用read/write进行双向通信
$ H) C& w: E3 b$ v5 i# s, A
) `7 r3 T  e( N

% i% M" w$ X% K& f, T2 K. w4 o6.关闭accept返回的socket
, G* q9 B# u! r! i  close(scokfd);/ c2 m9 I2 i( Z2 b- \
  s, {4 n$ {+ o; t7 e3 q9 C6 W
  m0 ]* C" u- |: F

5 i3 P! o1 J$ m7 @' c4 g* _
/ @9 A4 K$ a. L- K( ~5 B: G
下面放出完整代码
, U" z; S' p, A, y, g- ]( S# j* i+ c( E
  1. /*服务器*/
    % M; d) P4 \- ?! E2 t& D% S* ~
  2. #include <stdio.h>- T6 u: ^( m0 U0 q
  3. #include <string.h>
    . ~) M6 \  i9 T$ s% @; I
  4. #include <stdlib.h>
    0 ~  S7 f6 j5 m4 [# Q0 f
  5. #include <strings.h>
    5 \" F& g. m8 Y* w
  6. #include <sys/types.h>
    3 y( @% Y$ h2 n( T2 v  Q
  7. #include <sys/socket.h>
    $ h8 A6 @) W( {0 ]: t! m
  8. #include <arpa/inet.h>3 |) f) t& d) `4 B; [
  9. #include <netinet/in.h># V9 k1 r- A5 P( U
  10. int main()
    ) b5 C. N" Y4 w1 v$ }7 W; X* t
  11. {
    , P0 F& B% A' l0 w$ m$ w( ^
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字5 N: F9 k" b6 s2 Q
  13.         if (sockfd < 0)
    ! p3 F2 C5 z  `  A/ v6 b! q4 k
  14.         {
    * @3 P7 G1 Q- o" R- @3 c
  15.                 perror("socket");
    * g2 D, f  J. O* y& w  M
  16.                 return -1;
    4 C5 s8 m; A6 T# L% s
  17.         } //创建失败的错误处理
      v* A5 S2 I: P' |6 C1 L2 b
  18.          printf("socket..............& U( K3 W9 X- {" N
  19. "); //成功则打印“socket。。。。”) r8 ^. E  x6 j, F
  20.          
    + C# f2 b0 y/ y5 ^+ V0 W6 z! v
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    3 U  ]1 z) ]* `# I/ i
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    + m! L  {5 ]2 ~+ x" m0 \: k
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    - a$ C! ]2 B4 Y6 n: @
  24.          myaddr.sin_port                 = htons(8888); //选择端口号+ w% u5 \: b5 L' V+ G  X3 e% [
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    / ], B* c/ f5 y

  26. * G1 ~, L/ t" I6 A# e' u8 G$ V! x
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 ^- N& x9 |1 g7 z/ u  Q
  28.          {
    $ ]8 ]8 O( j- _- R; A/ g+ X9 U
  29.                  perror("bind");
    + k9 d* T7 f! C& }& t8 @" o3 o
  30.                  return -1;
    1 P; M9 o( h: `! L
  31.          }
    5 X3 x& F$ {6 C
  32.          printf("bind..........
    7 V3 n" _; M+ T3 {8 i, j; {
  33. ");7 d' q! Q- m% J$ I" R0 G4 u3 c
  34. 3 Q% [+ X2 y' {; v# t
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- q0 N2 H, J( n+ P" m: i0 E/ a
  36.          {1 D" a$ M8 M4 v9 u, V. n
  37.                  perror("listen");
      A6 @" t  ~2 c
  38.                  return -1;" }) ^; u; f& y+ f9 D
  39.          }7 J- G7 L1 O5 A0 u: {
  40.          printf("listen............" ]( Z7 y/ k4 D2 H
  41. ");
      p( Z/ e/ `; x0 C" M# J6 D
  42.          
    ' ]% z. p: y2 |$ B# J+ Z, ?0 v
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求- r7 `/ H- {( a/ G) K
  44.         if (connfd < 0)) L5 Z# o' ]; ~" p- G3 E
  45.         {
    $ P) _! [# Y7 A7 W" |2 z. B
  46.                 perror("accept");9 r: Y( P/ S1 u3 i
  47.                 return -1;
    % j7 M1 ^" K9 t, ?
  48.         }
    ; Z0 c  x& l1 s0 U! E+ r8 K
  49.         printf("accept..............5 N. W! J+ E: F; K! }/ {
  50. ");& D+ t: N) a/ A2 z
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    5 ~& J3 E2 z# S
  52.         int ret;  |' I* C* c) }6 W
  53.         while (1)
    ( R# d5 _- g) Z$ N# n
  54.         {
    ( R+ W# X3 ^5 Y) s; G
  55.                 memset(buf, 0, sizeof(buf));
    ) C( B" K- x$ L, B4 _& J! \
  56.                 ret = read(connfd, buf, sizeof(buf));
    8 e- I: ^) H' l
  57.                 if (0 > ret)# ?( \% G: |2 p- c9 a
  58.                 {" E) ^" s/ o9 v: W- U. u' ^. x
  59.                         perror("read");1 P9 c% q% P) s
  60.                         break;
    " v: ^3 K0 _$ u* S
  61.                 }//执行while循环读取数据,当7 R2 }2 t3 [2 A  E+ }4 F1 x/ r
  62.                 else if (0 == ret)1 q, s! M+ i" K& a6 H) H
  63.                 {
    ! D0 u6 }; c9 M5 u# i
  64.                         printf("write close!
    % P% i, T- A, F1 J
  65. ");" X, R# s0 v5 G& s- _3 Y
  66.                         break;
    $ a! I8 i1 b2 v. F) [
  67.                 }$ }- J$ R( L( }2 D. v
  68.                 printf("recv: ");  G  S3 N; z/ Z1 p: ^0 H. z" V
  69.                 fputs(buf, stdout);//打印接收到的数据
    7 c0 e0 M: V' {/ W3 l  d: ?3 ^$ ^
  70.         }: k4 C% @5 S- S6 m! y3 n6 g
  71.         close(sockfd);//关闭套接字
    " B! y- a: _9 V  t1 ?
  72.         close(connfd);//断开连接
    ! t! |  z' [. Z* u/ Y; ~  f
  73.         return 0;* Y, N9 q' j% d4 M+ `4 Z) i
  74. }
复制代码

- z2 u9 q( ~% f% K- L5 f* t" q. f+ f) }! _+ N' S  W6 h3 G
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    % @! ^: @3 W: k1 }; v% V* u" D$ j
  2. #include <stdio.h>5 a- Q- z( s# U" a
  3. #include <string.h>$ @2 N" |6 c) g5 Q# g- v
  4. #include <stdlib.h>$ R- O  k6 T. f
  5. #include <strings.h>
    " X' d2 \) B% i8 D1 z
  6. #include <sys/types.h>
    + g2 H1 a$ I% L9 K) B8 B
  7. #include <sys/socket.h>$ m9 b2 y, A4 X* h+ }3 f
  8. #include <netinet/in.h>) f2 o" N/ y- q5 f
  9. #include <arpa/inet.h>! ^7 u& N/ b6 g8 c2 E' o. v; d1 s
  10. int main()
    . B4 P( A, K. L9 i3 r: n
  11. {
    . Q* t" @1 i* _* P6 V8 s
  12. int sockfd;
    * Q: g7 T$ X& `. u, G
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))), B# ^9 }. d  Q6 p% V  @3 ]
  14.         {) ^1 \! G+ r) n" A; U
  15.                 perror("socket");/ O# b# t( w4 K
  16.                 return -1;, I2 ~. B& j: [) z
  17.         }/ n+ ]! }. m2 g2 F
  18.         printf("socket...........( n, ~9 H' V' K. e9 g& w* g) @
  19. ");
    " B+ f3 O4 j* y/ F8 }
  20.           \7 D, \* F5 ~5 s& U% q  u9 ^
  21.         struct sockaddr_in srv_addr;# `8 N# b$ I% \7 t9 e1 l- {
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    % Q. c% {3 R  _* S6 a+ z
  23.         srv_addr.sin_family                 = AF_INET;
    9 u/ J* c# W* i2 M' _' s
  24.         srv_addr.sin_port                         = htons(8888);
    5 b. Z) o+ f  s2 o
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");5 d, p; ?# d: x7 M1 s0 q
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))+ F2 g$ B% H4 T5 z5 N/ W; ]
  27.         {
    1 ]! `% H5 F5 G8 C; ^% s" @! D6 I
  28.                 perror("connect");
    ' c  v9 X9 x+ B
  29.                 return -1; //exit //pthread_exit* y, n: M/ Q- ]4 a2 t8 p& P
  30.         }7 {$ M5 ]" O, a/ O
  31.         printf("connect..............
    # c! V* m) d$ w9 p: l
  32. ");
    . ~+ J+ J& E0 U1 T
  33.         char buf[100];
    $ e; O8 P- p8 _% w& E* u7 A- x
  34.         int ret;4 f( m5 A- r3 F
  35.         while (1)
    0 J7 B& d: ?3 _2 Y7 e: d
  36.         {
    8 H/ ?" f  k" o& c9 q0 X( |4 k
  37.                 printf("send: ");: {5 R- o. \, N
  38.                 fgets(buf, sizeof(buf), stdin);; m+ i. C/ m& \) l
  39.                 ret = write(sockfd, buf, sizeof(buf));
    0 T6 S8 H) |8 Y8 p' Z8 Y
  40.                 if (ret < 0)
    2 S( y6 h' d" W0 F  G5 \# E% I# @
  41.                 {
    2 g% ?5 C2 L# C
  42.                         perror("write");
    8 G& z3 h# u& g7 |# E  R; R
  43.                         break;! i; K1 w) W% B" D
  44.                 }
    $ e( J: g$ M! o* ]
  45.                 if (strncmp(buf, "quit", 4) == 0)
    7 |- S- _! B( y
  46.                         break;2 }  z/ @- A" ^* u2 H2 ]5 d& d
  47.         }
      U& y% O0 w1 o, b3 Y8 \& F5 y
  48.         close(sockfd);
    9 d, W; M  k' z- M# i
  49.         return 0;& c8 k9 t( ^5 M; w
  50. }
复制代码
$ ^# i9 C  T9 M  f9 v2 M
- q$ |8 I4 q5 u* ]$ d/ M
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 10:49 , Processed in 0.122682 second(s), 22 queries .

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