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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
2 R& t  D* h0 z( V5 _$ r2 i2 A4 |8 s/ n6 A' ?9 Z

1 |+ @' g" d0 ]) Z3 ^socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。/ _2 f+ ]( C7 J  X2 F3 u

& ?! U  d( s' S  {. @) Y3 q
7 _0 x* a( A. t- s7 k0 R
TCP协议) @3 Y* N4 S% {# O* k( p* }: o( e
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。6 ]2 D. t# t# Q2 }

' c" \$ l7 W8 t& [6 f% d; Y

& Z0 o' ^  C  G% b! }$ E7 R$ R3 g9 w关键词:三次握手,可靠,基于字节流。
2 _; ?! S  |: O" w
( }* t7 `; }/ Z' u9 l; N

+ D, h( Q$ `, W- |  n可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
) ~$ o( X& s) m* u2 |
0 `3 w  U* p6 y1 NTCP服务器端和客户端的运行流程
( J7 |- t, d3 |0 H
' [1 U' H3 {, w# I6 |( f
/ Q$ \$ Q* p# E) [
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?! }7 U$ H# u- P, c# H6 m, O
  ]$ s7 B" Z9 H

1 L( r' r0 D+ [1 O$ M& w- ~4 m) P1.创建socket" h! |& U' x' Y" \
socket是一个结构体,被创建在内核中1 h) w0 ^* Y4 U3 Z
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
+ Z; _* g8 E% [2 U
2 G$ O4 f/ J7 E  G

& F2 S/ L2 U1 L5 w: T" z7 _' y2.调用bind函数9 D6 |$ @! N+ W$ g; s6 K5 N
将socket和地址(包括ip、port)绑定。
( G" C1 x$ u' u5 C+ L  q  Y3 E 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序7 ~& E; ]1 p6 F% |6 N0 F
struct sockaddr_in myaddr; //地址结构体0 X5 x8 l6 ^# m6 U
bind函数5 r9 F, f; D% e5 l- t7 `3 x5 p  c
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% h: j# f$ T* G+ u  j6 h5 y& {& ~
- o) K4 T6 @; i2 O

