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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

* B9 t$ Z1 I' m# q' d9 J1 lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。8 ?2 l. `2 K& S" \( `

  y% p8 F# D1 z  X  E
: X, Z+ q8 Z3 {: u5 ^& Y0 P/ n! t
TCP协议, h: W+ K* n0 v) K1 {5 @
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。2 V& r4 S- r( {0 c' Q- u: Z

) M1 Y" i+ I7 a! O$ S
4 T! C/ I3 E9 `5 W' [
关键词:三次握手,可靠,基于字节流。
  b/ s/ }* U4 @9 }) [- i7 a' }1 L" f' ~3 u
3 e# F' m0 q7 E0 d  O% f
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。' h+ V( M7 |( N% t4 W
2 j* M7 H7 E( j* d
TCP服务器端和客户端的运行流程
+ C1 p3 u" _$ Y4 t0 X9 K7 s8 L  W& A$ u5 y) q
! ?2 _& c  H- v, C
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
- I/ U* a+ ]8 u& R; ~, ^& O/ t& s8 @+ I- z- D/ K

! P  ]" F5 \, t. E" I- f7 B1.创建socket
3 [, d: L9 U2 Q0 \. V. Z: J8 {7 e) E6 { socket是一个结构体,被创建在内核中
$ ?4 R: f+ w/ }9 D sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议. j, }3 z0 p) ?

+ J% g' }/ D  z; M0 y# p3 [

. D5 Q2 X2 d3 d2.调用bind函数
- q& \# n1 p7 f! t 将socket和地址(包括ip、port)绑定。
( {' F2 w4 N6 r- d2 c1 A  P" }; A 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
: f2 ?0 h! ?9 b, @0 s  T0 [+ x struct sockaddr_in myaddr; //地址结构体% r+ k2 S4 B+ d% |4 D
bind函数3 U( W% [5 e# f# x
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
, e  d7 U: {$ G& ?0 A3 k: ^6 J0 H) H3 X/ \1 R) p

7 m0 p! v! @0 H4 Q9 G5 g3.listen监听,将接收到的客户端连接放入队列; K; t% V' h$ S* l$ Y6 h& J
listen(sockfd,8) //第二个参数是队列长度
( Y3 g% ]$ F+ `. ~) n4 c& u/ d$ d+ ^; k3 K/ z. i' x3 P5 K7 {2 f
: m  I3 b4 B$ }: b0 C: {4 D
4.调用accept函数,从队列获取请求,返回socket描 述符7 e, `( p6 w  L  o6 T, ^
  如果无请求,将会阻塞,直到获得连接
4 [9 G4 ?& Y' k  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
3 U* d% j& L% r  B
+ K8 b6 J- f. s* n1 D7 Y6 d
: Q$ Y1 v# b3 ~
5.调用read/write进行双向通信
. x3 D  A4 y: w$ d( Q
. [8 Q( k+ m; c: S

8 k" C! c; m1 q, i$ u6.关闭accept返回的socket  O$ g& u8 C& P, Z- U- a: j
  close(scokfd);: S8 p, Y0 ]+ B. i3 `

1 {7 z% t- r; P5 B. Z9 u
2 Y9 J, O; l4 L0 x3 X
9 W7 H5 G1 u% J: k8 d

5 }0 m8 N: c. N) n下面放出完整代码. r7 e; m/ c1 @2 I
) w8 p4 h; Q' T, c  `( [
  1. /*服务器*/1 _0 |) f8 ?- t# }$ ?
  2. #include <stdio.h>
    1 c4 _5 i6 y9 m
  3. #include <string.h>+ i6 ~& Q0 M2 K; k4 s
  4. #include <stdlib.h>
    2 m" d' }2 R9 C, c  p7 S
  5. #include <strings.h>7 e9 p; |- ?9 C" t4 o
  6. #include <sys/types.h># Q8 q+ E: H, z
  7. #include <sys/socket.h>
    1 ?# X$ F8 l' i6 }" h( Y
  8. #include <arpa/inet.h>. n" i  g' i! s- L
  9. #include <netinet/in.h>
    2 I3 M0 N% l" J1 P
  10. int main()' `+ l% S. t8 S
  11. {
    6 K+ P3 f$ `( G1 c
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    6 C* A: c* [  i4 }+ m7 D. N
  13.         if (sockfd < 0)
    , K4 {5 k3 z6 D+ s
  14.         {+ v- g& F  I! O* S* A. z3 V
  15.                 perror("socket");
    : w+ D, _) _- B; E& E
  16.                 return -1;
    7 t! j" C; J$ V+ L0 P' i
  17.         } //创建失败的错误处理
    4 m$ P) I- c1 x4 V/ f
  18.          printf("socket..............
    + F; f: i: _" ~& ]9 {5 l/ n
  19. "); //成功则打印“socket。。。。”
    , s0 N' ?4 e. q1 Q/ ]3 w
  20.          
    1 T! M- K6 o* S+ u+ O9 c( G# s7 k
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体" o; ~$ G* [3 W3 C3 F
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)3 U9 V9 W0 a0 k  V0 \3 m
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    " r- `  B) \3 k8 w+ I% c; g" Z
  24.          myaddr.sin_port                 = htons(8888); //选择端口号5 Y/ V% @. G' B& M/ q
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    4 w8 d/ r- K6 B8 t7 E$ c/ h

  26. ; V: t. T( O5 q
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    - c& ~5 O7 ?' }
  28.          {
    " s3 n: {& g* @6 ?$ {% ?4 N
  29.                  perror("bind");
    ! m) }( s5 a& N' ~
  30.                  return -1;
    1 ~1 u" G2 ?/ f6 V
  31.          }& A3 F8 A; N: T9 Z6 o$ B
  32.          printf("bind..........4 i9 A. w! W1 s' K3 [! {$ t# D- f- d
  33. ");3 `% S, l8 ^9 E3 T
  34. , X2 L2 `9 _. Y* s% g% S+ k* V" R
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 R8 U& s  s4 f; ~3 l9 z
  36.          {/ l& o8 s$ v4 `8 M/ E& A9 l
  37.                  perror("listen");
    9 q6 }4 V1 L6 P5 }! `
  38.                  return -1;
    ' q6 r' W& G$ l& ?" M& Y) \: A
  39.          }
    ! `. n" K( _% k$ N
  40.          printf("listen............8 e1 S$ ]; a& k0 w* C0 M7 A; `4 w
  41. ");5 d6 C% X( j, w8 a; S9 Y: i) F0 Z
  42.          
    2 T. ?. {- R$ d+ b3 {; V) ]4 p
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求6 B7 H# H. X" z7 p
  44.         if (connfd < 0)4 y. ?2 d6 X- v+ h0 `
  45.         {/ S2 t' D% _( [
  46.                 perror("accept");/ a0 f8 a0 g2 h) Y) ^1 E9 F- G
  47.                 return -1;
    2 d/ Z3 x& i/ o# {0 z2 n0 C) V0 k3 N' G
  48.         }. y- O; c# q5 a2 F
  49.         printf("accept..............4 J- P, }8 O6 b2 A% c
  50. ");1 W( ]) L7 R# v$ e% k. O0 ~
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    3 e& [7 e8 P7 q6 f% |
  52.         int ret;
    7 _% z* Z* O" d5 v" G  }% r9 w- A1 ~1 x
  53.         while (1)
    / U; j# x# }8 v
  54.         {
    6 p' i8 l9 n8 U* t
  55.                 memset(buf, 0, sizeof(buf));; D& |/ {  s6 R/ ]  }! V
  56.                 ret = read(connfd, buf, sizeof(buf));; i7 Y! d. |5 N1 j2 Z; w
  57.                 if (0 > ret)
    & H. u' b0 M' i5 n( f  N5 Z0 k/ a
  58.                 {
    ( N) f5 Q' b9 K, s9 _+ n+ [& o
  59.                         perror("read");
    " d# W! m, ?% L" J; c3 G8 Z5 t
  60.                         break;; f. B2 t# G4 N4 \+ B) v1 u' r
  61.                 }//执行while循环读取数据,当# B7 |" x1 n9 ]
  62.                 else if (0 == ret)
    7 P" f8 p% c3 t0 P( q
  63.                 {
    $ [3 m$ ?% t9 l& g( Q& C8 K: |
  64.                         printf("write close!! b7 k3 q  L. [% [# D! t# H& ^  \0 l
  65. ");
    ; Y5 Q# h5 b9 l7 J4 d5 u3 `* d
  66.                         break;) Q$ |1 J  O5 g/ V. k4 `
  67.                 }
    $ G1 l7 W, v0 g/ X* i0 m9 v
  68.                 printf("recv: ");  U, {* n& L; _2 n* t- C3 p# R
  69.                 fputs(buf, stdout);//打印接收到的数据
    ! X% d5 n7 T! d8 q: ?" a# g
  70.         }6 Z  l# V" u7 l% A4 N
  71.         close(sockfd);//关闭套接字
    / W7 K. P2 c0 U! j$ v. R( P4 ~
  72.         close(connfd);//断开连接2 {7 W( E9 ~0 `% H  o% v, ^9 t7 o4 l$ `
  73.         return 0;
    % @3 G7 U) a9 K. w4 V
  74. }
复制代码
% e0 c% }9 H2 k* @/ ]
# F' p; ]0 B! C' A! {4 {" @$ O
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    ' b% E* v$ v+ U4 T& K) W6 {
  2. #include <stdio.h>4 E% d* T* x. @9 p, [
  3. #include <string.h>+ e/ l& B& |+ ]& [7 s! c0 z
  4. #include <stdlib.h>
    7 G: [6 k0 _4 ?" s. K1 s# `5 J
  5. #include <strings.h>
    % K/ m+ U, g$ J" p% ^4 O0 j
  6. #include <sys/types.h>  a+ `# I6 p+ I. y
  7. #include <sys/socket.h>; R9 g6 \) W' R- f3 e$ b
  8. #include <netinet/in.h>$ s) f, F3 C3 u# d2 x
  9. #include <arpa/inet.h>& g8 S* [2 y9 Z/ b/ E' ^9 K2 @
  10. int main()
    5 _) v9 }" f3 _# H' W$ g! R
  11. {
    . F+ D4 Y! K: [+ Y1 I+ X
  12. int sockfd;2 F9 x  S0 O/ |' \1 Q( {. W& q' m
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))  M9 {9 |3 c) S7 A9 O) y: Q" i$ n
  14.         {0 u3 I% V- _3 S1 s! P1 T6 j( n
  15.                 perror("socket");& q1 j# g& ]  k3 l2 D# s! o, t
  16.                 return -1;6 E5 k# s6 M$ f& s6 B% z7 S
  17.         }2 g, H4 x) s: P4 M, w  u8 W
  18.         printf("socket...........
    . F. Y) c& b. m' W, C
  19. ");
    " T2 l$ o5 u5 h8 V  `4 A0 F
  20.         1 b( b/ N8 Q$ ^( s" p
  21.         struct sockaddr_in srv_addr;
    ) o5 J4 `2 w3 q! y& s+ P3 ^; u
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    9 L2 n" t2 O9 T* N8 u8 W
  23.         srv_addr.sin_family                 = AF_INET;/ {* v! n) \7 v1 |* c1 H9 r
  24.         srv_addr.sin_port                         = htons(8888);
    0 u5 ~, ^9 m/ j2 R5 S& Z7 O4 A
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    * H% y$ m& P2 Y7 a/ Q! U1 O
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    $ b8 K' k% v# S; p& b0 [. ^' Y6 o" b
  27.         {* J" b; W/ {/ D$ U; c0 D
  28.                 perror("connect");6 _& C3 f2 a; E8 \! |% L
  29.                 return -1; //exit //pthread_exit
    : g! d' {- E% R7 n4 `+ ^; A
  30.         }
    $ [- q( y- n5 Q) u
  31.         printf("connect..............
    : c- c2 u7 I2 ?
  32. ");4 Z  }$ g6 ?8 R# g9 e2 V
  33.         char buf[100];
    $ w8 t! T5 _, e1 G6 z
  34.         int ret;
    9 G, C# l8 {' t' z6 Y' [6 q0 q
  35.         while (1)
    ) u. S2 Y! S" J4 M. K5 V7 ~
  36.         {
    8 L. k4 N* V0 C/ U6 V, B" S
  37.                 printf("send: ");" @2 k  O. G9 q" ~; C
  38.                 fgets(buf, sizeof(buf), stdin);* V7 B! l" ^; g& r& p/ \0 M6 C
  39.                 ret = write(sockfd, buf, sizeof(buf));! b+ C+ \* b) C3 l7 P
  40.                 if (ret < 0)1 Y' o% Y  X. E. \& _
  41.                 {6 ~9 C. S/ q  E1 d1 [
  42.                         perror("write");( A* l+ z) t! [6 F; Y
  43.                         break;
    5 [# D) H( y0 P) O! F
  44.                 }0 F" h7 b3 S3 ?% a9 S6 D
  45.                 if (strncmp(buf, "quit", 4) == 0)
    ! s. s6 b: J$ `- A
  46.                         break;( v; ~" u$ w0 u* R  j
  47.         }
    5 }* Z7 i8 y. Y7 c
  48.         close(sockfd);
    0 q1 z" D5 |& H5 ^: P
  49.         return 0;
    1 l( H1 d3 z5 g4 A, }8 x
  50. }
复制代码

- H: d# ?$ `8 r6 E# z* P  ^
# c3 A9 q! A/ b7 Y! J! O8 ^, ]
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-4-27 06:46 , Processed in 0.128038 second(s), 24 queries .

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