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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
+ k) u8 v! N# J0 [0 c
4 X6 a3 r0 ~6 w+ x  Q. n1 x! J
% @! f( g# u+ W. e# w
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
; A: r# u4 M" e7 @) `. c3 E
+ x& y: J7 m! _% v
/ d1 M9 N$ ?& P2 D- @* f
TCP协议4 `# K" N* t( ?5 m
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
* I2 g* w/ k/ W5 |2 E5 w( e3 V* n! k  W! b
" `. c7 D7 i" m. o  c
关键词:三次握手,可靠,基于字节流。
8 F1 M7 v7 s7 K, o7 l" X
& W  q) n8 @, `+ O% E. ]4 ]/ [8 r% r& m

9 k% I' ^, q! H! B' l9 r可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。, J  d; I  Y: O
& c" `7 G0 ~- E; i" ?( y
TCP服务器端和客户端的运行流程1 V& F0 p: |" f5 u5 U
/ p6 `' D9 m" v9 x
) P, ?8 u) l" [5 i) w& d- u
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
, o$ P) A6 U) g* x4 k6 Y; s  c! C. `5 {4 A4 Z9 x  f$ g( a8 ^
$ r3 o3 m) R/ x. m. B$ d% o
1.创建socket, @* y' M9 Y; Y; B0 C
socket是一个结构体,被创建在内核中
4 C" P  |6 V9 w0 G sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议8 u6 C6 O' j2 u8 R. X0 o. d
9 A1 Y! T& S3 W: V

* W& \3 n& N( k5 S3 `2.调用bind函数& o5 b' q4 Q  e( ~" N! r
将socket和地址(包括ip、port)绑定。. B; K  [; ~& T  J9 i& d+ |
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序" ~  S; o# `+ F# ~* d5 x
struct sockaddr_in myaddr; //地址结构体
! E  C/ q% N" X, o- v" \; d bind函数( P; w& K' W) r( `- a5 K
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
2 ^4 w: u2 R, @9 i! G2 j  W& T$ }3 Y0 i$ T' J! Y2 W' R

; F" e. T+ e0 l4 a9 G3.listen监听,将接收到的客户端连接放入队列
% K, j" P; b, }+ Z$ I: H+ V listen(sockfd,8) //第二个参数是队列长度5 c; [6 Z0 u+ A  c1 w$ B/ O

5 N' n! W; r: U

5 r$ P& X5 [4 E/ x0 I; y1 U2 g4.调用accept函数,从队列获取请求,返回socket描 述符
0 h7 H9 @) J: l( A3 H, g# e  如果无请求,将会阻塞,直到获得连接4 W2 Q2 [# w5 l: ~
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
5 `0 e; w3 c+ i7 r
: B& ]5 I6 k' k
) s+ E  @2 @$ O. ]0 U
5.调用read/write进行双向通信
0 g6 U# ^; M9 q' i7 q& X2 j. R' r0 T* X; H: r& k* G* b( h

" Y  J. r( m: w  {6.关闭accept返回的socket4 g) h" F  @& j1 i7 V
  close(scokfd);: Z+ R8 y+ o- q; ?/ w4 c' I

) [0 n5 D8 {0 Z

4 S6 k, k# S2 z! d1 k4 h' j( f$ i8 _/ f
& A) u+ E8 u6 {& [

  [: ]. U1 F4 Z0 Z' W6 U下面放出完整代码
4 r4 y) y4 V# P, C) {# V8 s) f2 C9 J* |
  1. /*服务器*/
    9 m7 x9 X! ~: X* }
  2. #include <stdio.h>
    ( {, f% e5 o. ^7 Y
  3. #include <string.h>
    / W: Q5 J. a+ D1 q/ ^7 f9 G' Y0 V
  4. #include <stdlib.h>' }7 F% O# ?2 t% E' W
  5. #include <strings.h>
    7 W, T* ^# U1 d# U$ A% d+ j1 E5 J
  6. #include <sys/types.h>' i4 m) Y" v5 b' ?3 E# n! h2 p: w
  7. #include <sys/socket.h>
    7 y3 `" }2 @( z
  8. #include <arpa/inet.h>
    / ~/ ^, b2 w/ g; _  w7 i
  9. #include <netinet/in.h>+ Y& w+ }6 P- m
  10. int main()
    $ D' Z2 |. S2 q
  11. {0 c# t' A! q9 V+ E* k
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字/ J3 ~1 A, z: }: _( M" C
  13.         if (sockfd < 0)# k7 B+ n  ]7 m# h( B( Y
  14.         {
    5 |. e' |5 U- X, Z+ O* p7 v
  15.                 perror("socket");' F" n8 @& X+ R  \1 f/ C+ [  P4 J& y
  16.                 return -1;! t" d+ A2 S) `. {2 O
  17.         } //创建失败的错误处理: j7 ^4 e5 U7 }" k6 e
  18.          printf("socket..............
    ; k& ~2 D5 `0 M
  19. "); //成功则打印“socket。。。。”) c; I8 m, y+ b7 s/ f0 @1 R2 d
  20.          9 V( E( @0 O+ V
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体$ i+ ?% H* O3 c
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    / @9 B7 D) O2 p  U
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    2 U) D; U/ A1 d4 m4 y& `8 O, V
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ) P( l- t1 g3 A- {- `
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址3 J' r2 I; }: a1 K  M
  26. 1 M6 l! @1 T5 d
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 k6 o" ]6 V8 ^
  28.          {4 h" Z6 Y: a* j( q8 |5 x& D/ K4 W
  29.                  perror("bind");
      K4 H$ F: d/ K$ b" m9 i
  30.                  return -1;# ~8 d/ K8 I! I1 S! ?& O! {: |
  31.          }
    3 {0 g% v3 E  w# d+ A
  32.          printf("bind..........
    " l! s" g4 R2 A& n9 |: I$ B8 Q
  33. ");5 Z% d; Y( G3 s. [2 w7 W

  34. 9 G2 Y7 @; N0 E0 a0 T' X+ V- I
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    * F& a2 H' }" k* v9 g
  36.          {
    0 t1 F$ @& b" h" I* l6 C! @5 f1 g& K
  37.                  perror("listen");# |: \$ W7 L' i1 l
  38.                  return -1;! H5 X3 u1 r- G- V5 }$ M$ b
  39.          }' P6 A5 t7 Y) |% k
  40.          printf("listen............
    ( Z* N0 J3 Z5 n1 a
  41. ");: J' P: h7 t) u: G5 q7 ]* m$ p3 O9 }
  42.          
    - ^# \+ }. F1 H- T: [. }
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求& |7 n! S, B' X
  44.         if (connfd < 0)
    7 W& ]4 L& A& _2 v
  45.         {
      Q. x& j0 F  a
  46.                 perror("accept");
    8 F1 L& o8 x) u9 R/ \% [+ F: S
  47.                 return -1;
    ( }" B4 W; I% F; @
  48.         }& f5 [) u" x- h, N$ A* H# S" n$ Q. w
  49.         printf("accept..............
    3 ^. o& C4 l# {2 @; g9 d& L7 P
  50. ");
    5 N" b0 F4 [  f2 ?$ i1 q3 O
  51.         char buf[100];//定义一个数组用来存储接收到的数据  I( }1 C9 |9 {
  52.         int ret;, N0 @7 A; t) A* F9 x( V+ n
  53.         while (1)
    2 b/ f! H9 M/ B2 j# B' C) l+ T0 B* P
  54.         {2 U! a* s/ X3 E: B" l  K
  55.                 memset(buf, 0, sizeof(buf));: z2 E, ]* Q2 E+ I( H
  56.                 ret = read(connfd, buf, sizeof(buf));
    3 i. S) L( L2 D" Y; c. J1 q9 j- E
  57.                 if (0 > ret)# E% L2 ?0 z) x% R$ |
  58.                 {: \. ?5 \2 M/ J
  59.                         perror("read");
    8 y" {# N. E+ Q1 k4 T$ A7 n3 w
  60.                         break;5 R2 _- G* U5 H" A6 e/ f6 j
  61.                 }//执行while循环读取数据,当
    % v' Y0 R. Q: g& v
  62.                 else if (0 == ret)
    # T+ Y( v% p' h, F
  63.                 {; x- F0 V+ q: G+ Z
  64.                         printf("write close!% o3 l5 F# l- R: c
  65. ");0 K6 X: P) o7 N+ v0 \& R
  66.                         break;  }# ~! N# r0 h/ N2 z# y! t/ T
  67.                 }
    - R; o* D/ `: j  @# g3 X. u
  68.                 printf("recv: ");
    8 i; u) j) ]! g$ m, p# X6 M
  69.                 fputs(buf, stdout);//打印接收到的数据
    - H% e$ Q  S1 Y8 i, K, `; p
  70.         }
    & e5 S! @1 T8 v  G* U4 K
  71.         close(sockfd);//关闭套接字& a$ x0 b0 v. K$ ^$ R4 }
  72.         close(connfd);//断开连接# c# G. e- v' N
  73.         return 0;* ^: k# C; ]* V3 O& ^  l- m* Q6 l1 V
  74. }
复制代码

: Q; p5 r! \  \6 A, B  M5 f8 r! x3 V3 j1 Z1 Y/ }& v
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    $ U8 Q" x4 D+ a
  2. #include <stdio.h>5 r& Y$ X# A# n2 D2 u
  3. #include <string.h>% k& p4 l2 V% Y& Y, j- W
  4. #include <stdlib.h>, d8 ?/ T, N. Y: n0 r# y3 X
  5. #include <strings.h>
    - k- F5 S3 e1 b7 b7 o" c
  6. #include <sys/types.h>
    0 S; b) X  z0 V+ i+ X
  7. #include <sys/socket.h>9 h6 {7 y: H' v% \" ]: M+ Q
  8. #include <netinet/in.h>
    ! e4 p8 F% `6 L
  9. #include <arpa/inet.h>4 Q% p' r8 x0 b: S- O
  10. int main()6 H# s$ [0 e8 f; M' d
  11. {
    ' L0 U! ~9 K6 C+ g5 s
  12. int sockfd;! Z/ u7 i6 T. O" G0 T0 D' S/ V& l
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))6 _2 @! [; L9 g6 j4 j
  14.         {/ h( }) }5 s: q8 P. F+ ~
  15.                 perror("socket");
    1 r! E6 t( V: u6 l6 P( y
  16.                 return -1;
    % U$ P! T' b) [& A+ c
  17.         }
    6 H& p, u; v9 p4 y
  18.         printf("socket............ }. G9 a/ A  E! ]6 `- H+ Y
  19. ");7 v. A& V1 }2 v* h! o
  20.         
    # |8 J7 N  a+ o3 Q+ z
  21.         struct sockaddr_in srv_addr;5 v) I6 o* |& `" a7 Z; s5 @
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    * C; I9 n& b6 p, t0 V# N
  23.         srv_addr.sin_family                 = AF_INET;
    7 _( s% B) Q0 w; b
  24.         srv_addr.sin_port                         = htons(8888);
    & A1 @6 f8 Z' T9 y, e* J
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");$ ?; w3 T! B5 C1 P4 F" P. c0 G
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    2 i" A, O5 m% r5 K- K4 o
  27.         {
    , j/ F. u3 }8 [$ l+ B! c
  28.                 perror("connect");# ~0 N8 Z- z9 I3 i* T
  29.                 return -1; //exit //pthread_exit! m9 o" x$ I3 s, W: [3 u; {
  30.         }( ?0 k  F1 G# S2 ~' g
  31.         printf("connect..............% L: t' Z: k8 C  I
  32. ");/ e! v1 X8 _  D* \$ Q
  33.         char buf[100];
    ) v2 o- ~$ j! {7 g: e
  34.         int ret;4 p# y8 l) S6 p; j8 X
  35.         while (1)3 b! ^- `+ L* |0 I- Q7 }# X1 B, v& T
  36.         {; P4 T4 o6 z4 l0 ^
  37.                 printf("send: ");
    ; d2 r& ]0 e- m2 ~
  38.                 fgets(buf, sizeof(buf), stdin);
    6 L9 e* }( i( Z/ O5 F
  39.                 ret = write(sockfd, buf, sizeof(buf));
    % N2 {6 {3 q! c4 t2 J3 d
  40.                 if (ret < 0)
    ' x6 L* ~) _+ @# |( [9 F) S
  41.                 {
      z: T' I( A6 e0 a# P
  42.                         perror("write");
    # p1 a- L* k8 f% J
  43.                         break;( N% \5 N% A* e# g9 ?
  44.                 }" Q  s/ m$ G: O( t- A# A
  45.                 if (strncmp(buf, "quit", 4) == 0)
    & k/ H6 f  {7 q/ B! x0 l$ y9 g* @$ b
  46.                         break;
    + P# B0 r! o" L) a; j( C
  47.         }
    6 R4 m1 L+ q& X! ?/ x
  48.         close(sockfd);& l" n, T& z+ g# [; [: Y- P& v6 U
  49.         return 0;
    : t( |+ \9 N3 ~" x  J; C. j, Z
  50. }
复制代码
; [$ g+ W& c5 F  Y0 r
% O1 q/ b% K/ @5 K- B
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 00:17 , Processed in 0.064132 second(s), 23 queries .

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