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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
8 j* g1 N! }! N' {. P# K
" M6 {  i3 H! G4 P1 z

0 x  @* k: P8 L$ B' n1 N/ Fsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
) W+ H2 i! Q1 S9 ^- ~4 e8 b4 v2 x) k" N; v9 t$ g: o3 t# z1 g9 j

7 c! n9 v" K" D& c/ R$ c$ |TCP协议
. i% O  d- R* j; ?( c- NTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。/ a6 y: D. A+ I4 J" V
$ @& M1 B/ G! s

6 t4 z2 k0 _9 M5 s- G' t7 s3 c关键词:三次握手,可靠,基于字节流。9 O/ A2 u3 l7 z& X+ d

2 C+ i( f. u0 Z) u  m% W4 T- a
/ W- l& o5 B& b4 B6 C
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
" Z4 Y+ y/ Y% B7 f5 H+ h( q   o1 |& p/ G) ^- w! t8 q% C8 U4 s% ^
TCP服务器端和客户端的运行流程
, M1 W( {+ E+ ], V0 U) D5 |* J' s! t9 `) v5 X% F) }7 O# f( ?; D
0 T7 M  t% t9 f& `
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?3 Y" n8 n! p+ v- @
" b# w  C7 v3 i+ X  l$ n
# J# d3 A& x% r' A; p7 {
1.创建socket
2 I. `# u% g+ _( m socket是一个结构体,被创建在内核中4 Z4 T3 {* _1 |) f3 I, z. |
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议4 E( M3 u. B. x# G# G

! S1 s% i0 @8 T7 w7 ^. c

9 [" o( p+ I: ~) H% w2.调用bind函数- R# F) C. O2 U- e6 `# i- b
将socket和地址(包括ip、port)绑定。
8 j6 g6 M5 g' l! C* n% B 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
4 L1 K7 @, D+ Z5 |: P6 { struct sockaddr_in myaddr; //地址结构体
5 _' ~: |0 b3 l& B/ L# a bind函数
/ a  R8 c" Z, \: S, m5 Q bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)), \% U* b, l9 i1 M' L, E2 V
6 o; @; Y; Z: V9 |" A7 c- t% ?
; m: t" o6 g, T+ y
3.listen监听,将接收到的客户端连接放入队列' g( C& `9 @! z! _7 b3 ]
listen(sockfd,8) //第二个参数是队列长度: e& o, V# e  e

# e3 h- W5 }, p" N+ I8 o8 H
# T( Q* F1 O$ U/ t4 y2 C' B1 i" W
4.调用accept函数,从队列获取请求,返回socket描 述符
0 M' C3 z3 g+ k/ Z6 F. @% r  如果无请求,将会阻塞,直到获得连接3 V8 Q" ~. X6 B6 V
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
* Y; E/ \1 m0 b
# g) ?+ J; u9 y
$ L: r6 f  Z) V6 Y7 B
5.调用read/write进行双向通信7 A! @3 u# t6 g7 R8 W$ o0 C( h
7 k3 Y( u! p/ M/ B; p% g

6 h, [7 k; o1 D# u" q# K6 ?# o: @2 e6.关闭accept返回的socket$ x0 v( c$ j# K; y4 ]7 L' R, r
  close(scokfd);% t" ]. Z' O4 z: ?0 W# S; r2 m
) y0 c8 K) c. w! O8 l/ C
/ y: N  k! ?" N* }; v

( z: }0 Y; U4 s
: F5 k7 C. P6 @  J4 H
下面放出完整代码/ B0 W# ]+ v- s( d
( h4 o5 ]) B4 r3 C2 y
  1. /*服务器*/" _4 W9 M) N4 C' v* G0 O5 e
  2. #include <stdio.h>
    ( m2 O, ]6 u( x. ]0 X& h" t
  3. #include <string.h>
    2 A' p0 t2 \7 f( O7 \  T: g
  4. #include <stdlib.h>4 L( i8 Z& y/ y$ V
  5. #include <strings.h>( V' }5 c  K; h; L/ S( V
  6. #include <sys/types.h>$ K; t& [4 n+ x7 D6 m" n
  7. #include <sys/socket.h>
    - z8 r- V2 Y7 `. M3 |) g
  8. #include <arpa/inet.h>" i. W# A3 z* ]6 j9 ^
  9. #include <netinet/in.h>
    , j6 Z" r/ x3 w- S
  10. int main(), L/ t" D2 i5 H
  11. {
    " {9 k8 }; n! L, e, L2 g
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    8 [- M$ M, z+ M& H$ |( i& E8 r
  13.         if (sockfd < 0)
    9 |, ~4 W" Y# j+ y: y' E6 @/ D
  14.         {
    9 C  J! N4 Z0 S9 i9 H- T
  15.                 perror("socket");
    1 _3 _# q+ [+ D. m: f$ w
  16.                 return -1;
    9 S3 i  V0 s4 b* t
  17.         } //创建失败的错误处理
    " H: l% G' H2 R! @$ k
  18.          printf("socket..............9 [, \/ ^' n( b+ W, [+ \+ C
  19. "); //成功则打印“socket。。。。”1 i4 {" ^2 U3 ?
  20.          - w( w4 P+ g- \5 x
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    6 |. m# Q$ y. B( @! [
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    1 _8 G; p) }$ d% P) n
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型8 t6 [- h# U& d6 L: j+ w4 J5 `
  24.          myaddr.sin_port                 = htons(8888); //选择端口号/ c+ G; x5 d* t/ g6 ?" I- B
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址& G1 }, O' T! k8 @
  26. 4 m* R# i! v: y
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字7 ~" v1 R, L% i8 j4 H! _# e* A
  28.          {
    0 K9 O0 \3 {$ o7 A# p8 S" b
  29.                  perror("bind");" Q# U' N5 B0 U7 j
  30.                  return -1;) q" F* i5 j" z# t0 x5 g! G9 i
  31.          }
    & i  t$ Z9 }4 ]* Z" q
  32.          printf("bind........../ T% u2 M4 ]  x0 `% q7 C* i3 j  R
  33. ");
    1 T, J! g0 }) r9 n: q9 ]
  34. * y6 x5 K/ w1 ~9 w0 `
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    : l0 u/ d. V6 N% l" W2 V
  36.          {
    " z1 E8 M" |6 l+ ~2 P' v
  37.                  perror("listen");
    . u$ {9 [1 _3 m! c9 p8 _9 ?' O: q! _
  38.                  return -1;
      ?' T5 N% o' x1 W: k% k
  39.          }
    * p- q' [6 Y: ]9 V
  40.          printf("listen............
    % t: D; x+ G3 ?3 ?
  41. ");$ d' `6 U4 {9 P- T& D
  42.          . S1 V/ o+ q5 ~! [) T
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    1 Z; v6 k! B0 Z0 ?
  44.         if (connfd < 0)
    / \' C7 B  P; j: }9 u
  45.         {7 l. _, C% w9 h: n
  46.                 perror("accept");9 H* S" I& n7 J0 H+ Y6 B+ @. q
  47.                 return -1;
    5 P* D- X* I/ n% a& @
  48.         }
    / j! a0 x0 k# o$ l
  49.         printf("accept.............." I# O/ ]5 N, H: ^
  50. ");( Y1 r- Z! F3 E. W# P
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    # e  X: i9 f  {2 t; ]8 h$ Y* u
  52.         int ret;
    0 }+ B0 \# z0 l# ~$ ]! O& S$ ^
  53.         while (1)
    ) m( @% ~  Q; f6 a0 J
  54.         {
    6 L4 O+ |: |% Y2 D
  55.                 memset(buf, 0, sizeof(buf));
    5 L! X* G; C! |/ ~( v
  56.                 ret = read(connfd, buf, sizeof(buf));! a2 v5 `5 I0 t# _# d
  57.                 if (0 > ret)" [! x' B) s8 q( Q2 d) r( g
  58.                 {* j1 c3 o- D% ?( k2 T; [" v
  59.                         perror("read");! {4 J. h$ t. M" J7 J7 `! H) W/ w# ]7 w
  60.                         break;
    : g2 l- O) A1 r5 e
  61.                 }//执行while循环读取数据,当
    9 p( ?+ G+ O6 o& }( R
  62.                 else if (0 == ret)
    1 B5 {. S7 {/ y( J
  63.                 {7 r# g" L/ H5 A! R' n) G
  64.                         printf("write close!
    9 w. z- w! I9 a9 Z
  65. ");
    : c& D& T7 J9 |! a- [8 x2 T" u
  66.                         break;
    $ c! ~0 c) l/ S2 X* v  T
  67.                 }
    4 N) v/ z; |9 j% T5 n& w# w
  68.                 printf("recv: ");
    # ^& C3 M2 ?( b, e* J
  69.                 fputs(buf, stdout);//打印接收到的数据# m: ~- k; e' \! O* c3 q2 d. l
  70.         }8 L/ m! q" f. K$ N8 u$ {3 p
  71.         close(sockfd);//关闭套接字: V# d& g9 d. L2 ?0 S5 M
  72.         close(connfd);//断开连接* K/ Q1 P9 O; ]/ a
  73.         return 0;, y! E3 W. r7 }1 p3 Z& N* o6 ]/ y% C
  74. }
