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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

6 c  P. R. F7 {: U

3 w  f* L3 A( t0 d& t2 Z% y) qsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。8 @3 p1 f" l$ B: d
* p4 C$ C1 o( I6 k3 N  J; N

6 k. Z: G2 |4 u$ S( _% tTCP协议
9 G' r+ X) p% e/ aTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。2 Z- b5 J8 a; P) s

. ^! I- H2 Y' v
  s& i  B% c; H9 y/ X
关键词:三次握手,可靠,基于字节流。/ M+ Y  A4 y3 V. p
: x5 V, V$ d, M* U) c6 r

( m" M: N" p; P; x4 m可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。& z% C; B' t2 d

# A/ C7 o8 Q- w* {. q0 h) XTCP服务器端和客户端的运行流程
- u6 e. W' W' m8 x( m% `' ]
9 {4 t6 O9 x2 V! l/ p: E- S. w

+ o9 Y6 R* k4 x* G9 h如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
0 R/ ]; M- V& X2 C3 S, V
  g, k7 @% e  a& c2 ]% P  X

$ s0 Z" R3 m3 u: W% j1.创建socket& W' J5 ?1 q8 z" b
socket是一个结构体,被创建在内核中
$ o6 l4 ?5 l5 R9 y sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
& y2 @$ l2 v: K' r: i- K
/ n1 n% y4 j1 w1 W1 O

+ y' O0 W# M6 @: A2.调用bind函数( A( Q* T/ P+ z" c0 Z$ c
将socket和地址(包括ip、port)绑定。
+ ^% C( X# L) e8 T3 J 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序' G" Z! B% w6 F) T! ~5 Z, t* q$ X
struct sockaddr_in myaddr; //地址结构体
' M2 T4 R) V8 N1 x bind函数0 M: V" x; n9 _. Y
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))1 c  Q6 Y  u2 y8 y6 o! T1 m

; v) N+ |5 R, d% p0 h3 ~0 g

; T! U3 a" e! A/ l1 P: p3.listen监听,将接收到的客户端连接放入队列6 O) S% V7 I. J7 M; F1 \/ }
listen(sockfd,8) //第二个参数是队列长度2 K1 [  q7 w/ y

; d4 z6 ], B: I  K: @0 G
# m, z! a8 O' p1 t( N8 g/ [+ R
4.调用accept函数,从队列获取请求,返回socket描 述符
  |5 Y$ k, N- \  如果无请求,将会阻塞,直到获得连接3 ~8 U, {* D. g5 O5 a
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
  S7 k- ]( F" z% w, L7 T: H2 y+ z" p# u+ _/ ?, E; d+ I6 b' }' K1 Z, N
9 z' S$ o  a6 f* u6 ]6 J3 H6 b
5.调用read/write进行双向通信
* n9 ~. }* x4 q# o- [/ Y+ u
4 t, r, \3 C' {! E

* W9 t( M2 W5 {3 y6 t. p6.关闭accept返回的socket% s6 A7 O4 j+ V
  close(scokfd);; n2 V  m  Q8 P3 H* r0 g0 b
2 z3 O# D2 Y. d; t5 Z' U# p* J

7 g8 ?  p' @' s: v+ l, ?5 D
4 D7 c" M3 u9 M) c' t1 R

0 A2 m) r5 i! L7 d下面放出完整代码
* o; X/ J+ Q0 X2 @! G' C
+ ^4 ~9 m5 i$ w
  1. /*服务器*/. S6 K8 [. z$ e/ A; j; C
  2. #include <stdio.h>& e& g! k& d5 c" _) w  ]. {! L' M
  3. #include <string.h>
    ) {' q  p, n$ T) D! N) x% b' v
  4. #include <stdlib.h>, p/ h  r9 S; N3 T% @! g
  5. #include <strings.h>9 F* r! O( p! J
  6. #include <sys/types.h>. T- I2 k' [* B7 d
  7. #include <sys/socket.h>
    0 M1 {% F$ C5 w; v* U% K. h
  8. #include <arpa/inet.h>
    ' w" W! O6 T- E+ Z! s
  9. #include <netinet/in.h>
    . u3 r4 E7 r6 ~: d
  10. int main()
    % f' U* C- k% A+ u3 a
  11. {1 a* e. X' H1 S
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    3 e1 E' i9 H/ ^! r# f# D) _9 l
  13.         if (sockfd < 0)( X" _  s1 m+ V5 W; d2 K+ h
  14.         {3 k6 Y# U1 {$ O9 l6 F$ I% H, B- e5 N
  15.                 perror("socket");- \+ s% M& n' J4 b
  16.                 return -1;
    9 @7 M0 n; p! r/ _
  17.         } //创建失败的错误处理( p: n( Z" y+ x# B. Y' H2 E; q
  18.          printf("socket..............
    7 [  u0 g0 A0 V8 {2 U
  19. "); //成功则打印“socket。。。。”
    ( O* u3 S: C- X  F3 o
  20.          2 Y4 {/ y7 @4 A9 q( J
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体- L) G' y1 m, R6 C/ t
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)  H4 {: ]' W. b! M; S
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    6 n& |# n6 a0 x3 c: t: q
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    / ?8 K% |6 B: Y8 @  W0 ]( w1 R6 H
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    " \2 f# H. |; V2 F7 F: L+ Y

  26. 7 d% l2 f* r" D9 u4 ~, j; c% r) G
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字" N1 r6 f! u- h: o% l) d) t
  28.          {
    # f0 Y7 @) A" }; _
  29.                  perror("bind");  b* N2 a4 I- h; {; x- V* [9 Z6 u2 D
  30.                  return -1;
    1 C, `% \7 j' }# W5 J3 m
  31.          }* A) N% u5 p" P$ l# E
  32.          printf("bind..........
    9 a6 {" p! D4 K1 |  e+ d3 u
  33. ");$ h- p. g8 ^# Y: v% J: g% [& K
  34. + f$ O3 G7 G# Y$ y+ t, Q/ h
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听. j8 r2 I4 y2 W$ y
  36.          {5 ?, D$ i+ y+ i+ j1 D+ D
  37.                  perror("listen");
    * p, k) o1 x" N( `" ?1 k
  38.                  return -1;
    3 P# @/ w+ j, s& R, y. H
  39.          }- O- Z# x7 F" A9 M' v  {; a
  40.          printf("listen............6 i( R7 c% _4 S% b6 D
  41. ");) }/ \3 A" V% W) ^) C
  42.          * a4 @0 A- f/ h& F0 g, ~+ B1 ]
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求7 J  m; D7 g3 Y' |! \+ T8 x
  44.         if (connfd < 0)
    - Y( K, W8 h$ x( j, D  ~$ [# K6 C' C
  45.         {
    4 S+ O- L- @! K% b9 {
  46.                 perror("accept");- C2 V, j: F3 A( ?# x: I" e% r
  47.                 return -1;
    3 g+ g- ?* Q9 L& f2 W$ L! ^. u& i0 F
  48.         }
    3 r, y# C( u1 v. m. i* s
  49.         printf("accept..............2 P: O, G+ k. ]9 F# N) K+ G
  50. ");3 ~4 r$ p) d7 n' ?( l* v0 j. E8 c
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    * _) a# ]# K9 m5 y  f! p$ T
  52.         int ret;
    : X- |: ?7 H% U; Z, j& e0 r
  53.         while (1)
    9 R( S* U( f+ s
  54.         {
    ; G' l/ d  _4 L+ F, t
  55.                 memset(buf, 0, sizeof(buf));+ {2 H* b3 X& y- x
  56.                 ret = read(connfd, buf, sizeof(buf));9 T9 o; K% D' \7 M
  57.                 if (0 > ret), P$ R$ A8 f5 U7 C! E' {
  58.                 {
    . N1 m: o/ y9 k% O  i3 p
  59.                         perror("read");! V8 D( K# q; v8 Z. `
  60.                         break;
    4 {5 a" Z/ D0 \/ V
  61.                 }//执行while循环读取数据,当- M; N2 I0 d6 s  R! y
  62.                 else if (0 == ret)3 [8 M2 \0 b( H; ^5 H6 V: N
  63.                 {6 e, m0 E! M& S8 g5 e8 K! x
  64.                         printf("write close!2 B- Y) A' m! R# s( l# j; x: v
  65. ");: k  r6 l" ?/ V# h% q
  66.                         break;
    ! r4 B4 ~  f5 K) K  C. G
  67.                 }
    , C: j  h9 l" a
  68.                 printf("recv: ");
    / }- g$ m" a& P9 h# ]* h$ f
  69.                 fputs(buf, stdout);//打印接收到的数据8 C4 y4 E- x8 Q8 l, f5 R# w
  70.         }
    1 `, u: _6 @. a" r
  71.         close(sockfd);//关闭套接字
    1 z' u8 Z7 K. n! s0 V7 m0 s
  72.         close(connfd);//断开连接
    ! m6 V  T5 o! H
  73.         return 0;
    * ]+ v& I' u! N- z5 u6 d
  74. }
复制代码

: o% |' @$ `7 s% t
* h2 W0 o7 R0 D- h
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    + ~, k4 q- h2 T+ ]9 I- S. S
  2. #include <stdio.h>
    $ k+ ]6 c7 X& P& B& e" j" i: M
  3. #include <string.h>2 X1 p, M6 s' x* W
  4. #include <stdlib.h>! U4 a( r7 p! O) x" O
  5. #include <strings.h>
    6 ?- F& q, i- _( Z5 W2 e9 G& Y4 V
  6. #include <sys/types.h>
    7 B$ d# T5 [: b) J0 ^3 ]$ n
  7. #include <sys/socket.h>
    & j! [' Y9 o' N' t; ?
  8. #include <netinet/in.h>3 s9 ]. d8 }. `0 `! ^4 L
  9. #include <arpa/inet.h>
    ! t4 f% f" i$ i3 U8 }/ }- I6 Z2 `4 ^
  10. int main()
    ; f, s  ]  ]! ]- \$ _" v6 \+ G
  11. {
    1 Q) V6 D- S/ t& p
  12. int sockfd;
    8 ?6 C( F3 W  o/ `% E0 m
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    2 C% e; g: S; v5 k; s) [
  14.         {
    8 b  `% ]' \* \% P, M* W1 Z: |! K
  15.                 perror("socket");: W1 _& d0 r+ h! ~( q
  16.                 return -1;6 _* [) G0 @8 c  [3 a, t/ K
  17.         }
    * r4 T4 B( X. g2 w" r) c
  18.         printf("socket...........4 J* g% J  u3 Q$ J% c$ i% `0 P
  19. ");2 _7 P0 p. b5 y
  20.         ' l' W1 }6 x/ I3 D: J. n
  21.         struct sockaddr_in srv_addr;5 |1 b4 u* U# h( ~* R" x. t6 C( z
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    ! x$ o7 ]6 D5 r6 l8 ?. s
  23.         srv_addr.sin_family                 = AF_INET;
    ) y+ `) @+ ^8 f$ d2 F
  24.         srv_addr.sin_port                         = htons(8888);# F3 O. Z5 X. A2 S0 A! d9 ]
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");: |, e4 I" R/ X# }5 X
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))9 t8 U7 T8 p' {* j
  27.         {
    ' F4 k$ H$ w) W2 ?4 w' q
  28.                 perror("connect");
      n% W8 W' F- u0 `
  29.                 return -1; //exit //pthread_exit% r! @- J2 p9 S1 R+ V
  30.         }4 I; a' n% j1 }5 e/ o. t0 B! A
  31.         printf("connect..............
    ) H; Y5 X7 M9 B( ]
  32. ");
    % d2 e. |! Q3 g  U. L
  33.         char buf[100];! Z3 c% g# Y% a7 W
  34.         int ret;
    . z$ k1 |3 d9 ]- V! B. _
  35.         while (1)
    ' U& c" F; a8 q3 ]1 F+ ]8 D: ?3 A
  36.         {- V9 a' T4 V- l- Y( p8 U5 M- J
  37.                 printf("send: ");
    ; I1 D2 c! a# o$ ~( \
  38.                 fgets(buf, sizeof(buf), stdin);
    : _! B7 K' b3 y
  39.                 ret = write(sockfd, buf, sizeof(buf));: W% L8 z0 K- A5 s
  40.                 if (ret < 0)
    ; C0 k% J* U$ ?: Y$ Y
  41.                 {+ w: U' C* r- Y* Z, V: V3 i) O
  42.                         perror("write");
    ; N/ L4 ]. ]0 `* W5 H4 b: \0 \* f
  43.                         break;
    " q4 g( |) V0 S6 A/ Z
  44.                 }
    8 A2 U1 Q2 r1 X) r7 ]
  45.                 if (strncmp(buf, "quit", 4) == 0)
    # w- ]% c+ u# A* u$ ?& q
  46.                         break;
    2 K( |2 m6 G7 S8 ?$ l  L7 t6 T1 l
  47.         }
    . r/ L3 Y0 Z1 y, b
  48.         close(sockfd);
    3 u  P6 u. c! V' R
  49.         return 0;5 [$ z$ D& _3 @; `  N; W5 Z" V/ D
  50. }
复制代码

6 X: U9 V; c$ c
- V* I8 Z" D/ b9 M* ]% D
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-4-26 11:44 , Processed in 0.155622 second(s), 24 queries .

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