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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 8550|回复: 0

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

[复制链接]
发表于 2020-5-9 02:09:24 | 显示全部楼层 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
' Z# Y2 ]; [- g6 V
( z) m0 k9 B  Y) ^/ q9 o6 \" f
/ a& O8 t$ P; k4 o+ T
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
7 |8 U4 V; G& Z( {
5 v8 g4 c* ]* }8 j! B& \

+ s+ V7 |6 F# zTCP协议  V6 {( R5 v: V
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。4 H# Q; v- u: F

# [9 ~5 ~+ s1 p$ H/ l- W
& Z8 C: z% j0 K. }1 E& Y
关键词:三次握手,可靠,基于字节流。
. ]9 w6 w0 i, h( L$ N9 Y$ U5 Z5 L0 ]9 |5 O, E

; F: m- R' c+ x/ E可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。: p% S& R/ ~0 X6 T( u& ~" H
微信截图_20200509015654.png
# q/ @$ C; I# i! D$ E3 |) A" f0 u/ rTCP服务器端和客户端的运行流程% V  c0 O/ z. k. k( Z6 I; m& [

& g; d! B" k+ T# Q% a  G6 |2 k7 N
# S3 N' B% F+ j  e/ x
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?9 K- p. ~* V) T

5 c# v6 A. L' I9 b" z) M
! L% r8 k6 e3 U
1.创建socket3 G; h" p0 a' F3 `
socket是一个结构体,被创建在内核中
! S3 O5 _+ U+ m* x% y sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议: {4 h' V% C- X% {" H

" j$ l) F# W: A! d0 S

- P  x0 _8 M  Q2.调用bind函数
7 Y0 K, `, c  A# H; [ 将socket和地址(包括ip、port)绑定。
) h/ y1 D& r( T$ X 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
# Q/ x+ q; S0 B5 \9 {- v: r struct sockaddr_in myaddr; //地址结构体7 }: |0 J, t7 H/ K- ]
bind函数/ q% b! B7 M9 k& Z! U, ?+ V1 `
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))! X% d' m1 f0 g8 j( K6 W
% a% f" \( q0 S- e
( Q9 `( Z9 y: \6 b- ~! o
3.listen监听,将接收到的客户端连接放入队列# T4 P1 ^) C& Y' B; V3 u, {
listen(sockfd,8) //第二个参数是队列长度9 ^% ]. k; D' x0 @* |5 z
" J" \8 u( _3 c% a
9 C: k* l+ |% S1 `  j
4.调用accept函数,从队列获取请求,返回socket描 述符
; ]6 {7 m* g' ~, i# w  如果无请求,将会阻塞,直到获得连接4 C- o! m# x0 b
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数2 l/ @' S0 a6 q. F7 \9 i: C7 s

, x  a/ [, }8 Z4 f6 e
2 Y* J, v- Y0 ?' k0 L' Y
5.调用read/write进行双向通信" s5 P1 V' z9 ?9 R5 W

" S5 @1 j2 o4 D. ?; f

1 M  [6 Q& g) G1 O: {; S4 r0 S4 [6.关闭accept返回的socket+ D+ M# [9 W+ [2 n/ ^
  close(scokfd);
0 C, S. B% v& _8 L3 U5 o) W' J1 s5 R
" ~/ l. @2 u; Q( g0 @: L( a+ I

' a$ Y; W, }8 ]) a+ u/ U" |' k: G

" h# A9 l& Y7 f2 J下面放出完整代码( `5 Z' q& T' z+ W) t
% r* h" u$ N  M* a$ ?1 v
  1. /*服务器*/7 Z5 ?# u, D3 Y4 r5 P2 \) K
  2. #include <stdio.h>' {3 n: }$ S$ A/ s- I0 x! o1 U
  3. #include <string.h>
    0 o; H3 O/ B7 u( d0 I& p* k
  4. #include <stdlib.h>5 z; v; ], A+ X4 Q, j
  5. #include <strings.h>
    4 d$ M/ Q$ n  e4 l) `7 ^
  6. #include <sys/types.h>% d8 w5 C- H  [+ f
  7. #include <sys/socket.h>
    8 I3 ^5 @3 h' G/ v3 _, a
  8. #include <arpa/inet.h>4 m2 f0 D5 V2 }
  9. #include <netinet/in.h>
    / Q# j8 z; a! a3 f; |$ x% q
  10. int main()
    6 t7 ?; l& M8 K6 K
  11. {0 V* j: W. e- K( |& j- _4 Q
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ! C# K: M) O. V$ Z$ T& \
  13.         if (sockfd < 0)
    ; e; U2 U5 C0 W
  14.         {6 T2 d1 q3 A* M& K3 B  ~
  15.                 perror("socket");3 b2 m' l6 Q8 u* n! q5 b2 t- Y* W3 P
  16.                 return -1;5 p4 q1 K" \) Y: f) H2 K8 {8 Z5 g
  17.         } //创建失败的错误处理
    ) k1 ?6 R/ ~. J  @
  18.          printf("socket..............
    % @: V  E  t6 U: j2 q9 `* D& l
  19. "); //成功则打印“socket。。。。”
    1 A2 C" z$ d7 C* q- ~1 `  r
  20.          
    * ~2 O+ h% F; C( {2 k( g% N
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体5 ~7 T! x9 c5 P0 v4 F6 g6 I
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)0 I6 Z/ _: c' B( [; m& D
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    - A. a& y3 R  i! K3 n7 \
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ( X" B" x5 H1 G- y' V
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ( A/ y- P( u( q- L) p; z/ P

  26. 6 K1 a/ a) v6 |& w( [
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字0 n4 f5 u9 z) M$ W) \- q
  28.          {( Q3 J& |  ^0 n) y, Y8 c
  29.                  perror("bind");
    # ?8 @) t3 b8 u3 L3 o+ g
  30.                  return -1;
    4 M. t! `- G) i4 W6 G
  31.          }7 d) S" F8 Y: ~4 f. p
  32.          printf("bind..........
    ! o9 X; M7 V# [4 {: ]! k* O
  33. ");2 F6 V+ m2 R9 O/ b

  34. $ I6 _, W( G( c6 L' @. L
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听: k; U$ J/ Z8 P! Q! T! I( N2 s* q
  36.          {
    3 ]! I; F7 [; ?; L
  37.                  perror("listen");
    6 V. E+ Z  T  v  w: O
  38.                  return -1;- T  g1 k7 s7 \( X
  39.          }. R3 w& Q7 d# F% W( u6 w
  40.          printf("listen............
    9 d  k8 ?: Q: w- C+ G, r
  41. ");
    ( q5 n$ ]! G3 `& q
  42.          
    + N8 _* N% z3 t7 @$ H3 E4 d9 Y
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    9 T% y! E2 T5 a4 H4 G
  44.         if (connfd < 0)$ V- d& @3 r6 J* g+ {
  45.         {
      g2 j$ T" x" F8 B% W1 f
  46.                 perror("accept");
    ' ^- g4 ^* F, s( V0 P* ^
  47.                 return -1;
    * R$ M! A- r+ [6 U' i  i7 k3 T& v
  48.         }
    8 ^- Z: {) f9 z) R$ r/ y" c
  49.         printf("accept..............
    . P) c" ^) s8 F  z
  50. ");: L) Q4 O& ]/ \# W: M0 ^
  51.         char buf[100];//定义一个数组用来存储接收到的数据( ^! h  S# J. a( O8 ^
  52.         int ret;$ i7 A: [- ?- K- a" X8 G8 I  M
  53.         while (1)6 S5 x' R4 m) z& A2 ^# F
  54.         {
    + |. ]$ X: b1 u3 f$ e5 ^$ e! ?
  55.                 memset(buf, 0, sizeof(buf));" u% j# u' d; K) o3 K1 n6 C
  56.                 ret = read(connfd, buf, sizeof(buf));
    & G8 Z) k6 V4 A2 G
  57.                 if (0 > ret), z) a- r0 G: S9 g7 K$ [0 W8 x
  58.                 {+ V' ?, w5 @2 I: J
  59.                         perror("read");
    5 P' H4 z' c3 W0 ^, Q$ L% j
  60.                         break;2 Z% ?& |0 D) [/ T
  61.                 }//执行while循环读取数据,当
    $ o4 n/ I4 Q& p) c
  62.                 else if (0 == ret): R4 ]7 |4 G- R+ |
  63.                 {& w# q0 j9 V; y0 `  b
  64.                         printf("write close!0 G. Y, }, B/ _% z/ l
  65. ");3 |' ^1 u' o. J; S0 C1 B" b: @/ x
  66.                         break;
    4 M2 |+ [; M9 ^5 U* R7 T1 R
  67.                 }
    0 L6 ~$ k* M; D
  68.                 printf("recv: ");8 m4 U  m; e" ~- j# s: V% O* e
  69.                 fputs(buf, stdout);//打印接收到的数据& y! u6 z$ n# k# f7 {
  70.         }
    2 I2 F6 S5 \* ^
  71.         close(sockfd);//关闭套接字0 h/ O. L+ ~% d3 p$ ~
  72.         close(connfd);//断开连接
    ! j" W0 i/ F% \! |/ A
  73.         return 0;9 N# I4 S" r1 N2 y
  74. }
复制代码

2 Q) \2 b* Q3 h9 j$ ~$ d; }0 R7 t+ [4 C2 B! \: P# I
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)8 W# X! V* ~& r8 M6 S, E
  2. #include <stdio.h>
      F. J8 h3 Y8 `9 c/ y# r
  3. #include <string.h>
    ; i; X9 m' h" d
  4. #include <stdlib.h>, F; D4 X6 Y) @' V
  5. #include <strings.h>2 m' _" j5 H; a; ^9 \; q: R/ P
  6. #include <sys/types.h>
    7 x% ~# a6 m7 X: Q
  7. #include <sys/socket.h>
    2 ]" ^$ K5 W2 Y( i
  8. #include <netinet/in.h># |  F) d/ J; {/ D! A9 v
  9. #include <arpa/inet.h>
    / x" v! v) m' d, G/ P7 s
  10. int main()6 B" Y% w$ t1 {/ l0 r: X. ?9 }
  11. {
    1 T4 S, j6 x5 `  T
  12. int sockfd;9 k. u/ I# \% Z/ p; I+ z& P
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))! s( j: ~0 o0 [0 |% w
  14.         {, E1 c- A2 u6 M) w- q- ^
  15.                 perror("socket");+ ^3 [: c) |' K+ O0 y  ]! W6 o
  16.                 return -1;
    ) p( b" l, Y8 R2 l
  17.         }
    7 t5 N# a1 r0 b5 Y
  18.         printf("socket...........  X' @" l! j: |; o* r7 ?
  19. ");
    : Y% e, Q, S6 U
  20.         
    " F0 `; I3 T  X; v2 t$ M4 w, f
  21.         struct sockaddr_in srv_addr;
    ) |2 _; G- [! r' s) m
  22.         memset(&srv_addr, 0, sizeof(srv_addr));& \! s) U4 i* B0 D! D# Q
  23.         srv_addr.sin_family                 = AF_INET;
    7 U# k# G: z7 G$ {
  24.         srv_addr.sin_port                         = htons(8888);
    7 V  m( [; D, ^; x& C& [9 F
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");% M  d% k4 ^6 S2 X- a2 w5 W0 `. v
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ; I& Q4 H3 `% K% W
  27.         {
    - F; G" `8 m; n, _7 q- r
  28.                 perror("connect");2 M, A5 v) Z6 |/ z: o
  29.                 return -1; //exit //pthread_exit# w' S% a$ Z9 |8 e9 Y
  30.         }' N1 j7 ~2 g& }) Z7 h9 L
  31.         printf("connect..............9 V9 Z7 H* I: ~2 Y2 i4 {9 }8 T' w
  32. ");
    6 v2 z6 Y* m" H3 V" Q3 I
  33.         char buf[100];1 d( H9 m1 q6 B( i
  34.         int ret;
    # j/ L" ~) N+ k; d8 z5 \# d" A  z
  35.         while (1)( M% ]5 \# ~. f3 W$ U$ ~6 i
  36.         {8 a3 J7 V/ |, a3 ^
  37.                 printf("send: ");- J$ i! b9 q! Q7 c1 D6 w9 E
  38.                 fgets(buf, sizeof(buf), stdin);
    ' Q8 |7 N) ~/ |+ I! c, W( v; w
  39.                 ret = write(sockfd, buf, sizeof(buf));
    & t% p$ J; w9 [
  40.                 if (ret < 0)! K, w4 }8 e" w; q! u! k$ E* c" g% @
  41.                 {1 q& `& a5 W& _3 k4 G
  42.                         perror("write");! ^% h# n0 i9 \. J
  43.                         break;
    5 x6 C* \1 f, d7 H. w' w2 p
  44.                 }
    / U& W! Y  t8 p, }+ O: d$ i* |
  45.                 if (strncmp(buf, "quit", 4) == 0)
    ! R" H7 L, J2 Z0 u# a
  46.                         break;
    * k2 Y* Q! Q% \1 L
  47.         }' L( @& w: \' r5 g% K
  48.         close(sockfd);
    ( E, T8 {" B( |: _6 @
  49.         return 0;4 D9 Z5 m" H7 _; z
  50. }
复制代码
$ m6 M+ P, c# G- M/ K$ Q: M0 ~
$ K& l0 m# _2 S9 L. M0 q
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2023-2-1 17:42 , Processed in 0.147370 second(s), 25 queries .

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