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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
9 d) Z6 @+ x% }" k3 n8 J* ~. ?8 ]) Y8 Q
+ A" G) ]7 n' n3 s7 c' N2 ]. U; l
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
' e3 ~2 @- B' @  V; f5 ~! l7 t0 |
* D: W; ~4 B1 y6 S1 C- k, O8 r
2 L: g5 [; t4 l4 k; l1 m
TCP协议2 P. p6 ~3 b; H+ |8 ^9 r
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- T, z9 `% n0 R# W

: W+ v5 ~7 w% D) b3 w

/ K5 _- s9 u% `# j9 @! J+ T关键词:三次握手,可靠,基于字节流。) O- y! d/ u; V" P# I

' q/ ~* N  \: ^
1 m$ c) N4 L( e6 v; G
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
0 l% _! h% O" R7 _; i! ^* l # u( J9 @& F, T/ V0 Y+ g3 H3 J* ?2 L
TCP服务器端和客户端的运行流程
" I5 c; d$ W- H3 C* m/ I+ G" N/ K5 B' A9 a2 W7 ~8 D
  X# K) s; }9 Z
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?# S' m2 V4 x) k# h( a$ V$ t

; H3 @0 x% X9 v7 H) ^9 \9 @
9 Y, b1 x3 ~/ x! p* O8 I' P1 E
1.创建socket6 ^4 _) v& M& u4 u1 v0 H& r' x
socket是一个结构体,被创建在内核中3 R. P) ^" b. L- H3 Q9 w
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 y# c" L1 I- b8 u/ y) D& R  \) c& u% B9 T) `$ [* f
( V+ ~' u* }  |: |. }  {
2.调用bind函数% p; b8 V4 \( s; ~0 e
将socket和地址(包括ip、port)绑定。8 u0 g7 R1 E8 H& ^: D
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
# f( b( R: X7 [# M" T/ ^# c( ~ struct sockaddr_in myaddr; //地址结构体
1 G/ _/ h4 Z1 U! d/ t* O* f bind函数: t) j6 f$ Z" n/ p' Y2 t
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
! o9 ^) x! l7 S' Y: H. ]0 G1 ^% ]- a7 L1 m, p
* h+ S8 R' @! @7 n% T8 |) X
3.listen监听,将接收到的客户端连接放入队列
0 t; f2 q! N8 [& V/ F listen(sockfd,8) //第二个参数是队列长度
; I! h# ~" q: e" W' d( R
  A7 Y  E- @; M; M
6 J; p9 ^; I: t# \8 R9 K0 u
4.调用accept函数,从队列获取请求,返回socket描 述符
+ Y# |3 h2 {. R8 r9 }2 e# ?  如果无请求,将会阻塞,直到获得连接
) W8 V& L( f9 H) g: k  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
/ R3 s- [2 c. W  K: h: F
  S2 W' A' {" p1 W3 `& {

, m3 @% y! j1 R/ I' G# M& s5.调用read/write进行双向通信
" f1 H2 {" ~3 y* f- ~3 F( w
& |' {* m) S2 J" Z  e
. `" t$ E0 N; ?+ Z& A# ~
6.关闭accept返回的socket
8 H; x/ j9 m+ ~% D  close(scokfd);
6 Z2 E9 V2 @3 |% X, `
' P5 b& u) {; }& ~1 }

+ \' Z2 R1 |6 k- |; w
1 w+ |  @' e) K# W
* F, u% C* W$ }8 r' h! c& V
下面放出完整代码
- z- U9 D/ _% s! G& I4 K" @% x5 ], Z# r! `- w0 g# M
  1. /*服务器*/
    6 {* P' g" z, A  T  q
  2. #include <stdio.h>
    $ g9 v; i' x2 Y! {
  3. #include <string.h>
    , D& J5 ?( n0 d" C- w
  4. #include <stdlib.h>
    . V9 v; {* O( D4 ~& h0 m
  5. #include <strings.h>: Q# S# Q3 ?5 N" [% h# p+ M
  6. #include <sys/types.h>) f, P9 W3 j" j
  7. #include <sys/socket.h>
    ( h' r! x7 G! u8 _: g
  8. #include <arpa/inet.h>: }0 w8 ~6 [3 v$ t1 k3 d4 @
  9. #include <netinet/in.h>
    , g: ]. p! h2 o$ ~0 J; |2 r
  10. int main()! L9 ]1 J/ p/ }$ [- M
  11. {
    ' @+ \0 i3 T0 B5 U) b9 x
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    : e( @/ o; y; y* t' R9 h
  13.         if (sockfd < 0)
    2 M1 `6 |) \! q* O7 Z6 q3 o
  14.         {. B' o8 L+ r7 g3 B( u' R
  15.                 perror("socket");
    5 S+ t) X' c0 k
  16.                 return -1;$ A6 _+ i  O) z
  17.         } //创建失败的错误处理
    ; a8 `/ C; @9 u9 u% H/ k4 F
  18.          printf("socket..............
    / ]- ?+ f/ t8 Z, J7 j/ x) X, l
  19. "); //成功则打印“socket。。。。”9 P( r. E% `: M7 @# L7 x
  20.          
    & k& p6 M2 Y* f) d3 G
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体0 Q/ ?; x) W, ^% }/ v
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)8 n+ b7 d3 b8 K8 F
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ' ?8 T2 V' z7 o: o. F9 S7 }+ z$ z
  24.          myaddr.sin_port                 = htons(8888); //选择端口号$ A8 R1 _) @% v# r
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址# j8 ^. \3 x% S! J
  26. * R) k1 b) w, x4 o& d* O0 Q0 y* K
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字0 X" d6 V) b6 p# l4 a* x$ P
  28.          {
    / C6 f# ^! D0 z. |; G' |8 }- {
  29.                  perror("bind");* ]1 D7 ~) x0 H6 Q: r
  30.                  return -1;" b" f4 b- Q( S% K4 R7 B4 H* e4 N. ~& b
  31.          }
    ; s* l% g( h' U
  32.          printf("bind..........8 R6 J; y6 Z6 J( Z% q
  33. ");( F) {* w+ \- t. Y% ], T0 s& T/ O
  34.   M( {- N  |0 d( n! W  j4 n7 V# L
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    3 @5 B% \5 V) y( t- c# N
  36.          {1 Y6 n. k, w( L+ A# q  S: B
  37.                  perror("listen");
    * c9 g3 ]! S* g% W0 ~
  38.                  return -1;
    3 H2 \* c) X7 s5 {! w) E7 O
  39.          }
    . g! o, V0 ~3 d4 g& D3 q* y
  40.          printf("listen............/ k( w0 W- ?: n+ p7 V
  41. ");- K+ @% S" n3 U2 m4 p5 A$ i
  42.          / B. Y6 }( [: A/ J
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ' }- P3 t: d, f
  44.         if (connfd < 0)
      M4 v- x3 M2 s; ?. R# T
  45.         {1 |) G( _% S& R2 y
  46.                 perror("accept");
    8 f5 q# L7 K7 B' F8 j
  47.                 return -1;& z, Z  o$ W" S! a' L5 b
  48.         }+ [0 q1 T% Y4 \# k# I5 C) j' X
  49.         printf("accept..............
    9 ]: V' t+ n0 s# t: K; @8 p
  50. ");
    + F1 B# T) Q" j/ B8 n
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    ! ?  {) i3 |& T, r; x/ k' T* T
  52.         int ret;: @! y- [; R7 {( ]$ T( p
  53.         while (1)# f( L0 m" p2 l4 {
  54.         {
    1 c+ a* {% S3 |) ?
  55.                 memset(buf, 0, sizeof(buf));
    6 D) Y( I4 H: ?6 O; t5 d4 {5 A) H+ c
  56.                 ret = read(connfd, buf, sizeof(buf));
    - g! v# i/ s, n3 C# i
  57.                 if (0 > ret)6 |% h/ K5 R4 x  D9 _0 J
  58.                 {
    ( ~- Z/ j; y1 }+ Z
  59.                         perror("read");
    " ?5 B/ l) N  i+ @+ o3 q
  60.                         break;
    2 ^) ^% l! F% u# o) W1 A
  61.                 }//执行while循环读取数据,当$ J' Z1 r9 u. |/ z
  62.                 else if (0 == ret)- s/ B* Z, k, G- m+ M
  63.                 {& \6 L+ C' f8 d' W1 @! C5 k  T& j
  64.                         printf("write close!3 i3 T0 n# c. m# d
  65. ");- `  d4 ?3 l- M5 n; |
  66.                         break;6 L; v# v4 D8 x( e9 r
  67.                 }  M; A+ U3 v7 n, w
  68.                 printf("recv: ");
    8 g+ Z6 H6 ]5 @8 ]# {( O
  69.                 fputs(buf, stdout);//打印接收到的数据
    8 |1 V; K2 v2 h
  70.         }) Y8 f; m7 B8 N4 f: B2 k2 K
  71.         close(sockfd);//关闭套接字
      [$ W0 F( E) n
  72.         close(connfd);//断开连接
    8 b* d. ~; O7 x
  73.         return 0;
    0 S2 }- R1 s/ i+ M4 r0 Y
  74. }
复制代码

, r, s3 C* m1 f8 O" v* y5 U
% }$ \- V; y) |
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)! b" O$ w2 Y9 |5 L& A9 \( |6 ^2 f. c
  2. #include <stdio.h>! c4 C; }* N9 V0 z8 I
  3. #include <string.h>
    2 t0 I6 {7 e5 n
  4. #include <stdlib.h>
    / R5 M9 A" v$ y0 N
  5. #include <strings.h>
    6 |- c4 z8 z4 U4 c! R2 W; ?
  6. #include <sys/types.h>/ C  c6 Q2 p: J$ Q/ z% Z/ q% S
  7. #include <sys/socket.h>/ }* C8 }) g$ F5 @! F; d3 I6 Y
  8. #include <netinet/in.h>
    . U( f! P. t- D. ?& S/ ]6 h/ _
  9. #include <arpa/inet.h>6 c  [: D. {6 e9 B* `- R
  10. int main()
    / `4 H9 J/ ]* ?4 q
  11. {# i* I! P+ ~3 B' j0 I
  12. int sockfd;; K! e; E" u& y; u3 z+ ]
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))! J, @- h  S" R5 N0 M8 k1 G' G3 O
  14.         {( |' e% M3 E# r, H4 |
  15.                 perror("socket");
    * N; i4 O2 V* |3 F$ W/ S" X
  16.                 return -1;
    9 K# Z  T' F1 `. Q9 H
  17.         }) e7 R( B' b6 ^' [& z7 g
  18.         printf("socket...........: c& S% ~6 e, o3 y% t+ F6 j1 m
  19. ");# A1 T+ q, `1 I! {2 E
  20.         # v2 T  J6 I# d; ?
  21.         struct sockaddr_in srv_addr;, e/ P4 i; y0 l0 A8 V
  22.         memset(&srv_addr, 0, sizeof(srv_addr));! {' Z! ^5 v: h7 d
  23.         srv_addr.sin_family                 = AF_INET;
    8 {2 U' ~5 ^0 R+ P$ D% B4 \6 m
  24.         srv_addr.sin_port                         = htons(8888);
    4 R) f+ T$ G$ o9 \9 f( x
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    . L& X8 E7 F( z( v/ o
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    7 |- q6 ]& A, u& e0 F
  27.         {9 C8 ~& H* O' F
  28.                 perror("connect");) T8 E, A' _  ]3 O
  29.                 return -1; //exit //pthread_exit
    $ }- L; Z- Z( i7 N3 [
  30.         }
    $ l9 ]. T& S+ r9 j* G) a. F
  31.         printf("connect..............0 a/ L& j( K) g& R, o
  32. ");/ M8 R( O, n) S. e
  33.         char buf[100];
    / d  c0 c6 p4 [5 z
  34.         int ret;8 s' P( d3 z5 R& t* a+ T
  35.         while (1)
    , w8 ?1 o5 _. W- @- `5 y+ h
  36.         {; F+ Z7 K* b9 m( j4 G
  37.                 printf("send: ");( P; e5 j2 V9 D3 K$ }5 f
  38.                 fgets(buf, sizeof(buf), stdin);' h; j: d0 y: m: c2 E
  39.                 ret = write(sockfd, buf, sizeof(buf));( }5 C0 g, j" `3 S
  40.                 if (ret < 0)  \, e" H8 B5 I3 ^, ~
  41.                 {
    4 ~' H% |% o- z1 v7 g
  42.                         perror("write");  a( d$ q( q  d' {
  43.                         break;4 j7 g# m  b4 _9 o9 E
  44.                 }9 e8 u8 e7 D9 w8 k( Y$ }
  45.                 if (strncmp(buf, "quit", 4) == 0)) i- j( o" s8 u) X
  46.                         break;) a, o9 L" ?7 ^* [8 R* g! x
  47.         }
    1 S' \& Y/ B& N/ ^2 S* L$ n
  48.         close(sockfd);
    0 _' o6 w. W5 Y/ I
  49.         return 0;
    + e; L! p6 y: ]+ s  T0 w
  50. }
复制代码
( n. Q; ?# N: s5 C4 i; z3 z

, k  u4 S. s3 I: ?" Q1 \
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-20 03:36 , Processed in 0.133889 second(s), 22 queries .

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