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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

% n. \/ p* I/ s; B# X0 F% dsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
" m7 D- _9 x( [6 ^; P1 F6 d  s
, K0 D8 b$ f3 U, n

" k5 s8 X) }: W. j  STCP协议- U' R3 ~  U0 Y3 e$ I+ w
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
- Z! Q8 [. Q- L/ `8 W% R: |
  N6 P- x  b" C+ p# F- J6 I  i4 ^) ^
( F+ d7 _$ U( z  A+ F! L. a
关键词:三次握手,可靠,基于字节流。
+ P+ [& K* w% `! u: H2 G! s: s1 F; N/ q4 Z4 B

* K, \' S) ]1 ^可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。; K8 l. \6 p& I. k& }5 F" D

9 V9 \% P, k! {  f5 m9 TTCP服务器端和客户端的运行流程
. E" q4 b" x4 W8 y- D/ H
0 T; Z/ M& r6 t3 N
7 V( U) d8 A9 i$ G, j
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
8 X4 W7 w$ t0 X& @
2 j0 W* F! y7 s# I2 e

! ^' K8 F+ c2 o4 ~" n1.创建socket. d+ R7 G- G$ ^/ p
socket是一个结构体,被创建在内核中) g. u9 q2 A' y. p5 s( Z
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议* G) |" o4 Z1 s% i8 R2 a1 m
9 E, u+ v; d8 f, g& `# M9 o1 {' A' c
) S: E* g1 g- F2 M$ k
2.调用bind函数9 A( x: M8 z, z  ]
将socket和地址(包括ip、port)绑定。
" c8 v' u) `; F  e  T 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序4 l/ Y9 v  c4 t' p' s# n
struct sockaddr_in myaddr; //地址结构体
. c5 n( s  P4 S; r2 t bind函数+ B5 E% n. A" E+ K
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))& M  [) I' A% C% p

' f6 F* k4 ?& p) d/ Z

