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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
+ }! T# \" h9 ^6 A. B; y* `  ^1 F& Z6 @' _
7 K/ W1 v2 P% ?6 o5 n
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
' P  s2 V2 ]  q. p" \8 I9 |2 N* v: K3 }! L6 K
& T2 s) l% B+ \" L
TCP协议( F/ L7 S& f( s+ @
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。; h- `0 W0 |; ^2 O- k

& Q3 M2 P0 I# W' y2 Z; A

: R1 x  }/ f+ n5 @5 ^4 J8 _关键词:三次握手,可靠,基于字节流。
/ `$ N. M: W0 F* O1 ]8 K( l3 h9 R2 u' [

) K2 Z5 v, {! [5 T8 I& w可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 X& d0 F+ T( U" a
+ e; k( @3 o5 Y$ }$ r( |# C
TCP服务器端和客户端的运行流程
+ S* R/ [; Y9 c0 R; |
. @0 T) P$ V& p- B# v3 m

9 }1 R) F& ^$ _8 g& Q如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
( S8 ?9 Y% l. z# I3 r, x# _* W
1 {6 p: s0 r, Z0 i" o% I' x, [

, y+ q+ u0 S9 o+ ^& E1.创建socket
; {; |0 a* C. B: F3 [ socket是一个结构体,被创建在内核中  n( k# }2 \) f; A. J( [
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
1 o2 `5 K0 s/ l5 {1 n) c9 M  U3 x- {! I7 b- |* |$ v8 K
7 N8 M- w8 B( h3 R( z/ w' A
2.调用bind函数
8 p* E; s. X% X, N; g 将socket和地址(包括ip、port)绑定。/ i4 v& l( C+ }$ N# Z
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
6 f2 l+ Q  s. n struct sockaddr_in myaddr; //地址结构体2 g6 f( _6 f8 g& j4 W
bind函数
* o9 o& ]& N. Y' S3 U bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
! g- t' m4 V- P) v9 |% A
! n! C) T, v8 |, S; j& a7 n* `
0 ^# ?& F; f8 a) R7 r7 q/ D
3.listen监听,将接收到的客户端连接放入队列6 `" C* D8 U$ m; a6 e9 X" U5 Y
listen(sockfd,8) //第二个参数是队列长度
' C. o, L. K" s* U2 n  p/ P! S$ G) Q+ l% g3 Y0 Q, O: `

: X3 e( o' J5 ^+ b* d+ H% b; d4.调用accept函数,从队列获取请求,返回socket描 述符: k6 R( Z0 N: [, E0 b1 Q: k
  如果无请求,将会阻塞,直到获得连接
6 \, \/ v) f  o! U4 ~7 g  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
; X( E& k# H, e& B/ b
8 i6 L# K5 g. {; p9 J8 b
) O6 g' |/ J7 W. k
5.调用read/write进行双向通信+ m7 M3 G; L# d/ t5 o: a( s
" A, W% W. j& y1 n7 E
; m9 s6 O' W' S6 O/ M- R/ }
6.关闭accept返回的socket3 _* Y+ @6 n4 M) l. \
  close(scokfd);
9 }; A/ d% v  h0 z7 k9 z: T
- [- M4 k! [$ K3 _
  x) u" V9 D5 k4 [1 Z! h, y
6 h1 l) O* R" i2 I
6 O/ _  w, r6 e' @( ~- c
下面放出完整代码
/ U( ~* S3 i2 k7 u, @* l6 q, H& G/ _
  1. /*服务器*/4 w) F& G0 w* ]$ y6 Z
  2. #include <stdio.h>
    7 u/ h9 C' U; V# K3 ~1 u3 `3 o; |
  3. #include <string.h>
    % x+ ~- z2 E2 |3 |
  4. #include <stdlib.h>
    ( e: H" V' V+ e  T7 ~+ a- e
  5. #include <strings.h>3 n* Y7 [5 J, `& x; F2 v
  6. #include <sys/types.h>9 Q6 k1 D$ P! [1 A+ C
  7. #include <sys/socket.h>9 h' u7 H0 p8 u
  8. #include <arpa/inet.h>
    3 T2 g5 L& I- R
  9. #include <netinet/in.h>. ]2 j/ S* m# U& g2 E# n' w
  10. int main()3 x$ \, g* T" G' B
  11. {
    7 [" Y' @  J" g2 ~
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字; b. U# |$ m- o7 L6 H  R
  13.         if (sockfd < 0)
    8 m: Z% z% H& ^/ L; K
  14.         {
    6 G- r1 w* j, O* f, U
  15.                 perror("socket");
    0 G, X; Y5 v; c" c2 i" t
  16.                 return -1;
    8 ]( d& e! C- p, V, |
  17.         } //创建失败的错误处理
    . p4 P* V6 Y# Q/ c0 W. f, {4 x: ?
  18.          printf("socket..............! q" }- s2 X- j" D: f7 L: q
  19. "); //成功则打印“socket。。。。”
    . g3 y' x- M8 Y, M: P$ L6 p- `7 |
  20.          
    ! W) R8 k. ]% H1 }6 Z# d! i+ d  d
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体6 z: T# j7 {# U) I* p& V
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)9 U# |0 u+ s, H' X" k
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    : u+ e9 _! U+ O% A
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ' J3 h" x$ _6 K- D8 [  j0 [+ o
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    7 W* b, |2 J1 B/ A# j
  26. ! [1 H5 a5 R8 o+ k
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    8 j5 A' O0 ?$ a! ^5 m# D
  28.          {
    , }2 y! i% b/ Q0 M; |6 R6 S
  29.                  perror("bind");
    ) I0 j- t' l7 ^8 `3 ]& R, ]
  30.                  return -1;
    9 N$ Y( F" F% l( _2 N1 h7 j
  31.          }3 o- l+ f7 n2 b/ F  O: B
  32.          printf("bind..........
    0 D4 {0 k0 z' A- D% q1 W
  33. ");  H6 ]1 Y( `- y$ E
  34. ( K$ T) J3 G7 }4 n  I  O" }3 ~
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 A4 _& W5 d+ |+ H- [
  36.          {
    . z; {  g' W5 l$ v2 j6 v$ T* [
  37.                  perror("listen");
    / v- W0 W+ P, A& m' }
  38.                  return -1;
    , k. E" K; u% f  w8 a: D+ ]/ A5 r# F
  39.          }* a$ t! ~* g) }2 ?! p' w
  40.          printf("listen............
    ! ^7 i$ s/ E. ^3 i$ T& Q' M
  41. ");, U6 O- D: p( t6 u4 t$ q
  42.          
    2 G9 |  p/ ?* F1 B% q
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求/ ]. B2 e9 a1 [% ]
  44.         if (connfd < 0)
    2 K+ z; |! t; ?9 q6 Z( Z
  45.         {7 j+ l( u8 b. ~6 E0 K, ~
  46.                 perror("accept");
    ) A$ ~4 q6 M. ~/ a' t* {
  47.                 return -1;  W9 W6 B& b; K9 H8 b2 {
  48.         }
    0 C6 z$ m" u7 a/ }* ]
  49.         printf("accept..............+ `) q) n2 ]2 j/ S: |5 S8 C* A+ ^
  50. ");0 k4 Q! \9 n( F( p* w9 m5 q
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    & H/ r- S4 h* f, B
  52.         int ret;
    $ a; w& z, Q/ n2 ]* p5 K+ V, s
  53.         while (1)
    . y6 s3 {) v' k1 ]! W
  54.         {$ j  f: L# ^1 K: B
  55.                 memset(buf, 0, sizeof(buf));
    4 e# u+ R( X4 G  ~# C
  56.                 ret = read(connfd, buf, sizeof(buf));( d% F% U, l7 ~3 a0 z
  57.                 if (0 > ret)2 b- k; N9 b2 b4 B! x
  58.                 {
    ( K0 {0 n7 B! ~/ L* w$ r3 c
  59.                         perror("read");, \$ ]( |- H+ L6 E
  60.                         break;. p6 f1 e0 g/ \; o/ R0 i
  61.                 }//执行while循环读取数据,当
    & f5 L8 F& `; ?. Y/ F9 g4 X
  62.                 else if (0 == ret)% H3 b! m$ p4 t9 Q0 q/ z
  63.                 {
    4 I; y" d# N7 p9 I+ R
  64.                         printf("write close!
    / Y. p: N$ l3 ~
  65. ");: t5 o% |5 o6 d5 C. F1 Y, D
  66.                         break;
    * k) ?  s- X6 r+ o, K/ N
  67.                 }
    + Q# I0 L7 R6 n  }
  68.                 printf("recv: ");
    , s* I8 w) q! v& c, F. c
  69.                 fputs(buf, stdout);//打印接收到的数据, \0 B1 a& m' m
  70.         }; @4 M  v7 Q' b
  71.         close(sockfd);//关闭套接字
    . p- b& ]7 }1 x1 I' P
  72.         close(connfd);//断开连接
    # ?, s* R3 C& e2 R" B
  73.         return 0;2 _; F. r& u) u. A+ U2 b* j% e
  74. }
复制代码

( k! ]& l6 v1 e. S7 J+ \1 p" M  K! n
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)* w% }# K  {/ H
  2. #include <stdio.h>
    . c% Q7 v" O- Q( {1 }
  3. #include <string.h>
    ) G5 g1 I9 Q9 J. \. r
  4. #include <stdlib.h>
    , @9 K7 j* n( U! q
  5. #include <strings.h>5 P3 i& Q- h8 c" R
  6. #include <sys/types.h>6 ~( E& ^$ i8 s. t$ F
  7. #include <sys/socket.h>9 \7 p/ c7 c: \/ T3 O6 F: `
  8. #include <netinet/in.h>
    + Z# b* o, G" Q9 m: G2 w! W: }/ }; C
  9. #include <arpa/inet.h>
    3 e" x" Y4 V$ F! v7 P5 n  {
  10. int main()* P5 v& U* \: h  o$ c
  11. {
    1 g% n/ P1 B: Y2 P
  12. int sockfd;
    5 t0 G9 ~/ q# D; b
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    5 O- H' ^. C8 M' e
  14.         {
    6 G: f) v, C* O9 o1 l* Y% j& T, [
  15.                 perror("socket");7 T2 ^; C2 _3 F
  16.                 return -1;2 X0 L- k8 P+ `% P" P* W
  17.         }
    . S+ f2 ^8 L7 M/ Q9 {
  18.         printf("socket...........
    - a7 I# Q" p/ F
  19. ");4 z4 X1 O+ Q  Q9 `# b
  20.         " h' H& a3 i- U5 T  a$ B1 Q7 N* a
  21.         struct sockaddr_in srv_addr;8 Y9 ?% Z& |* M7 P' Y
  22.         memset(&srv_addr, 0, sizeof(srv_addr));. v* T/ n: e* N% V) K
  23.         srv_addr.sin_family                 = AF_INET;' [7 D0 X8 l$ S- {2 g
  24.         srv_addr.sin_port                         = htons(8888);
    ) C' l5 x2 w) o1 R* J( H! _4 z
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");& \4 B1 u# o' N
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
      ~% G1 G. u* I+ _
  27.         {
    5 d. Y7 s' k3 _" j! [( L
  28.                 perror("connect");6 L, T& I6 }3 s2 m( w
  29.                 return -1; //exit //pthread_exit
    ; T+ Z- k5 `/ E2 I
  30.         }
    5 f1 v3 ^: L3 k4 ?
  31.         printf("connect..............* f. W/ n* X! j2 M% i
  32. ");- w' X4 S' ?7 g% {/ ?/ q- _
  33.         char buf[100];
    ' `1 I, ^1 m+ C+ [
  34.         int ret;: r' [1 e, R! E
  35.         while (1)* [- z0 I7 J" z
  36.         {
    " ^' y  E; Q; H
  37.                 printf("send: ");9 h( W- L) V, h* q/ W
  38.                 fgets(buf, sizeof(buf), stdin);. v1 e6 N1 S6 i" ?6 j5 u, @
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ( V- m, J; S" H  l
  40.                 if (ret < 0)
    3 X+ S9 r2 y9 C. q
  41.                 {
    8 j4 B$ |" ^) N) l% j$ `
  42.                         perror("write");. F6 k7 t& k  P% u5 E
  43.                         break;
    . g, D3 [8 l& y+ _$ @2 N1 b! z
  44.                 }
      i; S0 f% G; B( b1 w: s
  45.                 if (strncmp(buf, "quit", 4) == 0)
    ) `) `1 Y! J* X. W
  46.                         break;# D$ O# h# L( p# Z( Z1 u
  47.         }! W4 Z! g! a% O7 `
  48.         close(sockfd);
    $ S4 T$ p9 h# D: ~
  49.         return 0;
    * b6 Z1 X& P2 J' W% s/ `3 ^/ a
  50. }
复制代码

' n- M$ w8 j* g/ s6 Y8 X% a( O, |: H$ A' z0 f2 i9 }8 o
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-17 20:25 , Processed in 0.128790 second(s), 24 queries .

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