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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
; R  _* B, B5 Z* o% L$ v  V4 f# H7 [2 W. j
" L7 y, p4 ]& X* J5 z
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
# _, g) J# i/ C9 F; Z- x/ N
% ^6 q; E: z- G; \, x; x' g2 Q
* U5 j7 @4 K) g- O1 u
TCP协议
+ c! s, q6 y$ ]0 |1 W3 NTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。- @  E( l3 f/ Y$ i# T* j3 T
- J( A& `' c, K8 {

$ T8 O6 k' K0 D6 ^关键词:三次握手,可靠,基于字节流。3 ^8 Q. s& \3 `/ X
0 {, v  N& ]. ]' j

" `$ F5 g! x% w* Y  G. x可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
8 A9 Z, V5 Z' r" M# @ - Q' y* l* N7 h! I- Z9 g
TCP服务器端和客户端的运行流程3 Z% w: L0 D) x' y) k
, v% L2 w3 r9 l  i: ?$ t

8 l  y+ m6 _( z7 m. [1 B5 x9 i9 a. p如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
: x7 T; k7 j" ^1 _. A# ]" L4 a$ f
/ n% T* T) X: v6 h
1.创建socket
0 i- y7 U, e# B socket是一个结构体,被创建在内核中
4 x. d  T& q8 \8 p6 ?  p sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& o+ }( c9 U" w# y+ s+ X/ s% \! R5 z2 [, S7 n$ |
# c" q' h0 e% ]' a2 m
2.调用bind函数* G" K; ]5 L- V/ j. ~8 j) k" m$ v# X) f+ v
将socket和地址(包括ip、port)绑定。
6 G$ y# s4 T7 o/ G 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
- ]7 _  U6 |6 H" ` struct sockaddr_in myaddr; //地址结构体
5 A, H# ?; p! p" Q& N( ]( ^% [ bind函数
, Z* }! D& }1 D+ y# Z9 n; v! _% m- A5 l bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
8 [0 l+ V0 i+ `7 f& m5 x7 ~  v( e5 n3 }( K. J

3 Q7 n6 i, Z7 \' y# @3.listen监听,将接收到的客户端连接放入队列" l# s* g: }# O; G# H' I
listen(sockfd,8) //第二个参数是队列长度4 M7 Y# B# u  f! J0 E" L

2 S, k: M% Q1 L* z

/ l% F# \* r, o8 {3 M+ c! {/ u+ Q" h4.调用accept函数,从队列获取请求,返回socket描 述符
0 O% z' Y; D5 X3 G; P, `  如果无请求,将会阻塞,直到获得连接) I' J2 ?- F6 t  R+ |
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数4 P+ j6 c% C8 D5 N/ Q% `; |
* h# V( H* u- B! ?, R

  o9 B; H1 V9 M. K/ w& T7 e8 l1 S8 Z5.调用read/write进行双向通信
" Z! i2 y" H* _3 p  ?( D8 t" H! H

5 T9 _3 p6 _/ u" Z/ a3 s" Q6.关闭accept返回的socket* X0 ~4 z' c& q0 H+ ?$ ^, L5 H+ O2 ~
  close(scokfd);0 j8 P1 @7 g; A7 I
& h5 e2 \; |$ R  B

/ }$ M1 H9 ?# ~3 f) ]  E9 `( L+ f/ f/ t$ l2 g8 |9 c

" g- m7 C* T8 Q下面放出完整代码6 ]7 u, Q  D, C7 ~# ~

9 f9 I9 R; m% }. a! Q9 t
  1. /*服务器*/
    ( U& T4 O# u3 s0 _
  2. #include <stdio.h>' M: [/ l3 c1 u4 V. @
  3. #include <string.h>
    5 O* g' |9 v% n; l3 i5 J5 H3 G
  4. #include <stdlib.h>) l  [- p/ r* F5 F, `2 ~7 G- Z
  5. #include <strings.h>
    ; g, h; \. k( k% \$ w7 H, ]
  6. #include <sys/types.h>( W3 ]5 Q9 y- T4 N+ y1 W0 D4 S
  7. #include <sys/socket.h>/ t2 [3 K3 e3 _' _
  8. #include <arpa/inet.h>
    * q4 ]3 a) f. G6 }
  9. #include <netinet/in.h>
    - M: P2 I- I7 C, Q% w3 t
  10. int main()/ W* d7 _0 z" G; Z0 D: {- f
  11. {
    5 y" h5 d, \$ J1 m, g( }
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字# y4 {  [. }- P4 ]
  13.         if (sockfd < 0)# i1 D; ?4 F; ]; `
  14.         {
    # G: g4 t0 Y5 W& o* i2 s
  15.                 perror("socket");
    ) q$ i6 C! v* Y: W  I8 r
  16.                 return -1;% k2 Z+ b7 p: Q7 w' }7 I# {* G+ w
  17.         } //创建失败的错误处理6 U/ b/ T$ b3 L% t
  18.          printf("socket..............
    2 \( X0 ~* |* F' Q
  19. "); //成功则打印“socket。。。。”
    - ~7 }1 J  `( L' F1 y
  20.          ! ^- w, ~( k0 u( n8 r; N
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体& N: K" s9 |% n/ }& o9 q
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)) ?2 \' e, P' E; e6 L
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型% B6 ?. \; C. b' o7 a0 L$ i
  24.          myaddr.sin_port                 = htons(8888); //选择端口号0 I2 x) k( B2 n3 Z. W: j$ Z
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址4 ^. x7 O/ }9 s
  26. 7 j* m) L+ J, w8 R( ?) k
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ' q1 \/ G- T' j3 N% h4 s( `
  28.          {
    7 C( y5 q" t" C
  29.                  perror("bind");' g; A8 i# a: V6 s( Z9 \! G( b
  30.                  return -1;
    1 t2 U1 |$ @) s7 N; s) i
  31.          }/ `3 U7 W) ]( \
  32.          printf("bind..........
    0 Z1 g) @5 A* A0 \+ y6 V
  33. ");
    " M6 U+ H4 t8 y8 U
  34. : F& }' n  n7 O4 ~7 |
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听0 o" ^' U0 A+ g2 L' Y
  36.          {6 x: C) P6 U0 s6 J$ I$ h0 T
  37.                  perror("listen");, O( l' y% o9 \: P
  38.                  return -1;" m* x$ a( \5 `" M0 ]+ T8 i
  39.          }
    * B" s% `  _0 A- C
  40.          printf("listen............& R0 `% _, u$ B) c7 q9 o) O
  41. ");8 Z+ }6 h: I6 u# U
  42.          
    % b- U, j6 t5 W" U
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ' v2 h  z4 S/ V, S' W: X
  44.         if (connfd < 0); S' k+ h9 Y# X: |; [  b! C# ?# A
  45.         {/ l( B6 ^5 M# q$ o+ c
  46.                 perror("accept");
    , O0 V. I5 k" S% Z% r+ h
  47.                 return -1;
    " G. p7 E% {2 E8 u+ C$ z
  48.         }
    . A$ V. J& N- s3 f' i: m6 X: x) a
  49.         printf("accept..............
    $ k& c5 c4 X& _/ i9 }* g
  50. ");
    - n! x* {! ]6 i* {+ s
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    6 j. j1 F+ f' T
  52.         int ret;! F- C- Z7 ~1 \2 `2 i9 }
  53.         while (1)
    3 y" i* X, q6 ]! }/ b0 F6 {* m
  54.         {8 o9 D# q9 x& r( j
  55.                 memset(buf, 0, sizeof(buf));
    * U: K6 E7 G$ g' ?# X& K$ R  x8 U
  56.                 ret = read(connfd, buf, sizeof(buf));! m7 Q8 v  q8 s; a% A
  57.                 if (0 > ret); G. P, k" r7 b* u7 j" J" h
  58.                 {; [) |, p+ c7 h4 o/ A5 r
  59.                         perror("read");
    ! s; Z) c" D+ b3 q
  60.                         break;
    " P1 X) F1 Z. W5 U0 B7 w1 C
  61.                 }//执行while循环读取数据,当# z% L3 S( y( w: C
  62.                 else if (0 == ret)
    9 [( l* a9 e4 @" E3 N6 @
  63.                 {
    4 `1 B- T( c/ H/ Z2 Y
  64.                         printf("write close!3 X# f+ @0 b) c( d7 Y2 l( P! p- H
  65. ");
    6 ~1 r4 g$ f2 _- Y" {& G  w& i
  66.                         break;
    " t: t9 |0 }$ f
  67.                 }
    8 U, B- C) k  y) q5 J0 ^2 X+ g
  68.                 printf("recv: ");
    ! k6 i6 G$ [/ w# ?4 w1 p
  69.                 fputs(buf, stdout);//打印接收到的数据
    ) C! ^7 a2 s- i8 s1 e+ H, S; M
  70.         }
    ( p5 H: I4 T. N7 @5 Y
  71.         close(sockfd);//关闭套接字
    7 S& ^# \+ i: U
  72.         close(connfd);//断开连接. z: @3 T2 U& x$ Z- V2 w
  73.         return 0;
    2 d" y  w; i: ~. ~) x! E/ H
  74. }
复制代码
( Y$ k% ]5 H# Z/ v4 G3 a

) K; h$ r! t! W+ s0 d! [4 H7 g4 _2 M4 w3 H
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)# A' B8 m% _  t6 z8 J7 }! P
  2. #include <stdio.h>
    5 Z$ ]( A! v/ _9 ~7 d2 O
  3. #include <string.h>' y& ^( c) M& b( k9 N% o
  4. #include <stdlib.h>
    3 Q5 k) A- v' N) u- C6 T
  5. #include <strings.h>, o- N! {5 V; ^$ G
  6. #include <sys/types.h>
    * y7 o& n" o7 I& z/ J
  7. #include <sys/socket.h>7 t% s" D& f2 s& }8 z
  8. #include <netinet/in.h>* D/ i. k' P0 D8 k9 I) T2 B, q/ r. q
  9. #include <arpa/inet.h>
    # y$ W) f1 `# I" a' B9 n* o" l! d
  10. int main()8 z. e; @5 z# W  e0 d* z
  11. {
    7 q9 @6 b4 E3 X
  12. int sockfd;  i& l, T: ^7 }) t. `: f6 x
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    " M. R( \( e, k. r( t9 D
  14.         {
    0 @& \6 B3 k2 i, I. r1 l9 L
  15.                 perror("socket");) \4 G8 A6 a& F: H) D8 u
  16.                 return -1;
    . W4 c0 H; M9 h8 [8 w0 D  o% Q' _4 u
  17.         }
    3 E3 ]1 o$ X1 \  Y( ^! R
  18.         printf("socket..........." x# G5 [' g4 M
  19. ");) Q" L4 H9 T" Q
  20.         : R8 r; F+ Q0 k  j: x9 N6 D, }
  21.         struct sockaddr_in srv_addr;% Q8 M6 n7 H2 j* g  M& Y% G8 W
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    9 y! ?% M+ O9 [% l  k
  23.         srv_addr.sin_family                 = AF_INET;
    ' s# J% K0 N2 X5 H* K4 B
  24.         srv_addr.sin_port                         = htons(8888);/ e* y; g0 m2 Q2 q
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");5 J$ S: v* Y2 }0 j
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    0 m& ?0 r  {  n7 f: i* C
  27.         {
    + ]2 t. _" B. ]/ ]0 p0 t2 {: e5 v
  28.                 perror("connect");( ?. p$ U+ e4 R% @6 \( y# {
  29.                 return -1; //exit //pthread_exit
    * b& s: M. i* i1 U
  30.         }
    1 d! \6 }# h2 E$ Y
  31.         printf("connect..............
    ! G$ Z, G( U2 I, s0 V/ m
  32. ");4 T( h: k/ p2 [
  33.         char buf[100];
    . N3 |: c: r+ d- F, X2 Y
  34.         int ret;
    7 z+ F9 L7 Z/ ^& o0 m
  35.         while (1)+ q  u7 G4 B/ f$ a% {6 y
  36.         {& M. K- Y9 W: |; h' R
  37.                 printf("send: ");+ X* C  c6 J* H: r" A" }: |
  38.                 fgets(buf, sizeof(buf), stdin);* e1 T0 w! K9 v( ?1 R
  39.                 ret = write(sockfd, buf, sizeof(buf));
    / O1 j5 f9 P# b. }) H( ~3 E
  40.                 if (ret < 0)
    5 G' p+ [$ \# K
  41.                 {
    3 y" D* S1 ]( D1 O
  42.                         perror("write");' D( N, l% E& O5 {# J
  43.                         break;/ F: S6 A5 R4 N0 C" `
  44.                 }& o$ V9 b1 C. `/ o) A
  45.                 if (strncmp(buf, "quit", 4) == 0)0 J1 ~; ^% t! i! c2 ~9 w
  46.                         break;
    9 ~( k$ I3 U8 C) U) V
  47.         }! s: s) ^2 z) q5 F' E  @8 `: J6 |3 \
  48.         close(sockfd);
    % o* z7 e& b$ u" d
  49.         return 0;1 J  g* x# n% |# G+ o" y: w, q
  50. }
复制代码

2 G$ W3 M+ }9 A
: _2 E2 d  z& z4 ]) l7 ]
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-17 14:05 , Processed in 0.089197 second(s), 22 queries .

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