4 E) i  O& D! b( J3.listen监听,将接收到的客户端连接放入队列
1 s  |$ {+ d& j! `+ O/ v listen(sockfd,8) //第二个参数是队列长度8 L! L0 b4 X& P- S) L& X" ]$ k* ~- u
# p. p; Z1 I) f  y( U3 ?  [

/ x# d9 _  K! U# w" R1 I( \- n4.调用accept函数,从队列获取请求,返回socket描 述符
0 C0 i, v# n/ a  如果无请求,将会阻塞,直到获得连接
& }9 ]# U; O% y' i, o' h  m  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
2 O/ m9 `0 y* g  _9 R5 S, {; M4 v3 w2 ]3 @. Q) g& C! _' h8 u
/ N$ j6 N3 L5 D4 ~4 `1 d9 [
5.调用read/write进行双向通信) d  S; e3 Q, |2 u+ L$ i! g& }
3 I6 U2 ?' z# f6 B8 S- H* o. N/ p) ~

7 d# [& B- y/ E' N7 p2 T  j6.关闭accept返回的socket
+ X$ m+ P8 X6 u. G. U1 Y* [  close(scokfd);0 E. P+ U9 |4 _2 S1 A

- B/ l+ h1 }6 q2 }* `- O
7 ?  [6 M! C: @: v
# J% ~; r5 t5 y8 C' \( B' ^0 C$ W

1 i( d! ]  h5 Q! a下面放出完整代码* _1 q# Y" D  e6 K# t2 R
8 ^; v) \" G# A( u9 ~7 ]; }
  1. /*服务器*/
    " w: Y' T5 h1 ^& Y
  2. #include <stdio.h>3 d1 Q) Q0 s  f1 J4 O9 ^( X; y7 L
  3. #include <string.h>
    ) m( l: p% `& x3 P, e2 L5 t) G
  4. #include <stdlib.h>" ]" Y* t+ a# ~5 ]
  5. #include <strings.h>
    ) ?  M0 y8 r( h$ \' l5 q( f
  6. #include <sys/types.h>  G! \- t% Z7 o# s8 G1 L
  7. #include <sys/socket.h>* n! X, J3 n3 U
  8. #include <arpa/inet.h>9 K2 @2 a, [, c7 @
  9. #include <netinet/in.h>
    8 |. U$ r: \0 V5 `
  10. int main()
      T( ^0 m% g/ ?
  11. {
    ' m* v: b( |& Q
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- P# M- O4 |$ L3 w
  13.         if (sockfd < 0)
    % H8 L5 P% C+ Y7 W
  14.         {# L( }" H+ w2 e5 \0 x& t5 p+ p
  15.                 perror("socket");
    * f" p8 X3 F4 S, o1 ^
  16.                 return -1;
    , ?: ~3 z! d- ?7 W* N
  17.         } //创建失败的错误处理
    4 A3 V7 q- k& o5 a0 {( K( {
  18.          printf("socket..............1 j/ v  V- w3 T2 n0 j& N  f$ j( x, Z) B
  19. "); //成功则打印“socket。。。。”
    # {( z0 y7 L: U5 @2 p
  20.          : Z4 `1 m% V8 i
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    2 w3 t; ~7 y' ^/ U" z# w" E2 `8 x
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)( V. |/ \: r1 ?
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型! ]  j3 R9 v+ s
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ; ^/ k$ W/ q9 u. s3 M
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    % W. N( G% Q$ \5 X( w) e6 ]8 W5 o

  26. $ d' S; X" j5 g4 W" |7 {
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    5 s# Q6 B; n3 d1 X. b' j
  28.          {
    + F' j: |: u/ n
  29.                  perror("bind");
    2 x- F  K# k4 d/ w' z
  30.                  return -1;- o' k2 X& m# @# O) p- z
  31.          }
    # N# V& G* P4 h
  32.          printf("bind..........
    2 _; p; s( M4 S0 i; k
  33. ");
    + c$ a1 U. R- l  j/ P
  34. ; Q6 m% R- E2 Q9 _' Z3 I+ I+ \% m
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听5 q- x3 d( N( }6 i/ ?1 p
  36.          {
    * z% Y- p* |3 X8 r7 C% `
  37.                  perror("listen");
    9 Z- [! E4 \5 w) m4 [& v
  38.                  return -1;, b) ]" g0 i* v, o& f: o
  39.          }
    " A5 R/ E9 |7 m& }2 C, j
  40.          printf("listen............
    2 @5 V1 I1 T' }* A
  41. ");
    $ S! U  Y2 B* B7 X1 b: J2 C
  42.          
    1 O7 D$ H4 ~& ~1 p/ Z7 T, n. I1 ~
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求) ^% v0 T1 E$ o4 E$ O0 j" W: t
  44.         if (connfd < 0)6 t" p% t! Q9 `0 g, `0 V- ~. D- C
  45.         {
    : Q/ q+ b7 k6 ]; i" F( W
  46.                 perror("accept");
    7 R! }0 t& x# _8 ]! m
  47.                 return -1;4 X( w3 S9 l/ Z9 N
  48.         }
    : l( @0 a1 f; p$ o! |+ u! J
  49.         printf("accept..............
    ! C) L1 w7 ?" |0 `, |
  50. ");
    % K$ M5 @2 H8 q) K5 c7 M4 Q2 X: C
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    " P) l* U; j: Z
  52.         int ret;
      x. G6 R1 }$ U; p' Q
  53.         while (1)# G% u. F% g7 D; U* c$ k
  54.         {
    ( l) f2 p- R2 j. p
  55.                 memset(buf, 0, sizeof(buf));- ]& w) L/ Z1 U0 x9 k, L9 I5 L
  56.                 ret = read(connfd, buf, sizeof(buf));3 h6 y4 l$ O# u: H6 V, T* W
  57.                 if (0 > ret)4 ?3 M! p* a4 g. k0 y" [. o
  58.                 {+ q. l5 F& Q) V5 k" T4 K4 u
  59.                         perror("read");$ s+ J3 v% z  T  z' ~* \
  60.                         break;
      w# L, A4 d/ Z7 c& ^
  61.                 }//执行while循环读取数据,当( `" }) Z% @5 }/ X) G% J
  62.                 else if (0 == ret)
    # Z9 m0 ~3 Y5 o: S
  63.                 {
    9 p8 F% t9 W1 R2 s# s$ e/ u" k
  64.                         printf("write close!
    6 o* `" }% P4 I% r' D
  65. ");- v$ K+ Z1 N$ L2 L  x, _& m
  66.                         break;
    7 K& S  ~' z. ~% M
  67.                 }  Y  h& f& S: }# C
  68.                 printf("recv: ");+ `4 ^2 E# s5 }
  69.                 fputs(buf, stdout);//打印接收到的数据
    % a9 R( }( }: F4 i
  70.         }
    * k- i; j3 \( h4 d7 ?$ P8 U
  71.         close(sockfd);//关闭套接字/ \5 V" Q" }6 L. `
  72.         close(connfd);//断开连接
    % {7 l/ ~# X( E0 a8 A% y8 E. J
  73.         return 0;( T+ D6 I% ~+ s1 p( ~
  74. }
复制代码

! O( N' v3 G% y. I+ R9 C, A8 U' S. F. a) r( ^
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)" X4 t: X- g" ?! {+ K7 Z9 Y
  2. #include <stdio.h>+ i$ M: V  w& S' b) R
  3. #include <string.h>
    2 r# @: z* j1 E
  4. #include <stdlib.h>* E3 ~' W  h2 u4 N6 V$ s
  5. #include <strings.h>
    , ^+ I3 K* v  d" X
  6. #include <sys/types.h>
    % K. ^- _# a5 |
  7. #include <sys/socket.h>
    * x/ g9 O! l9 S# Y' }
  8. #include <netinet/in.h>
    . ~; L% v! e8 G
  9. #include <arpa/inet.h>
    4 K/ {/ k7 `' j* W/ Z+ f5 `+ |
  10. int main()
    $ ]1 e5 m5 B/ s* _+ F" m
  11. {
      z  ], l- c+ T# {0 w- H6 F# o! b
  12. int sockfd;6 \1 Z0 C! s+ u% O& x% d/ z( W
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ) d1 t3 ^- h0 F2 q( d8 \. a) }; N
  14.         {! L  S$ o8 ?/ ~  ]+ v/ ]" H  [/ \
  15.                 perror("socket");
    ; j) ^+ t0 l1 s
  16.                 return -1;
    5 ]$ z$ K, A0 _, p
  17.         }7 i0 U* \: B' O
  18.         printf("socket...........& v! Q2 r: h% T
  19. ");
    4 e. z& S3 e" Y  y
  20.         
    3 s" m4 E; [. s1 |, c1 p0 c
  21.         struct sockaddr_in srv_addr;
    5 |9 g! K. [7 b( `" w
  22.         memset(&srv_addr, 0, sizeof(srv_addr));+ w) {8 S* {6 L& Z/ O" Q1 V" v) s
  23.         srv_addr.sin_family                 = AF_INET;
    ; n' I7 }& c* A! S
  24.         srv_addr.sin_port                         = htons(8888);  L6 x! G" B4 h) o1 Z
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    : w) w5 [+ W; E5 o! @* d( F
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    : r* {* g$ ]  A+ \4 J2 A1 a
  27.         {
    + k  \) \; U) y0 D; J7 S/ U
  28.                 perror("connect");
    , ~( w6 S" |$ n2 D; i0 K, Z
  29.                 return -1; //exit //pthread_exit
    7 |  t/ a# G2 i7 N' j$ ]1 o7 O
  30.         }- [9 o' X" m2 _; @/ r
  31.         printf("connect..............
    9 P7 @5 C5 s$ j* S
  32. ");+ ?3 b  |0 W7 h) V: {. C* Q
  33.         char buf[100];
    ' N4 R" |! G; I9 K: p0 `' p+ i3 m# K* J: ^
  34.         int ret;
    . M  a+ [2 h1 X  J; y
  35.         while (1)
    9 R8 R* d, O; H# |# B' L8 B7 \& I
  36.         {- ?4 n/ @! Y- i# K
  37.                 printf("send: ");
    - g& Q2 S$ w, {/ }* \
  38.                 fgets(buf, sizeof(buf), stdin);
    ; D7 Q; [! W5 ^$ I9 s5 `
  39.                 ret = write(sockfd, buf, sizeof(buf));4 e8 y: n5 q) ^& {0 X9 I/ Q0 g( o
  40.                 if (ret < 0)7 r' k5 r- s- }
  41.                 {5 ?8 Q7 |; J5 E& \+ U5 B
  42.                         perror("write");
    7 s6 {% Q. h; u' C/ r
  43.                         break;+ U( z- `) l0 b+ @
  44.                 }
    1 i4 b' u' l3 O  L
  45.                 if (strncmp(buf, "quit", 4) == 0)4 r1 k1 v7 u- Z) G+ |0 j" S) ]0 [# P
  46.                         break;
    : n. T+ M( S# c( y
  47.         }
    & D  u1 P6 G( T& V
  48.         close(sockfd);
    6 P( p. d( q+ `. q
  49.         return 0;
    2 |6 C# A4 P& K5 p
  50. }
复制代码

0 t( T2 \4 ]& \6 A1 r4 w! x$ Q6 @% t( G
  u! W1 U3 b) D
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-6-1 18:33 , Processed in 0.122758 second(s), 22 queries .

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