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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
, v! \  o* A: \3 I( _' X) I$ E7 W5 m( V. x# h9 ~; M: P

1 S: Q0 W* {3 C+ v4 U* Wsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
0 N) ^6 ?. F7 `
1 z0 I" @0 ?3 m4 y8 ?
+ i6 S; U8 ~" g0 O5 P+ [8 }8 U
TCP协议
, r- J! ]8 K/ l* FTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
0 r; H5 X5 D) J+ U! D4 B4 \6 _. h( l9 \
6 Y+ q) T9 l7 g1 j9 J
关键词:三次握手,可靠,基于字节流。
# }% l% |) e3 Q. ]( m9 E' b5 c: f! Z- J, [

  x. W) j2 D: [! _  K4 l7 a1 M可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。$ @4 k9 Z% r( E+ i0 q! m5 K
3 a: p" C# M+ a+ j7 }6 R# y8 ^
TCP服务器端和客户端的运行流程$ Z7 z2 ~& x4 s; [2 f3 C$ `

. m" B1 T- a9 n# R
- u9 x. d* p7 N* u; [9 W* ]
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?0 Y1 _! e: t$ y! ~- L. x
5 N( Z& i! x- l. l) y5 D0 i( r
% {. v) w+ d  I
1.创建socket
3 {# m9 }7 D- V socket是一个结构体,被创建在内核中
& p5 {# W8 m( s! K3 O sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议6 @! O8 q7 f( S5 l& u1 V% ?2 D
7 \& u9 L9 f) @% l9 X, V( {
9 J# l! F" X" D- @2 E
2.调用bind函数
' O& n7 \. l( J9 F& C4 a 将socket和地址(包括ip、port)绑定。
7 E5 n7 r, \3 z+ b1 r# P 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
$ X, }) K1 E! w& E7 r0 R# c. m struct sockaddr_in myaddr; //地址结构体, k. G- Q$ s7 k5 `' `
bind函数2 Q  N% O# s1 A, V
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
1 d  l1 A% n- X  L
2 ~: Q3 K: P5 Z: H

. I# y0 Z% \  [  d* t& z3.listen监听,将接收到的客户端连接放入队列
8 d- `( G, z* K) b, E listen(sockfd,8) //第二个参数是队列长度
% F: d4 Z$ ?5 O& f5 I3 G8 M. o" D1 W4 e7 m6 r0 ]: F

# \+ H8 ~2 O; G5 Z8 e4.调用accept函数,从队列获取请求,返回socket描 述符( l) B7 }. W! ^3 A/ i( C. d3 t& y
  如果无请求,将会阻塞,直到获得连接, m$ s" Z. D0 h8 P: W
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
3 r4 \6 A) R( T' ^1 ]8 |
. v1 q5 N, e% J3 p9 z
% ~$ s/ s8 M7 X% B% I; Z( m
5.调用read/write进行双向通信
6 u+ E: j' A0 s3 j6 c8 L, x  c( G/ w  v
, c4 z( L2 Z* }) g) J; ?" ^; C- T
6.关闭accept返回的socket
9 l; P) t/ [9 u  close(scokfd);7 k+ G, Y" q' G) N6 ^4 h4 F) ^3 M

$ A/ `+ [; i" ^4 V

8 Q2 A1 d+ L4 C- o  h) l& X* Q  [4 b) g0 B( w" _" V) }+ c7 b

1 P7 ^+ m8 T8 ?7 P' C/ W$ M下面放出完整代码8 x/ Q. [1 ^7 o8 b8 O
7 U+ N0 K2 g: [. g4 Q1 o
  1. /*服务器*/
    $ V5 o1 O% {. x& a5 C6 M- ^
  2. #include <stdio.h>
    ' g* N4 a8 m# c5 J% B  q9 z9 t" P- @
  3. #include <string.h>% Y# |2 ^& W+ g# u$ Q+ W
  4. #include <stdlib.h>
    1 x5 b" o7 t/ G; ]  V
  5. #include <strings.h>8 S6 o0 J8 f! O) O' v$ \
  6. #include <sys/types.h>* x1 ]1 @8 q9 a" E9 k  {5 M
  7. #include <sys/socket.h>' m5 v/ R1 E, I5 ]
  8. #include <arpa/inet.h>
    " D, P3 f" O0 J% S
  9. #include <netinet/in.h>
    " F; ?% E# G# x7 c
  10. int main()) Q% L4 R: I; f* ]+ O" Q; i
  11. {
    / H8 W6 R) P  i
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字8 [" h9 W; E( v2 ~  W  {! H: Q$ U3 Z
  13.         if (sockfd < 0)% ?2 @, G3 E  A- b) V- F6 L" g
  14.         {- H0 r# u9 e+ T! s# i7 ?
  15.                 perror("socket");* e" r& N- [; B1 ?: T9 w: ~2 Y
  16.                 return -1;
    9 S4 ]$ ]2 z( _# a
  17.         } //创建失败的错误处理0 m" c* i* r9 Q5 }' B
  18.          printf("socket..............
    % z5 h3 H/ L  x
  19. "); //成功则打印“socket。。。。”
    ! S1 d" F( C( R9 X8 x* s0 y4 w
  20.          / z7 B! Q; y$ x. y7 g
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    7 H9 ^$ L: d: S9 j5 }4 ?$ c
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)+ c* T( d0 P6 o) A; H- u
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型3 K9 q, Y% z8 h0 o! F7 h6 |0 L
  24.          myaddr.sin_port                 = htons(8888); //选择端口号; _% B/ x  d0 j7 p% E
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ; D  r* g: d* }( w  ~7 v

  26. ' B8 g# [$ J$ {
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字2 Q8 n, L/ V+ k1 h0 X6 F* W0 a
  28.          {
    + D. ], ]4 P) r) C# Z  }4 Z. n% |
  29.                  perror("bind");
    0 Z( d% f; _2 N9 u( [0 m
  30.                  return -1;) [5 ?5 K% }1 p5 S$ M# h$ V
  31.          }
      s: S$ t4 }( i
  32.          printf("bind..........
    3 P' S+ w2 n2 `
  33. ");& o- q7 K9 j& ]/ l7 b

  34. 2 B6 A( Z0 v. ~) S( H  [
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    9 m" K$ S/ L- M
  36.          {
    + F0 ~' c, h: K3 m; e; n
  37.                  perror("listen");! A! N5 S/ t6 d! h; ]* o& U/ L
  38.                  return -1;
    / w: F3 g7 T5 X& y# b  |& X
  39.          }2 r# b. }+ c% M. [( q6 F
  40.          printf("listen............: ^! B2 K9 r/ j+ P, ~* d
  41. ");
    , s! J1 e! F, E; _( J3 ^
  42.          
    & p1 J0 n. a( c6 G* N
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求/ s8 x9 s& A7 J8 |
  44.         if (connfd < 0)
    : F& ?( r& a, [& r
  45.         {2 I3 l# a/ u/ F) ]
  46.                 perror("accept");. j0 c& _% Z; `" t( P! u
  47.                 return -1;8 j" F9 r* W# ^+ j; v
  48.         }8 L& Q9 R  l, n- i0 M& k, O
  49.         printf("accept..............
    5 O, U7 D4 B1 l  g: @& ^+ z
  50. ");3 q% L" [, v8 G" u. l* Z
  51.         char buf[100];//定义一个数组用来存储接收到的数据% I6 f. [6 T; ]8 g6 L5 E
  52.         int ret;8 m, `, I8 Z- `5 A$ A
  53.         while (1)1 n: z- |5 p8 X7 F/ F# N
  54.         {4 R7 o9 x- ~5 T. w. Q
  55.                 memset(buf, 0, sizeof(buf));
    % z( W4 ?% e5 U  @; X+ D% |
  56.                 ret = read(connfd, buf, sizeof(buf));
    ! N( p  u$ e9 ]3 k& b8 Z" _- ^
  57.                 if (0 > ret)0 Q( E+ g6 A, t% X7 i( [
  58.                 {
    , Z1 y+ l$ }- J9 c0 Y4 c
  59.                         perror("read");7 _; Y7 b  q; j* y
  60.                         break;
    + x  X# x2 O  ~& f; ^
  61.                 }//执行while循环读取数据,当
    8 e" b2 k, K- O; Q! }
  62.                 else if (0 == ret)
    ! K7 G7 `5 C8 a
  63.                 {
    7 L2 d' C2 V; E8 E* E; t
  64.                         printf("write close!% t7 |8 n+ P9 s, Z
  65. ");
    * M2 S7 o! C3 W7 W. N' M
  66.                         break;( i9 N# `9 h# h1 _* e2 p# @# B: j1 q
  67.                 }
    , L3 |1 j/ b9 h& @5 e& T
  68.                 printf("recv: ");
    * K6 W8 K5 A4 o% Y( ~
  69.                 fputs(buf, stdout);//打印接收到的数据2 b% d9 O- ]1 t  ?/ k: @2 R4 d) R
  70.         }5 d. E. |$ Q2 I1 Y. ]" ~
  71.         close(sockfd);//关闭套接字: D. R$ V6 K1 S" `: h3 |( R* w6 h
  72.         close(connfd);//断开连接0 _9 k: i" K# ~# O, X
  73.         return 0;
    / q- G' J" i9 i) w  l7 A0 }
  74. }
复制代码
1 M( D8 u9 h: E; u8 R1 d0 `
% d$ Q  Z: r2 j. r% [
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)" O: A. G: Y2 @/ ]
  2. #include <stdio.h>
    6 G: t5 }) F  m- `, S; n+ y
  3. #include <string.h>
    , a" O4 I# O5 q' G
  4. #include <stdlib.h>, {* b$ w. k0 ?3 C) x) s
  5. #include <strings.h>' {: @) u( f" ]$ B
  6. #include <sys/types.h>9 t' }+ P( n+ y  p+ Q# L3 d
  7. #include <sys/socket.h>
    1 D- v' n7 a+ f( M- I3 W
  8. #include <netinet/in.h>
    4 m9 e' j" L7 \
  9. #include <arpa/inet.h>$ E# P# I$ ^9 y  Q. x& \$ D, w, w! Q9 Z
  10. int main()
    ' o" D" ^+ f- Z- r! v( x+ P" G
  11. {
    1 O0 q0 O- N4 [' m
  12. int sockfd;
    $ X4 b7 z1 Z/ @6 d
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))) a. Z0 \$ w7 m8 V- j6 N$ n
  14.         {
    . w  T6 b, H& N( \+ H0 o* K
  15.                 perror("socket");4 a5 t: I3 E1 R4 B1 Z+ f
  16.                 return -1;* L6 k$ f7 Y3 t9 e( f& \
  17.         }
    " ]! w6 w1 q* ?& ~3 G8 j! `9 c
  18.         printf("socket.........../ r( J$ }% D+ Q% q! I
  19. ");* z# t! A" _" x9 A
  20.         
    9 _# q5 a% G$ T. j7 Q
  21.         struct sockaddr_in srv_addr;# y0 J. @5 J7 T% S# ]6 D# D0 ~
  22.         memset(&srv_addr, 0, sizeof(srv_addr));( P( ^# m2 T$ O4 a: z: C* T
  23.         srv_addr.sin_family                 = AF_INET;+ j* x3 D7 b# Q$ |+ F/ ?+ F
  24.         srv_addr.sin_port                         = htons(8888);
    7 q" O5 u: [& m% W4 D
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    3 L, @. h  j" e4 a( V4 ]" }4 c
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    4 r; l6 X8 l5 m) t
  27.         {% X  o( T2 w$ ^# D4 ]; @
  28.                 perror("connect");3 P& X! w- o( q" a
  29.                 return -1; //exit //pthread_exit
    ; ]4 M4 h# y: Z& }
  30.         }
    0 d; O* w0 h1 M/ i) W
  31.         printf("connect..............  A+ Y; R0 G$ }/ L) F: O% O/ H9 _
  32. ");
    * @# |* h1 I  R$ E
  33.         char buf[100];$ {  P2 d+ b) h( K1 [+ j, Y7 V
  34.         int ret;
    0 z. n3 Y* {: B5 W
  35.         while (1)
    9 \. H3 Z& d( A1 Y" r) L. @
  36.         {4 \0 M8 i6 v* b/ e
  37.                 printf("send: ");
    ; Y- Y' T' s8 m, N
  38.                 fgets(buf, sizeof(buf), stdin);
    6 C4 T% `* k3 I5 S& a$ t, G9 P9 X
  39.                 ret = write(sockfd, buf, sizeof(buf));# @3 R2 ]# r) ^( f: h; ?1 D, U$ Q3 l
  40.                 if (ret < 0)
    9 d- d6 U( k' {. H, e; U) \( v: C  ~: n
  41.                 {7 ~7 R! L6 z+ ?# U- O; a+ i9 H7 O
  42.                         perror("write");8 q$ p0 N+ _% P% [: n' c
  43.                         break;
    : w3 K8 _' c' S( x- ~3 Z
  44.                 }2 p3 d9 d/ v+ h
  45.                 if (strncmp(buf, "quit", 4) == 0)
    $ V; u" T$ q% [* I& O  ~
  46.                         break;* E" b0 [" R. k! [
  47.         }' R, E5 ?) X  j! I/ c1 _9 o
  48.         close(sockfd);
    + R$ }; W+ F0 M
  49.         return 0;3 R& z5 h: K) V9 o9 s( w
  50. }
复制代码
" d# u5 x- T; I# e$ {' `
2 ?/ o3 w! q  P) ?+ l, N# t6 ~
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 00:46 , Processed in 0.153404 second(s), 24 queries .

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