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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

, l8 G" G; k3 S8 m. z

% ]3 H" u! [# M& G% V" T6 U! U9 rsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。# L" d! K' j! Q. R) w2 ?
4 S! W9 q0 c0 Z" S
( d( j; c+ ^8 S  ~4 r$ M
TCP协议& [' U4 @* A- ?
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- ]9 B/ e3 T% k  a8 M
# y  w- o0 P, A3 x) j
5 j5 C- O0 ~) U! Q! L5 S. Q) U
关键词:三次握手,可靠,基于字节流。# L1 N! u8 j6 H3 T' W; e& I. w

) `: G* J- |# w; o* H) |

6 i6 V& t% b' g, K  V' S# A" ]可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。0 }$ u0 s: A( J/ A8 e: x
$ ?7 c6 V5 Y; G  S$ l0 B
TCP服务器端和客户端的运行流程) D$ X5 ?1 R% B5 u* T4 k

1 l4 R+ z6 P2 x9 x9 Y6 C2 q

! y$ j, |: Q' C, t& ?; Y( k如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
$ _1 @, L8 z4 _) O6 s8 C
2 h1 y3 ]. W+ `0 v& G7 J  l3 D/ f/ Z

3 `' }8 s  {' U4 U" ^1.创建socket% C! G$ w. r9 x! Z5 g
socket是一个结构体,被创建在内核中+ c2 V+ |5 s* g% ^# S3 ^
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
8 ]0 c" ^- d, ]- W8 F
. M0 y7 V$ U2 J6 _

( Y- M% N1 k  P/ K& V- s7 x! [4 U' w2.调用bind函数: S3 y) ^( n) g5 m+ \
将socket和地址(包括ip、port)绑定。
/ v  q$ K5 d- H0 t. u$ G 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序% m) s7 b, u( Q
struct sockaddr_in myaddr; //地址结构体
* G0 x& T: J4 G bind函数
, w- W$ Q, W# H2 J$ N bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))" e3 w+ z& ^: p. X% }8 Q
' Q2 ^6 w! H( P7 r  p# x
; V( c* h: g3 W9 x2 o
3.listen监听,将接收到的客户端连接放入队列& v+ q6 B, J7 {5 f& ?6 v) f
listen(sockfd,8) //第二个参数是队列长度
; q, L+ D5 U" J+ k9 Z2 |7 }" o4 ~* b2 _

. l0 B: C3 x1 T4.调用accept函数,从队列获取请求,返回socket描 述符0 l# `2 A( c3 k
  如果无请求,将会阻塞,直到获得连接( K+ v  L5 g% i/ _( }
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数  j: l7 i! U. q

3 O5 d+ R/ P5 g; ?& y4 q
! X6 P6 T4 H* D3 {- D
5.调用read/write进行双向通信/ L2 P! [) y/ |" ?" x

& y7 ]& l7 {- D$ s' }% S4 p

: Y% D+ j) ~; F' n% A6 k( s) o6.关闭accept返回的socket( H1 k1 n" c7 ^2 x0 U
  close(scokfd);
% e( p& b- C, u1 U- D7 N5 K' Q3 {
. A" o# X/ t7 f  R& |
+ Q3 l2 K. z( m" j2 F

0 C3 M, S1 H. O! U7 x' J
7 D, f' f+ R2 x, |
下面放出完整代码
# t3 T- c0 h* |* W: V
+ m9 P* b) a9 U9 ?" K
  1. /*服务器*/' F: S0 Z; y, {% H/ ]+ U0 B
  2. #include <stdio.h>
    # t$ \& f/ ~, T- N
  3. #include <string.h>
    6 X5 [+ m* E) v% q, j  {- C# L
  4. #include <stdlib.h>% F- p6 Y2 l8 Y; v
  5. #include <strings.h>
    . m0 y& R2 f; r/ o8 W
  6. #include <sys/types.h>
    " f, E& K6 ?; `
  7. #include <sys/socket.h>
    ! A1 ~5 U# p6 _
  8. #include <arpa/inet.h>+ B* Y* \0 D9 E( v% j, _8 }/ P
  9. #include <netinet/in.h>
    & a6 Q$ e- p$ u3 l6 d
  10. int main()2 e) ], d2 r4 ^: S( B3 K
  11. {1 X. C6 e6 v5 D' t
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字  ]! @: y3 p4 P% x
  13.         if (sockfd < 0)
    ( H" Z6 e% Y' b4 [
  14.         {
    2 ^. U0 a; _* ^  j- j
  15.                 perror("socket");
    ( R- @& A5 J* p; [
  16.                 return -1;9 ]/ j% x% ?8 Y: W% |  ?. b0 W
  17.         } //创建失败的错误处理% J0 \5 Z7 Y1 g& X3 }
  18.          printf("socket..............
    , }$ X' ?8 w7 {8 m# s
  19. "); //成功则打印“socket。。。。”. n5 T  l! V6 e. P/ G  n8 G! _8 v7 _) b
  20.          
    1 f% L: I9 N$ C! v3 V) J' C4 p
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体/ O2 K, |9 f/ z& ^5 @* \  ?' a
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)7 Q; v# I% k4 ^- i# z7 a2 ?8 N# n1 c
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    3 c! r" y  L9 G& e% C, a
  24.          myaddr.sin_port                 = htons(8888); //选择端口号& s, l; I+ _2 W1 V! {9 W( A
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址/ g6 S' g0 r- ~

  26. , X6 j* G+ v8 F/ b& E( ?
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字, X& u/ G9 [% l
  28.          {6 P% e( Z. T/ o/ ]
  29.                  perror("bind");) D3 b/ Z$ k! \7 D
  30.                  return -1;  v* f1 ?5 Z: k
  31.          }
    8 r6 b6 f2 v! i4 F. V4 E  A
  32.          printf("bind..........) g# {0 ~! q0 Y5 Q- l* l
  33. ");6 Y1 u0 H" t. y) l
  34. * V! A; Z. f2 E4 r7 b
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听: M2 H. a* b0 \" G
  36.          {+ T$ E, e2 u- c/ Q; Y! E, Z
  37.                  perror("listen");$ |) U) [7 |4 @# l" z2 x7 k
  38.                  return -1;
    - k# Z3 e4 Z- t4 u; W. ?( O
  39.          }5 o& \0 |! J% l* i5 {8 c
  40.          printf("listen............
    2 }/ `; f. o7 b: C+ G7 E$ l
  41. ");7 v  ~3 Q. a3 Z8 f& E; z
  42.          $ A5 h( q$ f5 V2 A( f
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    % D, A8 V5 Q8 l' ~$ f; x
  44.         if (connfd < 0)3 H# p2 P8 p+ i
  45.         {9 N/ j" D! B' f7 S
  46.                 perror("accept");3 a/ v7 C7 R6 F* e, u
  47.                 return -1;1 @! F: o& L# b* I
  48.         }
    . I% Q2 z8 q( i" f' A1 z+ ?
  49.         printf("accept..............
    + _1 X  Q! ~% \# u( H8 v
  50. ");& f$ P. a. E8 i0 q$ E* X- R
  51.         char buf[100];//定义一个数组用来存储接收到的数据: @% H7 n0 a& m3 l
  52.         int ret;
    ! D! }, e6 N6 l6 j9 ^
  53.         while (1). R. u' t6 C0 H5 i
  54.         {
    1 }3 f7 ~- m, q* s& B" ^
  55.                 memset(buf, 0, sizeof(buf));
    & L1 H: k, E+ b7 h+ S- I
  56.                 ret = read(connfd, buf, sizeof(buf));" X( t; u# {/ i' e* M% T
  57.                 if (0 > ret)
    & R: i. e- y- M- w: R
  58.                 {/ Z  r, F! X$ ^6 ?! s3 M9 Y
  59.                         perror("read");
    . L8 |2 Z, q% q" o( y
  60.                         break;
    3 k) V  `" \  Z4 c1 l5 l
  61.                 }//执行while循环读取数据,当/ V! i5 d% ^9 u, O* M0 x
  62.                 else if (0 == ret)* ]% C1 p; L' L' y  G
  63.                 {
    $ g6 k# A! v9 ]
  64.                         printf("write close!
    ' [/ l9 t0 V5 {5 I. s3 r) B  ?$ R: P
  65. ");) o8 g& ]: F  v) o- x* `5 o
  66.                         break;$ e& r6 O$ k$ V- n3 }$ s
  67.                 }- r2 K) [; G& O4 m7 d' O3 B# A4 |
  68.                 printf("recv: ");0 n1 `$ V/ `$ {( c/ M
  69.                 fputs(buf, stdout);//打印接收到的数据
    * P3 }  b. ~! P8 D% E" B
  70.         }
    & ?/ q6 [: P- f. S) b
  71.         close(sockfd);//关闭套接字2 w* T1 j2 A& P/ P6 P5 s
  72.         close(connfd);//断开连接
    $ H% `; a9 G0 [. a
  73.         return 0;
    1 @5 r* ^1 x4 j7 w: m) T
  74. }
复制代码
5 Q+ i1 W7 e1 i* b% {/ Z

# u/ |3 K8 S0 w2 j, ~! `
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    - t! _* J0 Y5 \3 G" w2 f' I0 p5 i
  2. #include <stdio.h>+ b( m# o' w0 b  o* Q( I
  3. #include <string.h>
    / V1 I/ Q) u7 q" O% b
  4. #include <stdlib.h>
    $ k+ u" u8 X% y" q  q1 C/ J) l* X
  5. #include <strings.h>5 C$ [4 V: y. S, f
  6. #include <sys/types.h>) D5 H2 B2 n9 S
  7. #include <sys/socket.h>
    & \9 c4 L+ v# M+ e
  8. #include <netinet/in.h>
    3 b( |3 T) d+ q$ o+ S: D& F  p
  9. #include <arpa/inet.h>7 f* f# P& |* q. g
  10. int main()
    6 _9 J5 [' Y! t# R9 ~. N
  11. {
    , w& p, o$ L5 K6 g% X
  12. int sockfd;$ K" ?' k1 o4 I' p1 ~/ m  [- F- _) V+ o* j
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    4 R- E8 p. X) @1 C( C4 b5 ]+ P- ^5 Q
  14.         {+ Q  |& z3 r' ?
  15.                 perror("socket");
    ) A6 l2 p4 j" c2 a/ B- y7 f
  16.                 return -1;% I/ q  u& V9 d  I% p* h/ z6 e/ h
  17.         }
    + Q/ h( ]! }5 F- d9 r. r
  18.         printf("socket...........
    & O+ h* U% O+ w8 {/ r
  19. ");9 G9 a* P+ t, L2 J5 B
  20.         " J6 ]! @$ U) @& h6 Y4 t
  21.         struct sockaddr_in srv_addr;
    - C+ e# a; N0 c( f
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    8 _0 B; ?2 `1 W# B5 l% H
  23.         srv_addr.sin_family                 = AF_INET;+ S+ _" d0 D3 Y  n0 V
  24.         srv_addr.sin_port                         = htons(8888);! D1 A2 p2 @1 v9 s& @. g9 H
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");; b, n* C2 [# S' Q
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    . C0 g5 m5 X0 P/ D
  27.         {' i" L7 f4 h# s/ V& {. V
  28.                 perror("connect");4 |& d3 ]  \0 x
  29.                 return -1; //exit //pthread_exit
    ; n" }  W# K& \. H& G; u
  30.         }
    / l! D4 }- u. g# c" [5 ~
  31.         printf("connect..............) d$ h. `& G' `
  32. ");  J) `# v9 }. W! Q$ R) t
  33.         char buf[100];
    7 C2 B' y. H( o  d3 s
  34.         int ret;
    % v6 s+ c( Y% B( C
  35.         while (1)
    + K1 r. ]6 x& N% q
  36.         {, w" v" e% z( @/ X& L: v
  37.                 printf("send: ");2 g/ T  f& M$ Y' u. O) v9 {
  38.                 fgets(buf, sizeof(buf), stdin);4 J, n) n% F' _& [( X
  39.                 ret = write(sockfd, buf, sizeof(buf));& H3 Y6 j# W+ A( |
  40.                 if (ret < 0)" Y( @% c; A" R% P5 Z# _/ G
  41.                 {3 x3 C# ]5 |. J( |6 s
  42.                         perror("write");
    ' E$ X3 _! q6 C9 Y/ }, n! O
  43.                         break;4 L+ Z& M: L3 T% q: H  r
  44.                 }
    ' Y, h3 w! f7 P* q5 V$ u
  45.                 if (strncmp(buf, "quit", 4) == 0)
    4 P/ `* |, b- ?! V9 N
  46.                         break;
    # O: e6 P! D9 n( `( }
  47.         }  n8 ^% \: X% E" m8 H0 w$ Q
  48.         close(sockfd);
    % d& `$ p5 p4 f" l7 o
  49.         return 0;
    & I; |; y4 Q' k& Z/ ^8 p) n' Q1 s
  50. }
复制代码

4 d2 @) T; i1 y& X4 k3 R6 x& O0 {0 q& i8 ~) Q/ ~( y
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 05:56 , Processed in 0.150197 second(s), 22 queries .

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