复制代码

& _5 S0 {- a- r4 q/ v/ E6 F3 D, y: h, i
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)5 ?. T; R# ~$ x4 ]
  2. #include <stdio.h>1 P4 U- r/ K% ]. ]! g/ ]& E1 L8 e8 f  j
  3. #include <string.h>3 I$ q) z- c3 Q/ v
  4. #include <stdlib.h>" X& k3 N- ^4 o! D- a- `, \
  5. #include <strings.h>+ s- U7 J) U( _
  6. #include <sys/types.h>
      f5 l- p. F2 F
  7. #include <sys/socket.h>3 q* X& q( M0 e8 }1 l
  8. #include <netinet/in.h>' M- ]9 |! s' i, C& W3 w6 q) k
  9. #include <arpa/inet.h>
    * [, U) W6 i' n6 ~6 K2 [3 M
  10. int main()
    * F& [$ o  d  m9 o- v
  11. {
    , Q  {* \, O( c& C7 I" p
  12. int sockfd;6 N* l' F! ~. ?; k! }2 p2 v* f; G! \, \
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))) H1 p0 k& `1 v7 x5 r) e( C; H( W3 w
  14.         {7 y' L& i. z3 H3 x$ y# F
  15.                 perror("socket");1 ?( X& m! R; d, `; q- n$ a
  16.                 return -1;
      ^; ~6 ?! ]( I9 ]2 M
  17.         }
    4 L# J4 l; \6 z6 g+ E% C0 T7 |; u
  18.         printf("socket...........
    ; a* L, f8 W; z) ?  ~- r
  19. ");
    5 J4 c2 E9 t, L5 \+ o4 D
  20.         - b4 j  [$ I  v9 v
  21.         struct sockaddr_in srv_addr;
    1 E1 E! o+ ?# N# o# T
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
      Z: K# V" z6 D/ ?5 e' o% b
  23.         srv_addr.sin_family                 = AF_INET;
    ; |0 Z5 \/ m2 e! o3 y
  24.         srv_addr.sin_port                         = htons(8888);
    ! X$ ]& h: l  l: Y
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    7 ?& E8 ]2 m- I! o' C5 Q5 _
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))' R. `1 \& k7 q! Y8 s* ]: }
  27.         {
    & y) i  c4 H# @0 ]1 @  ^# X# T
  28.                 perror("connect");6 g8 ]$ y3 V1 r9 d" A, I! S
  29.                 return -1; //exit //pthread_exit+ _& A# `1 `3 ^% V- p; I
  30.         }
    + A' n" H/ O7 f% w
  31.         printf("connect..............4 F  g1 U+ m- E' N0 ]0 B
  32. ");& f: N1 B: C5 Q3 V( i* j
  33.         char buf[100];# ^8 s& n7 o, d7 T8 T% Y6 r- w3 s
  34.         int ret;5 R( `* s. r- m" K) I
  35.         while (1)
    8 G  y) z$ a; K6 L. u4 C. @
  36.         {
    # W5 ~- C5 D6 u4 \
  37.                 printf("send: ");: ^8 n+ t- |$ a' u
  38.                 fgets(buf, sizeof(buf), stdin);  P9 E5 j8 I8 R, Z' J. i) u% r
  39.                 ret = write(sockfd, buf, sizeof(buf));
    4 m4 i" I$ V8 @+ q
  40.                 if (ret < 0)
    " j6 W4 y" f& d' V  j# r$ v* F
  41.                 {. U' `; R6 g4 t0 C4 d5 l! Y% U' [
  42.                         perror("write");
    8 W0 H! u5 k- y. {* U2 Z
  43.                         break;' P/ N- X: O% \7 v/ X: M3 {
  44.                 }  Y% k; b: V- G6 ?1 Z5 V5 o
  45.                 if (strncmp(buf, "quit", 4) == 0)0 x( p7 G( h8 a3 C
  46.                         break;; Z3 t5 [( I# `6 g- A+ R! K  a5 E
  47.         }
    ! I, Y6 J$ @6 V6 r$ p' c3 M3 p
  48.         close(sockfd);* {' t" A' [9 R7 f+ J0 j
  49.         return 0;
    5 u8 p7 {: d5 y. W) b6 X
  50. }
复制代码
8 G5 E- C0 e0 ~, L+ v' |: `

* E. e# R+ Y! w; |) n7 c
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 08:10 , Processed in 0.084213 second(s), 22 queries .

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