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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。: _$ v& F. R2 W( n' y* u+ q

* g- R! i& \9 K1 o; n
+ b- S! I0 ?+ @& m& Y, Y7 [) _
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。. c- h/ t/ c  h8 b. L; s
# k: W* x, h# r0 r! ?+ W! v! E
" {/ O- X: b9 O% Y2 n3 P
TCP协议
7 R& p' E# j; [TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
5 _( U* G$ K4 q- P3 G
6 b& s& a* P; ~
; K, u* f# `7 q0 K- h
关键词:三次握手,可靠,基于字节流。
8 i, z& x% A5 @& e8 \/ h  B+ b* j; c

% _8 [6 x2 ]& e, X3 \' Y可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。$ l" ]" K( j. T4 K8 D
% x& e+ K: W; ]4 ?# r/ p
TCP服务器端和客户端的运行流程
& F0 A4 r# U& R" _, q3 b
; z1 N& F2 s( F% T3 f! O
$ M3 x5 {, N7 S6 |9 D4 c( T! U8 o; F
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?" p* t( t& M8 X. N$ Y6 b3 M
) p, X" B; h. m! _4 K
# L8 A# ?7 V3 R% w0 H' y
1.创建socket
1 _" ]8 G# M3 E* v$ f, V8 I socket是一个结构体,被创建在内核中
# p+ j) y! _  c sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
/ A+ O, q. w& F( L  k( N
  O, i: |* R( D9 \8 _! l: @
  Y! ~3 ~5 o: B2 D6 M$ G" I3 K
2.调用bind函数4 P! v9 ~8 v& `. U3 |
将socket和地址(包括ip、port)绑定。+ l8 K$ `# u3 j6 ~, m, O: i* H
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
4 c: N- T1 x  T& i0 W' g struct sockaddr_in myaddr; //地址结构体
; R/ Z: Q. n. ?. Z! ^; T" q: R  _$ r9 r bind函数3 l* s9 T' Q2 \% _; f- }
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
3 E1 T  `+ R3 G
& x$ }" I: }$ {* v$ i, }

! \* J" N% e& V  c9 w% H3.listen监听,将接收到的客户端连接放入队列
8 S5 r  N7 e, s* ~ listen(sockfd,8) //第二个参数是队列长度$ W  ?# V- P& R6 @

' v4 g' L* I7 I% m2 M! \
+ Y, M! I7 \8 _4 A
4.调用accept函数,从队列获取请求,返回socket描 述符
8 x2 U. v2 I: e. \6 ^+ z  如果无请求,将会阻塞,直到获得连接
: U3 H9 N3 {7 `1 C1 E  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数* @$ m0 E/ ?$ O5 E
8 e$ m2 r3 ]' I; E5 d* x" q

* \1 ?& P7 ]) A: N5.调用read/write进行双向通信, a- H) q! D2 l# E. s) @
* Z" w7 }( @, ^% M) [- u7 i

$ p8 l( [% y+ L; y6 }6.关闭accept返回的socket
6 K) O, o7 N. e  ]( c" b$ L  close(scokfd);8 D% r: U0 o) u! [6 A

: J5 A9 A1 a; u9 I: t# b

6 |. W+ b+ ?& l  [; O0 E; X/ Z# q0 F5 r6 ^
9 i- _8 U4 p9 S! |; R6 G: _
下面放出完整代码! M/ u8 E- q# c7 W3 W% o9 a

6 N, {5 Z0 A6 w
  1. /*服务器*/( b! \  Q6 y- Q5 b& x
  2. #include <stdio.h>
    7 ~/ ~( X0 m$ ~0 U
  3. #include <string.h># |: K" \* K% H* R5 d2 k$ T
  4. #include <stdlib.h>
    8 y% h# L) m& o/ Y% ^5 i' Y
  5. #include <strings.h>/ ]. k1 Y" h, I
  6. #include <sys/types.h>
    - \; Q1 {+ U9 w8 A' _6 X
  7. #include <sys/socket.h>
    3 z% D' f0 z3 ~* Q" h- a% y$ n) }: x
  8. #include <arpa/inet.h>
    $ K  g; i9 }+ R" r$ `6 c
  9. #include <netinet/in.h>
    % V+ L8 L: C9 r
  10. int main()
    . ~* `' N- ~( B9 K) b
  11. {
    ) K. C: l6 s1 w8 T* |' W8 r4 w! m
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字' ?5 }# @8 K# V  l9 T" e
  13.         if (sockfd < 0)
    ' O) S, Y( s' c$ X5 }
  14.         {3 Q0 c+ i+ ?" A, e# L0 `* n5 z
  15.                 perror("socket");' \* p1 \' g: z6 u: J% o
  16.                 return -1;2 A/ B5 O5 k* U6 w& A  |+ t0 i
  17.         } //创建失败的错误处理
    " Q4 r% A6 i4 O. S2 P2 j( N4 o
  18.          printf("socket..............
    : ^/ a- u. z' _3 L0 @. F; b( a5 o: d
  19. "); //成功则打印“socket。。。。”
    8 e7 W/ l* \8 O' l/ K
  20.            j$ ?5 g9 ~6 w& `
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ( c9 m  A: d$ Y
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)% H; b( L$ f& I
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ' ?5 q. {& p  y0 H6 i4 Z1 \* k5 C7 `
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    & E1 J4 L, T( o$ j
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址/ B: T. c1 P- t* C

  26. + ?% g2 A2 W- u7 [- V* ~  Q
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字1 j! |2 j& v. H; R
  28.          {
    ; k  P8 }$ G8 M2 Q2 L
  29.                  perror("bind");
    ( f1 P) i; Y- p! U" J
  30.                  return -1;
    " X! l- i1 j$ |4 F  Q
  31.          }
    * S; B) `/ b% M1 B! k$ [
  32.          printf("bind..........
    ( ^% T0 U0 ~* g; U4 C
  33. ");
    6 `( W9 a9 i/ h1 T$ B
  34. ) z5 A. R* ?% G2 M
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ! k* M/ v% l4 t& Q, O7 @8 A
  36.          {. Q0 M, h6 ?( }4 Z' m% i; ]
  37.                  perror("listen");8 q% P! U" f4 A4 t  z, e* O
  38.                  return -1;
    9 }% y: A  j6 {5 N! v9 }
  39.          }" w5 ~/ [( ~  h" A
  40.          printf("listen............6 y* K$ U9 f* R0 V- o6 O! K! b; `
  41. ");
    5 C" M* y$ t( u6 q
  42.          
    / `6 y9 C8 H1 M0 n
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    5 Y$ `* _' T1 H  u1 Q! G
  44.         if (connfd < 0)2 e9 u7 W: }. ?- W4 ~3 O
  45.         {# X: C( Z2 c9 H
  46.                 perror("accept");
    ) t2 }0 P# w& S/ N5 ]. |
  47.                 return -1;* \1 w& y$ b% o1 N7 h8 T
  48.         }
    / K) U9 G$ M  y& S2 s! E5 W/ D
  49.         printf("accept..............' c/ H, m. {0 M1 w5 b
  50. ");7 @/ m. g* t' _9 u# B5 P
  51.         char buf[100];//定义一个数组用来存储接收到的数据/ `0 d  @* A% X) U
  52.         int ret;
    ) X7 c) I) }; i+ r: k
  53.         while (1)2 J( B% ^) A6 |
  54.         {4 q1 c" U& f& K0 g# P- {
  55.                 memset(buf, 0, sizeof(buf));
    ( `) z  }; [5 S9 w5 X
  56.                 ret = read(connfd, buf, sizeof(buf));1 F( r0 a( [! r- i' @
  57.                 if (0 > ret)' l; V# X  o& M2 e- a' h7 n
  58.                 {* X% F- Q, v$ P
  59.                         perror("read");
    . X3 n$ g6 d: U! R1 }1 r- f4 i
  60.                         break;
      @+ q9 d6 t$ \6 t% M4 x# d$ ~
  61.                 }//执行while循环读取数据,当. Z* d. B" p+ e& u, Q
  62.                 else if (0 == ret)
    & O& ]0 ]" n  U# d" E
  63.                 {. P! S0 p; g. X  D: j
  64.                         printf("write close!* ~! [' Q/ r& E* ]; c& |  l
  65. ");
    5 q- }; X$ H- _: Y
  66.                         break;
    6 d# }7 K0 p& |( l
  67.                 }! ~: Q, |4 f: k
  68.                 printf("recv: ");
    * v  y; x: `+ `/ d1 T$ @
  69.                 fputs(buf, stdout);//打印接收到的数据- x! Z0 d$ F  L2 P
  70.         }
    0 H3 b; q6 [0 o
  71.         close(sockfd);//关闭套接字* ]0 h/ A% r7 Z" q
  72.         close(connfd);//断开连接; n& I2 h: {& k( [2 g
  73.         return 0;
    5 ~( x  f1 t- s- ?/ S5 U* Q
  74. }
复制代码

" Z) Z% _# Q9 d+ `0 j6 n6 n4 |7 C- i3 ~& v9 G4 J; `! z% j
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    " J; n' t! A. e' {9 o9 D8 ?( g
  2. #include <stdio.h>* u' \: r( M. R
  3. #include <string.h>
    # c! d7 {) ^: W4 b& m
  4. #include <stdlib.h>  `. Z9 I+ |! ^" @3 d# P
  5. #include <strings.h>7 U! p+ q8 n1 w/ B$ e' y
  6. #include <sys/types.h>
    2 M& ]9 S+ P4 u. j2 K4 C& q# l
  7. #include <sys/socket.h>! F4 ]$ E8 R& Z$ K, k3 y
  8. #include <netinet/in.h>
    3 X: k$ V# `2 y# H1 a
  9. #include <arpa/inet.h>
    ( [9 {  f9 _3 ~
  10. int main()
    5 B. b! j" o& t/ P
  11. {
    - ?& f% e- Z' h3 P" Q
  12. int sockfd;
    4 W, N( M9 Q6 U' B5 P) X  e' n$ g
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    7 D3 W  z( k  n- y1 d
  14.         {* k3 P# E: Y) y: V. a3 a
  15.                 perror("socket");! _$ @4 Q4 a4 X: N* y0 [5 D  f4 {
  16.                 return -1;
    ' R/ o5 M: V$ q" g: q
  17.         }8 G- T' Z1 Z0 F5 @
  18.         printf("socket...........) ^5 n# `8 q0 L  q
  19. ");6 \( U1 q* Q; Z  g. d+ w( W% E; A
  20.         
    ( f) b  Y) z" x, M" c7 j
  21.         struct sockaddr_in srv_addr;+ x& J. z8 p' _7 g8 ?# [
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    $ N4 O9 z9 q1 F1 O
  23.         srv_addr.sin_family                 = AF_INET;
    + O/ H2 d( v  o, n
  24.         srv_addr.sin_port                         = htons(8888);
    + ]5 G5 `0 e2 Q9 i; [0 S0 O
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    7 J3 p0 M& N" b$ _) O/ S
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))2 ?* ~9 Z; c' ?; c2 F( Y1 }
  27.         {# j" L; I' G0 E8 f. [. S8 v" I- G: D
  28.                 perror("connect");5 x- T, S6 `* q2 Q& U$ Q
  29.                 return -1; //exit //pthread_exit4 }  a/ k) O7 e3 Z% h; }$ E0 n0 Z
  30.         }
    ; f, O2 V8 T: O8 x' `6 ]/ R
  31.         printf("connect............../ _: D4 ?# G+ E% g
  32. ");, @) f( g4 {8 r% s4 p1 n
  33.         char buf[100];# r4 U# r2 h. t2 o/ u( x3 g
  34.         int ret;
      p- f0 V. R! n- z0 @( ]  \) {, \
  35.         while (1)' B2 K; R8 e4 V% H
  36.         {
    + D) r; ]- K6 E5 g
  37.                 printf("send: ");
    ) e2 m% b, ^5 t$ B6 c
  38.                 fgets(buf, sizeof(buf), stdin);
    7 \2 |$ S* A5 P: }
  39.                 ret = write(sockfd, buf, sizeof(buf));
    : v% G# m7 T8 P9 C) ^2 u
  40.                 if (ret < 0)
    & v* @! r! \- M3 o, ~  z+ J
  41.                 {
    ( t" `" |* b# U% M* S
  42.                         perror("write");) c. }1 L! E( g4 ?  _3 U
  43.                         break;
    6 ^4 j  h+ I' d, t. `. k( l
  44.                 }
    5 j; R; t; k) h0 l) ~
  45.                 if (strncmp(buf, "quit", 4) == 0)
    # |" F. Q4 X9 d) X
  46.                         break;
    " P4 g/ {1 H3 Q
  47.         }' P1 x5 I# P4 ]1 C
  48.         close(sockfd);
    ! ]6 Q7 H: p: i7 ^+ g9 r/ I, A
  49.         return 0;$ w6 B" l1 N5 b+ k
  50. }
复制代码

! p, M" m2 S! c; k( d! {, m1 a% B9 H: I3 k( _( }
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-8-4 08:10 , Processed in 0.058449 second(s), 23 queries .

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