& L6 j; o4 G' T) z* B- o  }3.listen监听,将接收到的客户端连接放入队列
0 Y8 y" q0 u  V6 A; M% J) c listen(sockfd,8) //第二个参数是队列长度
+ ?, x7 P8 o4 Q" j& c$ ^
8 _/ }  q$ l5 \( \3 J

. R' A/ y( `6 w: w4.调用accept函数,从队列获取请求,返回socket描 述符' y$ H1 f8 f# B8 R0 X% _' Y
  如果无请求,将会阻塞,直到获得连接
0 d) M1 z2 ?! k0 `; I  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
+ O1 N4 K: _. @2 E& B0 n$ Z6 n; X% Q) Y* `% @+ [- ~/ X1 s# r

% Q7 ^: d+ V* E" ?9 C, P. S( `5.调用read/write进行双向通信
: U' f: y  t$ N& s1 a, c) @* \7 C7 b  l: E+ O8 ~  Y
$ u' x2 m  o) X1 k7 y& ?0 G
6.关闭accept返回的socket
0 s7 \* P$ w, w& l( a; R! n  close(scokfd);
1 p4 O/ f6 a! @4 L
  W- f1 Q# H- X& F

2 k9 d: W5 u* L" M5 g  B  Z; h8 g
% G. B) C( J4 K- n/ y7 N

) T  G1 }4 t) Q# I下面放出完整代码
8 {5 P1 p5 r. [+ B4 p
6 ?  H% o3 r2 w! N) N. O
  1. /*服务器*/
    ; O5 A+ b7 t7 u3 n. ^' K
  2. #include <stdio.h>
    8 m! k* y* q9 h
  3. #include <string.h>
    ( h7 u6 d, i! k0 ]
  4. #include <stdlib.h>
    * X: ^+ b5 `" I! C1 f' `) L: W( R
  5. #include <strings.h>, S: t( u5 A$ s
  6. #include <sys/types.h>7 G) c2 Z' M/ E4 A" Z
  7. #include <sys/socket.h>: q. J, d+ p4 [. {! x2 J5 {
  8. #include <arpa/inet.h>/ G* l! u1 ~" d
  9. #include <netinet/in.h>- m8 F  }4 h+ ~3 e& E0 c% W
  10. int main()
    ' X0 u& X5 p' g! U  U( V9 e) F* V
  11. {$ T( \' c' m& e+ w8 A) B& f
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ' S' j$ P! l) v5 Q/ c. t6 j
  13.         if (sockfd < 0)" W0 j; G* s  F" G
  14.         {
    ( f9 H( K5 D7 Y5 Y
  15.                 perror("socket");! g* A# ?  |* }
  16.                 return -1;8 |% h3 j% M* M3 P- L
  17.         } //创建失败的错误处理
    / D, t! g1 T& [; ]
  18.          printf("socket..............  F! n6 y- X, @3 v& h  f. @3 S
  19. "); //成功则打印“socket。。。。”
    3 G1 |; f" ?) W
  20.          1 [0 l! |' Y7 y2 ^
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体1 W, T4 f  D5 s) H" Y7 D
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    " h& z7 `4 G/ B9 L
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型4 M/ c2 d0 c( `4 h1 K
  24.          myaddr.sin_port                 = htons(8888); //选择端口号. F! J5 t1 b& b0 k$ Y* m' P$ G) d
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    2 E& k9 x6 Q- s) W/ I  t" }
  26. 1 D8 s, H3 ~0 S/ O1 ?' \# B6 S5 f
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    $ ~% Y  R, _; V& a$ H1 h4 }4 Y3 B
  28.          {
    9 {6 ?0 D/ s0 \9 B5 J: C! B
  29.                  perror("bind");
    / F" a% U! l+ e( N
  30.                  return -1;: V& H8 o$ [# G* [9 C
  31.          }
    ( G& y6 u+ Q' h# j; i" l' {
  32.          printf("bind..........4 a9 v' R. Q, k% M& n' O3 P* ?
  33. ");/ w: P3 c0 h5 N/ c; V$ e

  34. ; h* b$ c- [$ c9 P% S
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听9 s1 y" G9 ]0 s3 Q; F
  36.          {- d# y7 v+ d! z" y: J5 H
  37.                  perror("listen");% G/ [5 ?6 p( z) J+ w
  38.                  return -1;
    1 L' |0 m+ K) x8 x, n. d
  39.          }+ i+ |& L) X4 r) B
  40.          printf("listen............
    5 L. `8 t/ r$ t0 A
  41. ");
    ' Q% r, d$ [0 \7 t
  42.          
    " w9 q+ P2 Y! U, [. e* J
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求1 b4 _: C6 V5 W. m( X8 C
  44.         if (connfd < 0)
    " r: y  O) O# w- v
  45.         {3 t* R8 f6 a& d9 _( ]
  46.                 perror("accept");
    + q* {& O+ a9 N7 Z+ @+ r7 I$ H  t
  47.                 return -1;
    8 Z& F& E! K1 o) D+ S2 p# p$ V
  48.         }
    , Q( D# L2 g! M# f
  49.         printf("accept..............
    . q9 `" L9 |! g4 t; B6 K0 t
  50. ");) Q2 w( l0 Z9 P, A
  51.         char buf[100];//定义一个数组用来存储接收到的数据, ^: D( m- c1 b' z* v9 L2 w$ G9 a
  52.         int ret;
    ! _: X% l$ B% Q" D: s% v
  53.         while (1)
    : `& h" V# f2 ]% }
  54.         {. ?' ]* r6 Y: Y
  55.                 memset(buf, 0, sizeof(buf));
    ( M( ]0 F# j. q, Z' s
  56.                 ret = read(connfd, buf, sizeof(buf));
    ( }6 f2 R' D! B# Y+ S  j7 q  N
  57.                 if (0 > ret)
    & M2 {+ d! F0 K: [; y& Y- a/ L) T
  58.                 {
    ; q6 L$ }$ j9 ^* f* d
  59.                         perror("read");
    2 X* l. w2 Q# V% ^2 H, N4 X. l
  60.                         break;
      e# }$ M. C: ]7 ^
  61.                 }//执行while循环读取数据,当
    7 P" i, h' f+ s7 j  b2 J, v  x
  62.                 else if (0 == ret)
    + y4 Z7 F; S$ R$ l
  63.                 {/ R, ^) E& x/ D. c
  64.                         printf("write close!& i6 l, W' }, x! H6 o8 [% C. o
  65. ");3 h5 L8 D9 g, f3 S% q
  66.                         break;
    # ~) Z" W/ [8 k$ c; N* X  f
  67.                 }+ o3 G/ j' J1 d& x5 [% ]6 s
  68.                 printf("recv: ");
    + ]2 Q" d# ^0 k) p, n2 N
  69.                 fputs(buf, stdout);//打印接收到的数据8 ]* h# r$ _, r) s8 O* a# j
  70.         }% W% U% Y  W% {# \
  71.         close(sockfd);//关闭套接字8 T& r0 V& S+ G8 h7 r
  72.         close(connfd);//断开连接5 q3 t2 k$ c( ^! h4 A  K2 P  `1 I
  73.         return 0;
    " Q5 u+ D' v6 k# {8 f
  74. }
复制代码

% p; e) e; n0 B. L
1 E' K. x$ f# ~' `
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    5 I. {+ ?$ j8 T# v& j5 k
  2. #include <stdio.h>
    * k# U& L$ `5 n4 k1 o
  3. #include <string.h>
    6 ]( J. ^# W1 M$ \# }& S: F
  4. #include <stdlib.h>
    " B9 s9 K$ ]$ f5 [8 h
  5. #include <strings.h>$ v# u8 C3 F7 B* w. P! F
  6. #include <sys/types.h>! a! h+ m8 R0 v4 M* H  O* l' t
  7. #include <sys/socket.h>7 e1 S5 g% c1 l8 \
  8. #include <netinet/in.h>/ I7 y9 m- V8 u) f
  9. #include <arpa/inet.h>; g' I5 w/ p/ N: C! S, f
  10. int main()  n0 y- d$ [3 f0 i' E' c
  11. {  |, O% P& d; l; O
  12. int sockfd;$ |6 x! ?( T& k4 A
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))); N8 V, V  I; {" Q+ X1 J( E; v
  14.         {- m8 d, w1 G, n
  15.                 perror("socket");
      n0 ~7 X) e; h. @) y3 I
  16.                 return -1;
    8 \7 ?# f1 `1 s& T% l1 ^2 U
  17.         }
    & ?% _# g* p. K( c4 l
  18.         printf("socket...........8 q8 K5 ?" k) O
  19. ");& O. V5 [: z+ O, _# H* S1 z. O
  20.         , a! x6 y$ }1 u& e/ w- T$ F/ r
  21.         struct sockaddr_in srv_addr;, C; r' ]7 `" a( s3 @+ ~. A
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    5 a& \, w( Q8 E; J9 y
  23.         srv_addr.sin_family                 = AF_INET;) i: ^" w' \8 P8 W& ~' ~
  24.         srv_addr.sin_port                         = htons(8888);' p: N$ q( v( }. f( D' U
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");/ N- b3 l2 @! @3 V. Q! s9 [) [
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))3 J2 z7 {" t' ]# ?  D
  27.         {
    % b1 R( W2 r* `* ]
  28.                 perror("connect");
    0 }2 c( h$ T$ k
  29.                 return -1; //exit //pthread_exit! v+ I8 {, x0 n4 f: l( z
  30.         }
    $ M3 K' d3 \& C5 Y' Z  L
  31.         printf("connect..............
    0 w- `! o" i, G
  32. ");
    4 B6 _( A! [7 N" \6 k/ M
  33.         char buf[100];, ~% F8 H4 o" F/ [
  34.         int ret;
    ' P  G! r0 o/ D6 }/ v
  35.         while (1)* X1 q  v7 i3 B6 T. [3 |; b; ~
  36.         {
      c1 m1 p! h* S6 L9 P: v# {( e
  37.                 printf("send: ");
    $ M6 I1 f8 e+ s/ K
  38.                 fgets(buf, sizeof(buf), stdin);
    6 s8 I- ?! l5 [- g6 G) R; N$ g" z
  39.                 ret = write(sockfd, buf, sizeof(buf));2 w9 G4 g# \8 s" o+ u( l
  40.                 if (ret < 0)8 t: r! I# m( p3 D& O
  41.                 {
    0 _! `6 ^7 I1 d
  42.                         perror("write");5 H. p4 r9 ^3 [3 q
  43.                         break;
      \" H$ x% B! S
  44.                 }* J: W' C) h; k  V
  45.                 if (strncmp(buf, "quit", 4) == 0)
    % r0 Z" V. U- G2 W
  46.                         break;, ]2 Y$ c& c4 a& G
  47.         }* y3 e6 E0 h) _- v2 {
  48.         close(sockfd);, ?( V: a8 g; U0 A" F' p
  49.         return 0;
    & d, U+ I6 E1 M) x
  50. }
复制代码

( I6 `$ i; l3 k  o/ r: N4 U6 B8 ?, }% }' @) x5 Y
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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