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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 12232|回复: 0

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

[复制链接]
发表于 2020-5-9 02:09:24 | 显示全部楼层 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。# x* `2 u: ^6 U

3 }4 j4 v3 }, W3 M( b  B

* M+ S. g7 \9 K6 ssocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
1 Z6 [8 b8 x/ E% G7 v" f5 m, _. k0 M8 ^1 Q' |0 x

- P$ j" _; \6 R8 K* XTCP协议3 N! S9 J1 w" S2 N1 Y3 X
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。! W9 E/ G; q, N  E2 T  O

+ f4 P8 ]6 q# D/ J
8 I% i0 L' \$ v* A" `2 D
关键词:三次握手,可靠,基于字节流。0 y8 y% c5 A+ P" W' V
$ J1 |: e# w9 Z0 Q  s

) o; Q$ u5 [+ `可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。* ^) u* i$ w1 _$ W
微信截图_20200509015654.png   F! ?' h2 B3 N' k+ q4 l
TCP服务器端和客户端的运行流程
' O1 i; ?4 i( @9 x
! y/ L- v1 i1 \% U; E$ `1 Q
  R' b6 \7 ?9 B8 Z7 g. y
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
1 d* Y" r. G! `! K7 I* {- q& R& B" q' s

7 I, U0 E: B3 L) p% `7 ?! a$ f1.创建socket6 f: z; @0 k  Q# N9 L
socket是一个结构体,被创建在内核中
8 s! |0 W5 O  S  [$ T& S$ ?/ V sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议( i- G$ G+ ~( f
( G; A. N$ a& ?; u

9 Y4 h. C6 T3 g2.调用bind函数! M0 t, Y0 t8 T
将socket和地址(包括ip、port)绑定。
& M, d( ?. U. H; q: L 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
) c3 x% D1 s% i struct sockaddr_in myaddr; //地址结构体
( ~" |0 p7 r: J& _6 h1 g* e6 G/ E0 } bind函数+ ^8 G' {( H+ J4 ?- Q
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))4 b4 c9 ^1 F; S6 Z. r

8 l8 K- o+ ?1 n# A
) N7 e( K: |8 O
3.listen监听,将接收到的客户端连接放入队列" W& P6 w$ t5 q2 A
listen(sockfd,8) //第二个参数是队列长度  j) i+ W, R5 C2 z! B  P
4 `6 K( |9 K$ J, K) [# A. z

' y+ c: d, u' ~0 |3 w% [4.调用accept函数,从队列获取请求,返回socket描 述符5 ?" j+ @& m* M" u1 ]
  如果无请求,将会阻塞,直到获得连接
: C/ c; x; J' h8 v1 u3 @1 f  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数  n; p2 R6 I1 o# U# {9 k$ \

, o$ C* P( Q1 t, E

7 J# A6 D8 M1 `2 z4 Y5.调用read/write进行双向通信9 ?2 ^1 c  [6 j1 y# \

0 h; x1 s( Q1 c9 p
; ]8 F6 l) I: n4 f! T: t
6.关闭accept返回的socket5 w3 k" w  ~7 A
  close(scokfd);
3 R; j" l% ]9 C! r( J; S: _6 k* m1 x- ?3 A4 S5 g

7 V) k* r/ k5 @4 `: i$ X# u% Q; o/ ^3 N" \& Q) l

$ J% @( j8 o# \下面放出完整代码
: O+ r0 E3 G, I- c! P) u% I1 V/ f
  1. /*服务器*/7 C; g' V9 h7 v* e* f8 [
  2. #include <stdio.h>& v2 S( p# f+ e9 [
  3. #include <string.h>0 n# b1 o& u6 Y
  4. #include <stdlib.h>
    , _' R* O! @1 U$ J
  5. #include <strings.h>% A7 c% B9 Z' n) W- s
  6. #include <sys/types.h>
    5 Z, b0 Z; o+ {, `
  7. #include <sys/socket.h>
    9 l  ]5 k" M! y8 k7 {0 E
  8. #include <arpa/inet.h>
    6 {+ Q/ v  V5 W1 o* p
  9. #include <netinet/in.h>& Z7 q$ R$ y4 \: B; [& ?
  10. int main()
    - _6 F9 O3 ]$ U& g
  11. {
      j; I% F& k0 r$ m
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    5 B" K5 D+ H8 u4 Q# X* Q
  13.         if (sockfd < 0)! t4 }$ {  ]9 M
  14.         {; z, B. J4 ~/ [
  15.                 perror("socket");5 _0 X3 {9 |0 v8 ?2 _
  16.                 return -1;
    : R  J$ \& d# [2 x# k0 U. |
  17.         } //创建失败的错误处理
      {' S1 b6 t, u. ?  W
  18.          printf("socket..............
    & h" F" k7 i0 u5 ~. x" N+ p
  19. "); //成功则打印“socket。。。。”
    0 u5 ^9 ?6 p0 _) w5 h4 Z
  20.          3 T3 T4 L, N/ L
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体/ [1 X- z0 u: `% S
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)1 B/ L9 R/ W  O& h% o1 k
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    7 h- S! x1 @( r
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    * C8 I5 ~  B5 p
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址! N: L, ~0 Q$ \. l3 M

  26. 7 Z! o. z" h9 U. _
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    1 X! t& E' w" s, j  H
  28.          {3 U- ^" g7 ]$ Z
  29.                  perror("bind");$ U# {6 j6 }, d4 Q
  30.                  return -1;
    4 J* |, y. M& i
  31.          }+ `3 v, h& v: x
  32.          printf("bind..........& A. P$ \# h: F
  33. ");0 G9 Y- X2 L# v9 S# ?
  34. ' }- G' I2 l& n4 _5 t/ j
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听3 V  A- a! P+ S+ [1 \: @6 j
  36.          {
    3 d0 F* V6 v) r' ]8 d+ o# y
  37.                  perror("listen");
    $ q: Q' K3 }0 k+ x; T- V! S6 [
  38.                  return -1;( Y7 @3 Z5 y0 R; }' p2 Q; R4 e" ]
  39.          }8 M- n: L  F1 N" ?
  40.          printf("listen............
    / D1 @9 Y- C% y( @" _/ K3 G
  41. ");
    + K5 r- l" Q5 l0 D7 h8 X
  42.          
    & y' K2 i4 D; A) @) J
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ' H! Q" P5 k0 e+ W: @
  44.         if (connfd < 0)
    $ G2 K& o$ I1 \; d3 M, U: Q; u
  45.         {2 E, @. l# p! u
  46.                 perror("accept");
    - E5 f2 [7 X3 a9 u
  47.                 return -1;
    2 V* u4 {  z6 y$ E5 f" L
  48.         }0 f# y: z: L8 E) e' ~/ b9 L: m
  49.         printf("accept..............9 X, a& e6 {. |, P8 w
  50. ");
    , M: u& P) c! J% e$ y
  51.         char buf[100];//定义一个数组用来存储接收到的数据" J- Q+ _" _$ p# @3 D& M' G$ z
  52.         int ret;
    7 u' _: P& J$ D/ Z& h: |( _
  53.         while (1)
    $ G: S/ a, n2 z5 h. z
  54.         {
    $ v" l* e1 n2 W$ q. l8 H) `# A; ]
  55.                 memset(buf, 0, sizeof(buf));! W- f% F% y! O( S2 C. s
  56.                 ret = read(connfd, buf, sizeof(buf));
    8 @: H1 n; M3 q4 I! k4 p
  57.                 if (0 > ret)
    8 u) a, x" E4 v* T* ^# N3 b
  58.                 {$ K0 |3 F5 g' F. @
  59.                         perror("read");
    ) X) [& i- E+ f+ e7 B$ Q0 k3 w& ~4 A
  60.                         break;
      G2 L! ^8 n& x( a1 Z
  61.                 }//执行while循环读取数据,当
    6 C8 ^* L' A, R5 `+ X: y5 J& I
  62.                 else if (0 == ret)
    6 |, d$ j' u/ K! _4 \" J
  63.                 {9 j/ C  `6 P5 O* k# |, p
  64.                         printf("write close!$ s* u( V; ?! h* }! c  ]. t) Q
  65. ");
    ) [, \7 i" O* r: Y! ^) I
  66.                         break;6 O, m6 E" f- m' b: m
  67.                 }
    3 c+ E: k! f; x; n5 t3 `( Q
  68.                 printf("recv: ");
    ) Q) I! f; ~# e& ^2 b" V3 {
  69.                 fputs(buf, stdout);//打印接收到的数据
    " D: T# G1 S, ], [2 ~5 |2 Z$ Q
  70.         }, @- d; v, c+ X4 S
  71.         close(sockfd);//关闭套接字
    , R! q; {* ]" J, k9 n
  72.         close(connfd);//断开连接, I0 d# f# u: T! m9 X$ K
  73.         return 0;- I# }2 ~3 o# P* H; q( c; ?6 U# u9 g- v
  74. }
复制代码

4 ?& G7 G1 d  Y  L% ~8 I% S/ z5 n' Z1 T- @+ O5 T5 e1 z; |
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    ! W7 c: _' g0 H! j7 b
  2. #include <stdio.h>& K: {: n, E# [% ]
  3. #include <string.h>9 j) o' i2 @7 A+ k6 p. m
  4. #include <stdlib.h>0 Z2 ^% b8 o9 t, P
  5. #include <strings.h># E0 M9 t2 O) |: c
  6. #include <sys/types.h>
    4 n) n. X- [4 V- R% S
  7. #include <sys/socket.h>* B; j! `. t$ S8 d0 O! T- [
  8. #include <netinet/in.h>! A" _8 P  j* _* ]* E
  9. #include <arpa/inet.h>; G- H9 l; R" X; ?% a" X6 e
  10. int main()
    ) L5 Q2 c- m8 a) m
  11. {7 s7 K2 U3 X0 U7 j9 I; g% ]" r
  12. int sockfd;
      S7 N) t( M. [4 w
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ; Y% {0 h) ]) n  T$ o3 @
  14.         {& r$ Z  P& N% y9 U1 L4 E0 K: i! `
  15.                 perror("socket");& h5 N1 n& p6 D
  16.                 return -1;
    4 @6 r1 m: l6 f! i
  17.         }
    + o( a5 ~9 g  U
  18.         printf("socket...........! v* L3 j0 N  N& s+ S! h
  19. ");
    , z, e9 A: m! G, O2 `
  20.         . Y& \3 a* v: r' o! X. x* A! @  G
  21.         struct sockaddr_in srv_addr;- x6 j. w2 g; u% @% q' L' c
  22.         memset(&srv_addr, 0, sizeof(srv_addr));1 l5 {, Y% F6 V& A- ?7 {; S: W" t$ N
  23.         srv_addr.sin_family                 = AF_INET;$ s& V/ s- s: l9 t
  24.         srv_addr.sin_port                         = htons(8888);9 W% f) g0 I) i% S; G+ l
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");# ]1 s# v. C4 A: {. q, h
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))3 s& P3 p' `8 C5 b: b
  27.         {
    ( Z) m5 n( f1 r- g" s
  28.                 perror("connect");$ }9 \2 F  [. n$ D# d3 i/ S
  29.                 return -1; //exit //pthread_exit
    ( v, A; p8 u! [- d7 a+ @
  30.         }& H6 M4 y! F$ D0 D; N! o
  31.         printf("connect..............; R2 z8 ^- \1 n
  32. ");% H* j1 l* T4 ^8 Z; b
  33.         char buf[100];$ a8 `8 V0 ?! J1 A& ?
  34.         int ret;. }' [' M' i3 K% x
  35.         while (1)* \% ?7 S2 M$ a  [. b
  36.         {) G7 r7 [) }- ]! h
  37.                 printf("send: ");
    ( W6 w# ?$ v; d$ A4 ?: K
  38.                 fgets(buf, sizeof(buf), stdin);! _: T, b/ U" l
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ! @9 _1 n; P! s2 [9 h. A
  40.                 if (ret < 0)
      E2 M5 n8 l0 X$ V8 A
  41.                 {2 L! ?$ ^* c/ O' b5 O: |6 {
  42.                         perror("write");  M8 q, `' N4 r8 Z0 q8 S
  43.                         break;1 P& m) u. |. m* ^# {' Y- P
  44.                 }$ F5 f' Y7 N7 S7 g5 m1 F
  45.                 if (strncmp(buf, "quit", 4) == 0)
    * E9 a8 \1 c2 w* w) W; C4 C
  46.                         break;4 c4 J8 Y$ B7 x5 Z6 ^
  47.         }! X2 {. h4 ]" g' b3 w
  48.         close(sockfd);& k1 X0 P6 |' k5 z
  49.         return 0;% e  a$ Y) J0 G2 U" F
  50. }
复制代码

4 Q. f' Y3 Z( s! X
8 \, ?9 |4 f9 s# g. |) j. q) I
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-6-23 23:00 , Processed in 0.123045 second(s), 23 queries .

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