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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

7 c2 Y- w# E. i  A% z2 W/ C
& i( i: c+ [0 z
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
" h- |) g0 W; ?) C9 I* Z) Z9 A
4 ]1 ?' c% {. t# }

  ?3 t2 m+ j3 z2 `TCP协议0 ?. r) u  W% k
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
. u4 A' X, s  K( y1 y6 n. V+ n9 t
( p9 u8 j: n+ O% i: Q

2 O7 D) K- A7 [9 F" C% h0 d关键词:三次握手,可靠,基于字节流。+ Z3 j5 H9 O3 X5 C" a( m
2 O$ o  k3 a7 I  S' o' ~( C
3 x! s) X  U; x: B6 `5 {
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。. v5 T5 o  x; d; O

# }4 j* v2 N# r" e$ _! V% }TCP服务器端和客户端的运行流程
  x0 \6 @$ c& a, w
% N! z' {% ~6 q1 E, O/ c( P6 P5 c
" X8 r% \1 m7 ^" {6 |  |- m
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
3 c1 u7 D9 F+ G' [: e+ e& l2 v* Q- l9 F7 c

' a/ d4 u7 N' H1.创建socket
, k( d- N, q: q* R socket是一个结构体,被创建在内核中" q! C9 \$ p2 R$ o5 t1 \; V1 O( ]
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议. m, J7 z1 ]9 l+ x0 e6 S1 h1 W

6 S5 {- C) n. }6 B, b" C4 p9 C, c

- ]9 ?! G% m8 I$ k7 g' W0 L2.调用bind函数: i) ]4 H: k  L) o% Y# A
将socket和地址(包括ip、port)绑定。
7 ^' s9 G" o  a! e  g3 r 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序7 c; i6 p7 w8 N! s" z: F$ X
struct sockaddr_in myaddr; //地址结构体9 f# @6 W2 @$ [9 i8 o! v
bind函数6 ^8 D5 |# Z" v; I# v, u! K
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))- w0 q) a9 A$ `# l
; d) j4 S; Y6 y3 ~* k' c, _
1 A" j, H8 ?. w) }- I. ?& c
3.listen监听,将接收到的客户端连接放入队列
5 d& H. C8 f* R. P+ x( A" A4 ^* | listen(sockfd,8) //第二个参数是队列长度' M6 a. x8 k" C9 S* a+ n

% n/ f. C" b' X* H0 P8 ?! W9 z7 Q

/ v& o* Q. V8 f. _; U4.调用accept函数,从队列获取请求,返回socket描 述符3 s% c% ]- a/ M
  如果无请求,将会阻塞,直到获得连接
8 q6 r. i6 M% D1 d6 ]2 q& b! R  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数. ~# Q* [2 j* O! r7 k

" \  [/ g# R2 j% d0 J2 j

  q5 L: a# R+ D7 Y6 M* W2 }/ x  t5.调用read/write进行双向通信6 g  N1 a* w0 J1 q  j! F- a

. r8 [& F5 K* ~/ @4 l% u+ ~

: I4 {' l8 a- c" a7 z' d6.关闭accept返回的socket9 {3 p! `. i* `3 O- [4 p2 m' J/ B
  close(scokfd);8 x8 T! I* q% W( E; Q" N' z

" u5 r8 V$ d! F- |) M* ?6 ?
3 b6 p  H. x1 v, p# G
& v. B2 [: u( i& z* n& @
) z, Z: m9 }/ L! e7 B
下面放出完整代码; G0 ^4 Y7 N9 O9 z* I7 y
8 J% a( v5 _% q- [7 g7 z
  1. /*服务器*/
    3 J- R% m- U$ r( }' u
  2. #include <stdio.h>, p" J1 ~1 X3 _$ L; _. R
  3. #include <string.h>
    4 a, j/ X! p- P) ?1 C
  4. #include <stdlib.h>% ^6 ?. x2 G- u2 N$ S" {
  5. #include <strings.h>
    % {/ G% }" A6 r+ K$ }/ v5 G7 S5 `
  6. #include <sys/types.h>
    / d; p5 E0 F- l8 e/ F! b: l, J' x/ n% t
  7. #include <sys/socket.h>. q% C' p" f+ V% p
  8. #include <arpa/inet.h>
    ) \( H# g, {, K$ x+ W5 {
  9. #include <netinet/in.h>
    ( i4 A; a2 B' I* F$ t2 e
  10. int main()* Z) V) R. [. C1 v
  11. {
    - T' S( M4 D) [2 |! r
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字% O- w& \# ~& R6 E
  13.         if (sockfd < 0)
    ; E- w6 u. i# q$ o% _- M  |
  14.         {
    ( V- a1 C; ~! @% A# g2 g3 O
  15.                 perror("socket");. ^& a6 M7 A( s" e. v
  16.                 return -1;5 ]5 i+ u$ s* }  J4 l
  17.         } //创建失败的错误处理
    , h/ }0 G/ J! b: q
  18.          printf("socket..............5 C9 I0 S, A! [$ z
  19. "); //成功则打印“socket。。。。”
    2 ], V9 g) E# _
  20.          4 `5 ^5 V1 S! S. S
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    & J5 j- ^. G  l% T
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)' j% R. e. L2 l9 K: _
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型( a# A, ~: @. _4 o' }) i
  24.          myaddr.sin_port                 = htons(8888); //选择端口号+ R0 |$ F5 j- o8 a( q% ?
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    $ J  v# `: A! ?
  26. / `' M7 D; l+ x1 W8 q/ Q4 y, d
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
      C- X* \/ [! \! D2 H" [! P- Z
  28.          {$ N" `# H3 U, b/ L2 g: m8 H6 G
  29.                  perror("bind");
    / B. `- ~% o& `
  30.                  return -1;) b9 Y0 |( I5 i& @
  31.          }
    0 E* c5 L- D; K4 l) e5 c2 }' [" A7 y" c
  32.          printf("bind..........% a/ ~$ t6 g  B2 o# Z7 p, {" ]
  33. ");% ?3 t+ H7 I) x  c6 g

  34. ! m: a& E$ M6 Z5 O9 @
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ; }( P; e) y" `, I7 A5 N/ s
  36.          {. w$ \' U. r4 o4 R% r2 j$ n2 F
  37.                  perror("listen");
    + H: k: G- Z) {8 l/ K# g# T& c9 f
  38.                  return -1;1 Y' l9 s0 `1 W  x
  39.          }
    4 v' i$ _+ R: O$ w* Y
  40.          printf("listen............+ a% r) Z, B/ |0 H4 ^+ L
  41. ");
    7 v# Q" r  A% I* u& ^4 F
  42.          
    : p9 e) M7 e2 x  `
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    5 E- ~2 x& I* f# T- S. X/ i
  44.         if (connfd < 0)
      m) U3 y% ]+ e1 S9 Z2 |& h/ K
  45.         {9 [" X3 {, F# V- z
  46.                 perror("accept");1 q; i+ R/ |  j- W, f* p) I
  47.                 return -1;
    4 @! L' L: B% N* n
  48.         }7 T# p* Z! @' f. U2 v% Z  q
  49.         printf("accept..............
    8 g+ o' B, m7 o  s4 E
  50. ");
    & r# \" }% `9 s" p% a# g
  51.         char buf[100];//定义一个数组用来存储接收到的数据
      x7 n+ |- [3 E' g# L0 o+ M& [1 Z3 ^
  52.         int ret;
    : Y* C6 g' S* o9 B. r
  53.         while (1)
    4 y1 Z. a; t- F* s
  54.         {
    $ }" s0 g7 }" \7 p0 b; x% M% d; v
  55.                 memset(buf, 0, sizeof(buf));
    5 T- l1 i1 N- Z+ d
  56.                 ret = read(connfd, buf, sizeof(buf));
    / B4 h$ Y* _" |8 k) L( G1 q
  57.                 if (0 > ret)6 R( D/ Q# u  l$ \* b! x
  58.                 {/ W; o) _- U' [6 U: A+ O" _
  59.                         perror("read");. Z: e% q: k+ Q/ F  k3 x  t
  60.                         break;, ?# y$ |# j+ S! {% o, Y1 \$ ^$ n
  61.                 }//执行while循环读取数据,当
    * p  A8 `# R4 [3 d# f$ t' A9 j
  62.                 else if (0 == ret)
    : z; E8 @2 T! P& \: f' }
  63.                 {! _8 N, K2 k% s; F/ V3 `! @
  64.                         printf("write close!  U8 h/ E8 g3 V2 g* M
  65. ");1 D! e  [- `- _8 r# I3 A2 G+ [
  66.                         break;
    # y# E7 F# ?  N+ Z! s
  67.                 }
    + Z7 `+ f9 B( K8 a
  68.                 printf("recv: ");
    5 a) n% m  G6 P2 ?& P! R: K7 K; N
  69.                 fputs(buf, stdout);//打印接收到的数据
    % b4 {' K+ v# a
  70.         }. P7 j! m; m$ l
  71.         close(sockfd);//关闭套接字% S! P' |: r! Y9 t
  72.         close(connfd);//断开连接6 v5 N' C- R: l% ?! O+ }- I- j0 x2 C
  73.         return 0;  g0 }0 y6 |6 K" m- m6 @: v- E
  74. }
复制代码
8 d' y2 y6 g: t/ U* h- @
/ E- V9 Z$ g0 \1 \7 F  _" s
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)" n; a- w6 Z. P* {- J
  2. #include <stdio.h>$ [; _; f" e5 g9 q
  3. #include <string.h>5 ?; ~3 I9 D+ j
  4. #include <stdlib.h>( ^2 ?0 r, I2 B: l' g
  5. #include <strings.h>
    ' e; Q1 Q* o# Y1 `4 |! @
  6. #include <sys/types.h>- T  a  Z" X, m6 G7 A
  7. #include <sys/socket.h>
    . G! v$ H! s/ I4 ]1 ~/ \; B) H4 H* E
  8. #include <netinet/in.h>6 z' `2 n/ L% Q5 W
  9. #include <arpa/inet.h>
    8 c3 {/ O7 I4 j
  10. int main()
    , I4 c9 p' I. U# o  P8 u( _
  11. {% ]  j- N8 W. L$ i0 h1 P; T
  12. int sockfd;+ `& r5 v9 {6 H
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))( c' E( h/ K" k3 K" g9 h6 u* X
  14.         {0 e1 h) @& u/ B% ~% ~& M# T
  15.                 perror("socket");+ J! I$ G# }5 V! C! z0 w
  16.                 return -1;; P4 P# S" s6 p7 V1 o7 s
  17.         }
    0 h- u0 y- U0 k; F7 b! Y8 o6 G
  18.         printf("socket...........0 O5 I  Z- U6 ~/ f# m& z
  19. ");
    6 m; E8 C3 ~: J4 |$ n
  20.         ( l3 S& r, v2 ~/ F4 H
  21.         struct sockaddr_in srv_addr;
    / \5 K1 B) G, Q  W( n
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    8 @5 S4 r; Y" L9 V$ l$ w
  23.         srv_addr.sin_family                 = AF_INET;
    6 l/ M2 [: M; |/ p3 j( F
  24.         srv_addr.sin_port                         = htons(8888);2 B! w; _1 C% H
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    , }  U) G6 e1 r- n. q
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    2 K, Y- C; }8 [
  27.         {
    7 m; P2 r* e7 {, E, j
  28.                 perror("connect");% q4 W) y7 w' c" i' [! n; x2 f# I! e* x
  29.                 return -1; //exit //pthread_exit' j; U' L3 E) m) H9 w# D" ?
  30.         }2 w( y- R! \, k% f3 e
  31.         printf("connect..............
    $ C6 p6 [, I* E, d
  32. ");
    + A9 y+ I2 L& S1 j9 g2 ~6 b7 Y6 y
  33.         char buf[100];7 h. T7 x9 q$ H% A: h+ M; }
  34.         int ret;, r, e7 K- ?, o
  35.         while (1)2 A4 @1 r) ~4 J' G
  36.         {
    6 A5 A2 Y7 ^+ ]8 B& N( x8 A
  37.                 printf("send: ");
    - \2 n" ^2 T: `8 T5 K, f
  38.                 fgets(buf, sizeof(buf), stdin);' w8 }& l6 w% [' H5 ]4 y
  39.                 ret = write(sockfd, buf, sizeof(buf));) D' s1 @/ T& e: ?
  40.                 if (ret < 0)6 V# P0 C. k. I% h! R* \8 f
  41.                 {" U; Y5 C' [) Y4 N8 i+ z# p! C
  42.                         perror("write");
    ; x  b6 m1 I+ L6 N
  43.                         break;
    " E0 W; j% ]8 ]9 a+ R1 ]
  44.                 }3 C. i/ B% u+ T+ Q4 d
  45.                 if (strncmp(buf, "quit", 4) == 0)% B- h. S6 f/ w/ U% f7 ?
  46.                         break;8 ^  h) U6 M4 x1 I5 X
  47.         }$ p+ Q) I+ x( T$ {2 I) S/ l: u
  48.         close(sockfd);; H8 u0 s( I3 v5 T3 t
  49.         return 0;( T' O6 F/ l' q
  50. }
复制代码
' X, g$ Y6 j" U3 }  Z
, {7 `$ D! n. Y" H4 n
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 11:35 , Processed in 0.082571 second(s), 23 queries .

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