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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 3715|回复: 0

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

[复制链接]
发表于 2020-5-9 02:09:24 | 显示全部楼层 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。4 K5 t/ |  |% I8 ~# v2 [
' v, |' ]4 s# _0 f2 c

6 y9 D" f5 V' b4 ]/ osocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
3 x, s' a# J/ F" N6 w% e7 k; V+ ~7 B+ \7 q  Y8 D

) A4 {1 M! d. v. vTCP协议# G- M+ }8 Q( j1 B' {  {
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。1 k0 N& u+ X0 S2 }8 B: \
# M- s2 k; z+ q' c0 R- c

  k3 W6 v. D! x. O* ^关键词:三次握手,可靠,基于字节流。+ N* Y0 ~. j  V( ^% C

  E4 c6 o- W" _& E. k! k

* a6 @# b2 u% k* s% q2 X可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
/ _  ~: z2 W! g 微信截图_20200509015654.png
1 y6 |. f" {4 _5 ?( p, KTCP服务器端和客户端的运行流程) @! |: k) c' f% T2 D
0 p- S# g3 h$ I5 n- A. ]
. Y+ [% P' L6 P9 g
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
( k. ^  d+ q% ~9 O  M! B9 N/ m6 s3 t2 H' ^5 |1 ^9 T
3 a+ Y% Y2 |: Y
1.创建socket" I$ F) z1 v' v/ ^+ a6 f$ r
socket是一个结构体,被创建在内核中
, O+ p0 M5 k9 r7 g5 q sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议6 N  _8 u' o2 i- }

, m3 R/ X2 u$ y+ K( \: T8 S
- s: g1 \1 f2 h8 Q" J4 K
2.调用bind函数+ q, y! }0 D& b* Q1 _
将socket和地址(包括ip、port)绑定。
: X! ?+ w$ \6 x: O% a  _) o 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
  x3 a1 {, ]# x" p  } struct sockaddr_in myaddr; //地址结构体6 i  E$ H# t- K2 v9 B- d
bind函数
4 z+ F4 J0 X9 q7 ? bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
; |& _7 K# _' ^9 F" c6 S; b. z6 j& M, `: `) a$ v% p* O
- Z, o/ g. {) _
3.listen监听,将接收到的客户端连接放入队列' h6 j  _9 ]* ~) G
listen(sockfd,8) //第二个参数是队列长度
% Q( w7 a% w' G- {
+ O3 x  Z2 r0 n& D( n

7 I* O9 [: d/ y; f4.调用accept函数,从队列获取请求,返回socket描 述符
3 h1 f2 a& J$ T; h- q4 Q. P  如果无请求,将会阻塞,直到获得连接
/ |! e9 r& @4 m# C; y5 E  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
% v# c- G9 D; H4 k# u8 N( F6 [5 Y' p0 `# g0 ^

/ W8 ^& B% g# R7 A7 |5.调用read/write进行双向通信1 I/ n8 A" B3 L+ [: u

! d# F( N$ }; E& ~9 [

; K7 a2 o7 ]6 f8 f! u6.关闭accept返回的socket) ~: W- q& A! r' g
  close(scokfd);: |: h4 H) {0 w

" f0 H9 P7 `/ b  Q$ A! b2 }6 V& }
+ r4 z  F% a: A' D0 `: ]
* a( |+ V& W4 U7 ?3 q" j, g7 b. A& @
2 ?. C7 V2 v  t) b5 w3 I. j
下面放出完整代码
; d% F1 U9 z2 ^, w7 F) P% u: S& U9 O2 E) {7 [
  1. /*服务器*/
    0 k! X; h" o$ O9 V' k- G
  2. #include <stdio.h>
    0 S2 k2 B  U  b' o* K
  3. #include <string.h>- f5 V, |' w+ _2 I) ^
  4. #include <stdlib.h>
    ( S. ~) Q* ^! \% S5 K- t5 z
  5. #include <strings.h>. u+ V+ d7 }$ ?- S+ U
  6. #include <sys/types.h>1 d6 w6 W" g: M; t1 o
  7. #include <sys/socket.h>& Z- R) q* S( s/ d- q
  8. #include <arpa/inet.h>+ i1 |! ~' u' q! |3 l# u; u) D
  9. #include <netinet/in.h>
    9 J$ r6 F4 [0 o% T, k, H
  10. int main(). Z2 _/ S3 B- {& S5 c# d
  11. {
    . C7 }6 t' U  G9 N+ t
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字  ]6 |9 h6 j( k" b: I+ A
  13.         if (sockfd < 0). W% p* j5 ^$ S: w. T
  14.         {4 Z" r5 p: X' y( w3 V
  15.                 perror("socket");) I+ b4 q7 ]/ P! Y! S# m  H2 ]
  16.                 return -1;
    " L" y" K2 |& H4 N1 t7 t- t
  17.         } //创建失败的错误处理* q& m1 H* j4 _$ A+ w5 u; n
  18.          printf("socket..............
    $ _+ E/ x- X- h# X6 X0 ^: ^
  19. "); //成功则打印“socket。。。。”
    1 T6 \0 h* y( y7 f9 ^! O
  20.          
    5 N6 C, r$ C% g# {5 T
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体  N( U" ]1 _" W* F
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    7 T6 D) E8 j5 [- k
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型! y3 }; m5 }' ]# a' k+ u) N1 ]
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    " k/ F# s) F( ^# @) Q
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址7 U8 n7 ]4 j# [6 v7 H0 _
  26. 1 v2 V& U, C! M* V* O2 \
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字  E# ^7 R1 z6 l' I
  28.          {! V4 w$ j* V8 P& v
  29.                  perror("bind");) Q* T; v5 E% X( @( |; F+ W& {$ V
  30.                  return -1;
    ! S4 X! J. E* i6 ?' h* i
  31.          }# {; }# {+ ^' A
  32.          printf("bind..........1 {8 s, Z' K5 \0 Y4 O$ e
  33. ");
    2 ]) `+ P2 h2 ^: F' K$ R

  34. 1 k" J& _) P, F& y( K: \
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听  a& }- ^* r# H; O; ^4 X5 I! G+ t
  36.          {7 i* y4 V' Z! N7 M# W4 c5 t( r
  37.                  perror("listen");
    . E8 n9 y5 _, [( c6 O% L
  38.                  return -1;2 H6 [0 T7 Z( N# q0 P% O) {2 E
  39.          }
    ! {- C6 Q7 v4 Q9 I! P
  40.          printf("listen............3 D4 X" j  H6 c4 f, t
  41. ");0 N* m& ]% @# p4 \5 I
  42.          
    7 R5 Z5 \  p3 {' A6 K6 V% v
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    + e( j7 V+ n% z
  44.         if (connfd < 0)1 B: c% y. f6 v
  45.         {
    8 M. t$ o" U* o5 R$ B( K
  46.                 perror("accept");+ R, F! j0 Z3 ?* m# Z8 Z
  47.                 return -1;
    $ Y$ W8 X9 R, H2 T
  48.         }" [- p! Z/ _( Q' M: ?7 I
  49.         printf("accept..............; c& Y% {4 E. ]8 I/ b1 T
  50. ");  p5 {. Y. N6 |# f. I; U( I
  51.         char buf[100];//定义一个数组用来存储接收到的数据% \$ c6 J& w- @' E+ h4 w
  52.         int ret;
    8 V  `7 ~9 `# T; @4 F8 A2 C
  53.         while (1)$ w- W2 A; r# C$ u: j
  54.         {; k1 r( g4 _0 ?; V5 w$ Z- g" P
  55.                 memset(buf, 0, sizeof(buf));
    - n% ]( S8 P; T" ^
  56.                 ret = read(connfd, buf, sizeof(buf));
    ( Z) G. ^$ k5 ^5 g1 S- K8 t8 _# F) s
  57.                 if (0 > ret)  t# o" d/ [8 X* L2 r: u) U% l7 f/ A
  58.                 {
    6 g$ z8 \& w% d& N
  59.                         perror("read");
    + M/ n) {1 V& f; @8 b0 ]: A
  60.                         break;
    % T1 q  @( |( u1 R
  61.                 }//执行while循环读取数据,当
    & H6 w; C5 ~8 {  r* A$ w
  62.                 else if (0 == ret)
    2 |$ Q4 f$ H+ c  [
  63.                 {
    6 w( X0 l" |) {0 |3 o" y/ S5 R
  64.                         printf("write close!8 g! K! l8 `: u5 B+ F% M5 @- @
  65. ");0 u/ t3 Y8 N0 k3 _/ f
  66.                         break;
    / J+ @  [; `6 e! I* J3 t
  67.                 }+ ^. N. g5 R" u! \$ q
  68.                 printf("recv: ");
    " n5 n9 N; d- t  O
  69.                 fputs(buf, stdout);//打印接收到的数据8 O5 ^1 u9 J% x- i- I; ]6 B
  70.         }; g8 S- A! B  ]* B/ F7 P. V" h
  71.         close(sockfd);//关闭套接字3 V1 L. O6 e* L" _' Q: S8 Y
  72.         close(connfd);//断开连接
    6 x; H, X0 S' m! M. o6 Y1 f: Q. \$ s
  73.         return 0;* o/ L: S* V: K; S. P
  74. }
复制代码
* F3 l" E$ F6 l+ W8 }

8 ?* ?2 T  I* }7 p# t! ?' g
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    # b* D. I  l  i
  2. #include <stdio.h>- ^, o- t& L6 x7 j3 X
  3. #include <string.h>
    # h! A# _7 [! N1 C& r( B) B
  4. #include <stdlib.h>% B6 |/ {; Q9 `+ w
  5. #include <strings.h>
    / |- l( Z, |6 t4 l: Y
  6. #include <sys/types.h>
    & G0 b# v3 O5 l) m
  7. #include <sys/socket.h>
    ; j. k  D. n. h, `1 X% T
  8. #include <netinet/in.h>, e0 A% J% Z! M( |4 B1 `" B
  9. #include <arpa/inet.h>
    2 E1 A" r; _3 j$ k# M) R* `# Q
  10. int main()
    ) m* D9 U+ d/ r
  11. {
    ; T/ ^' P3 \. [3 e. z
  12. int sockfd;
    / ?, l1 t8 g# X! x# x+ |6 k: q' k
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    % [/ h! X- o# f
  14.         {( q2 G  }9 \* z
  15.                 perror("socket");0 C1 U4 O- H) n4 n2 G6 }- @8 s
  16.                 return -1;
    : x; L6 Q5 w- I  f* k
  17.         }
    : B, v; |% n7 |
  18.         printf("socket...........  Z( x& f2 ~  w* X' |
  19. ");
    $ I/ j! p9 K" d* Y+ ]# t3 H( ~
  20.         : R) c* u  f9 S: T6 ~; f+ X
  21.         struct sockaddr_in srv_addr;/ S/ v; T4 z' W* ^0 ?5 p2 k
  22.         memset(&srv_addr, 0, sizeof(srv_addr));# Y( Q* C- J2 ?( m
  23.         srv_addr.sin_family                 = AF_INET;. G, P, m4 [6 C4 a' Q/ q, f/ j$ \% r. {
  24.         srv_addr.sin_port                         = htons(8888);
    $ a# t, c% T7 t) n+ {
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");. u, t0 B" M2 H0 y1 T" `
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))) f) u$ G% I, C' V( {2 v
  27.         {
    / I" ^; E( Y# u6 M
  28.                 perror("connect");  K7 K* y9 A% i
  29.                 return -1; //exit //pthread_exit
    7 L+ \9 ?/ a" s
  30.         }
    ' A$ ~4 o+ X' r* y9 A. k+ J
  31.         printf("connect..............
    $ M5 u! \% u1 _& T/ S
  32. ");
    # k, `4 U) ^( V" k4 n: y
  33.         char buf[100];& i$ ]  M6 H7 E5 D
  34.         int ret;
    0 P* p, o9 c6 s  A; ~; ^3 ?: R
  35.         while (1)$ U) ]. @% d; q2 W4 S" v6 u
  36.         {( K7 ~5 l0 J" Y- g. b( T
  37.                 printf("send: ");
    : V4 i7 `! c) ]# Z! S/ a6 \
  38.                 fgets(buf, sizeof(buf), stdin);# S# Q3 I) e% y3 h
  39.                 ret = write(sockfd, buf, sizeof(buf));! ?+ l2 b( m6 M: L9 E/ O
  40.                 if (ret < 0)+ @( ]! n9 H! y. e# S9 v# j7 o& @
  41.                 {
    7 M* Q& i( p2 C
  42.                         perror("write");
    1 A: \& E4 g& l, a2 j( M8 i8 @
  43.                         break;
    * O/ i; t) D9 r
  44.                 }
    ) i6 @, c' Z' y% V5 Z% i; p
  45.                 if (strncmp(buf, "quit", 4) == 0)
    8 i0 b. q) E7 h: r/ N8 ]# t2 ^8 \! C0 H
  46.                         break;5 p% _! `* s3 l1 r: ]: g# I7 u  i
  47.         }8 S& `& Y/ A: Y
  48.         close(sockfd);- r0 V7 h, z2 x! C/ X8 K
  49.         return 0;! l$ |0 Q8 [* Y$ P
  50. }
复制代码

! n( [& c: }! k/ b3 D" [" J
: q5 j& A4 K& {  E! Z
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2021-9-24 17:39 , Processed in 0.139472 second(s), 25 queries .

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