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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。' J( }9 Q/ S2 D' z" E
3 C- l- T0 {* c! T0 E
+ @4 b+ ~+ u7 T  y' k
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。3 D, g+ f% {* @
, K2 _3 J) |* }
: O, H$ ?9 _! {3 G' I, [
TCP协议
! U7 H5 I; n  p9 {9 E6 s2 ZTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。$ I( Z5 E6 J$ _' c
' d& B0 V1 E* q4 ~* q  N

; [: K! ~/ j$ c+ }# @. m% \关键词:三次握手,可靠,基于字节流。
$ {$ W5 m) M5 p3 \: l" n: ]- L/ B  m1 ^! m; E: W1 G* e. t
$ @' r9 Y) }0 b: g
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。7 H! q/ r, M) T( j' O. t' o3 g+ k5 T
4 T6 E8 |( v) N: g% ~+ [! }7 f9 A
TCP服务器端和客户端的运行流程
/ b* @! Z1 y( Z0 a# c7 @& ~# \! [4 O2 ^8 G2 ]* \7 Z
( w9 I0 h' ?  y
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
8 U# {; r4 e* \3 r: g& |1 K: Q* G8 k  b

0 h9 t6 Q$ S% ?7 ~7 v/ p# A0 f1.创建socket
. M" k9 e  d$ V socket是一个结构体,被创建在内核中% D; P& B8 H6 |% }5 a: _
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
' Z  c2 n, l. j; n) s  T' i2 o* Y9 I/ M+ M! S$ H5 \$ y) K* I) d

. S$ e8 J# p3 t8 e6 W! V2.调用bind函数
$ R' |( O3 u' d% W. r! H 将socket和地址(包括ip、port)绑定。
' r" i6 v- ]9 J" k 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
* g  g: R3 }- u; G struct sockaddr_in myaddr; //地址结构体
  Q* ~, N0 o* r% F5 O# T5 j: p4 S2 w bind函数
' c; d$ M6 U2 r0 v( }/ @5 r: n bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
$ o/ E# |4 x! i1 |1 Y
" D. w* h3 z, F: _$ T

( T! ?2 y: L. B# S6 q7 ^/ i1 k/ J3.listen监听,将接收到的客户端连接放入队列: H* [0 S7 d$ w/ @: _  g9 o
listen(sockfd,8) //第二个参数是队列长度
, A9 R5 ~0 o6 u2 `( ?) G: z( f7 ^5 w$ J

- R, l+ ]2 V) D4 M9 j# R7 b9 e4.调用accept函数,从队列获取请求,返回socket描 述符, `0 d% L0 y  S+ m! W: i1 P! F
  如果无请求,将会阻塞,直到获得连接
1 {0 b; i6 h' I7 {0 X1 ^: g8 E' t: A  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数' z5 X+ U( ]- e5 L+ |

% p( K9 l* y6 Y! X* ?" {5 R

& H. ~& h! s5 h/ g; N) i" |5.调用read/write进行双向通信
& o: J+ h8 m/ U/ Y2 s  L7 \9 d' V0 i/ V

! W6 c; K$ \9 a; X# y1 L4 y6.关闭accept返回的socket
. ]4 T: ]- ~' L! a  close(scokfd);0 F! L$ l$ y' Y: j& E& l
* H; L6 x- S4 I! E' g8 y
; ?# g3 G. \- h# `

- j, z' }9 x! n3 M4 e7 W9 V" ?6 X
! P# M! n; w: f% |  S
下面放出完整代码& }4 l4 A+ l% ?5 L4 a9 y
3 P: h; i5 y8 [; Y9 k
  1. /*服务器*/
    6 q' n; k* |0 X, [/ w  k: c+ L
  2. #include <stdio.h>+ L) _, T6 D/ b: v+ ?7 o  c
  3. #include <string.h>6 m% B1 Y( {/ a% U9 w* n0 F9 {
  4. #include <stdlib.h>: N) x/ V( W6 K  d$ V
  5. #include <strings.h>" S3 ?  s1 C9 q+ n
  6. #include <sys/types.h>2 D  ?  S& f) f! \/ N
  7. #include <sys/socket.h>: A& @3 p" f( s, T7 Y/ g
  8. #include <arpa/inet.h>
    4 [! w0 m& p! h1 z( F
  9. #include <netinet/in.h>
    2 f2 F: c! Z( h
  10. int main()
    ( }$ F9 }8 u7 g
  11. {6 m/ ^# f' f0 H3 K* \5 _: ^1 U
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字/ o, R8 H" U. p1 O( q
  13.         if (sockfd < 0)
    9 E8 ^+ r, `9 M1 P& G2 x" N# R
  14.         {% O* J- b' r) H. _$ |2 D
  15.                 perror("socket");, y4 w& q3 S; C0 _& U
  16.                 return -1;
    + L& B, C1 V! J7 g+ r  ?  g; ]
  17.         } //创建失败的错误处理
    ! u# ]% r4 B' u: J$ S
  18.          printf("socket..............
    : p' g' p/ W- h
  19. "); //成功则打印“socket。。。。”) a3 \4 p: E* u: o/ @3 R
  20.          
    & A% Q/ Q4 L. Y6 q, D5 s
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体7 S: v. B5 W; p, }
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见); `& Z# X+ e6 V9 F: e
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型( W% n$ g; {( b& o# ^9 g  A  Y/ c; z
  24.          myaddr.sin_port                 = htons(8888); //选择端口号) I0 o& F  r+ }4 a# ^
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    5 r9 I9 a9 v' U* u0 ~' _

  26. % U* K1 p, Z: D) |( g! @8 S
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    - F5 L" P" Z2 l$ I5 ?
  28.          {
    / [/ P4 u0 A+ s  f! h
  29.                  perror("bind");
    7 N4 [* O) G, ?- A/ q. T
  30.                  return -1;9 V/ [0 B2 l) M
  31.          }$ V6 k3 Y6 k4 k: F. |+ O0 b
  32.          printf("bind..........
    + [$ {) r4 v; U6 S# F  u
  33. ");
    6 f0 |9 j0 h& K5 }" c/ j3 \6 i

  34. 6 B$ H, G8 y, I0 L
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    : `' T: d' `* F
  36.          {
    - \# z2 N( @; ^: j2 h
  37.                  perror("listen");
    # @# R+ }, G* c& @, K
  38.                  return -1;4 |& `# @5 d( q2 g
  39.          }
    ! I* ~! P" m1 q# ?9 J8 l% l) c6 f
  40.          printf("listen............
    $ D2 h! l' h/ \8 i8 O. u  {$ U
  41. ");
    3 R" O1 @; n3 M  p: X
  42.          
    6 @/ y4 v/ s( I' Q, G4 k' w
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求3 x$ Y( `/ Z% z' r7 c0 B, y
  44.         if (connfd < 0)
    ' ~9 D, \' v  f- ]  d# c
  45.         {
    * Y: Q! Y! k( f* ^
  46.                 perror("accept");2 y2 X: _& w# u, I0 V; `- @
  47.                 return -1;
    4 Z: j2 F4 o. Y
  48.         }  u8 C" r4 @% D! u; t
  49.         printf("accept..............% k) N/ [8 c1 d: M0 }
  50. ");
    3 c& X5 B& F! k! v# e; i
  51.         char buf[100];//定义一个数组用来存储接收到的数据9 H1 {1 X7 D9 Y
  52.         int ret;" _' K7 e/ y4 p. U; W
  53.         while (1)& Y; E+ l- K; A2 d  U6 ]- M, V' l
  54.         {( p' o+ j. M! T0 v2 V# v% l5 ]
  55.                 memset(buf, 0, sizeof(buf));
    , Z+ s3 _/ i7 h
  56.                 ret = read(connfd, buf, sizeof(buf));5 z2 L! H) m9 d4 q/ R
  57.                 if (0 > ret)9 r! {, s4 ?8 i" c1 W- f
  58.                 {
    . n# N; x3 l, v. Y% W4 Q
  59.                         perror("read");1 k7 B' w( k) y2 K
  60.                         break;
    # W; a1 [8 n& X2 K7 a
  61.                 }//执行while循环读取数据,当% G0 [% G: C. p7 |. D  N* s
  62.                 else if (0 == ret); }3 Z" M6 {0 T& ?) F$ f
  63.                 {
    8 l) ]/ G! R, s% ?" P7 c" P; K
  64.                         printf("write close!
    ! r, f0 ^! d" H' q$ @
  65. ");) i& C$ ]: k' h+ B
  66.                         break;0 D# w: S) v1 z8 L% @5 C
  67.                 }# B  T9 J; k3 y* B# R% ~
  68.                 printf("recv: ");! Q0 C& i! w, G# Z7 A% c' o
  69.                 fputs(buf, stdout);//打印接收到的数据! l- ]) ^3 h/ f
  70.         }
    4 N; ]% I. f; o
  71.         close(sockfd);//关闭套接字
    7 {8 N+ Y5 Z. J4 ^0 A! D) L" A1 D
  72.         close(connfd);//断开连接2 K8 r3 q$ r) ^
  73.         return 0;
    / H2 i9 B( `: R2 S3 m1 _
  74. }
复制代码

6 [2 r, B, ?' V* `+ Q) k" j
- P" e+ U3 ~* c  i3 X
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    3 J" N6 z1 B8 p) y9 V' e. A+ Y
  2. #include <stdio.h>
    / N8 X* c, V! D  |/ q
  3. #include <string.h>
    0 l. z+ q" v( e' D) g
  4. #include <stdlib.h>
    ( R: O/ N5 s7 ^& l- q1 @
  5. #include <strings.h>0 ]+ Z' j, e: ^- X
  6. #include <sys/types.h>
    2 l5 \0 B$ g/ j; Y5 i! w
  7. #include <sys/socket.h>
    2 {; d4 r8 X- y. b5 w& ^
  8. #include <netinet/in.h>
    . m/ S6 S1 |5 T5 e) \- q: j! U
  9. #include <arpa/inet.h>
      |- O8 g3 ^5 a  N) ^
  10. int main()) V& o; f6 l. D/ d  v, M) v
  11. {
    - s: `1 T5 C9 E4 N! A, B, k
  12. int sockfd;
    $ {8 p1 D& q- V" o$ e; I
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ; A: W0 k2 e( M! G+ ?4 L3 s
  14.         {
    ( @2 [* }/ X/ X) C; b0 w& C6 H: j
  15.                 perror("socket");
    & b" C  Y/ L2 ~
  16.                 return -1;
    ! r/ I% ?2 Y; g( O' m% I
  17.         }0 _( g8 X! F, e' l' y9 W
  18.         printf("socket...........
    ' O+ l8 U; n% R- r3 x' y* {
  19. ");0 O! X( s/ U+ g; W/ N" p
  20.         
    7 o9 u( d9 c$ Q6 U' S
  21.         struct sockaddr_in srv_addr;8 F: m$ Q3 u6 ?* u
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    % o& v/ T3 q* n( U
  23.         srv_addr.sin_family                 = AF_INET;
      N9 ?! E2 f; V. u
  24.         srv_addr.sin_port                         = htons(8888);
    0 j! B& `) N7 Y3 f& I5 [8 s( m" Q% R
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");2 g7 f- N+ T' {7 Z  i1 V
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    1 w" {2 t8 \+ }, K% `" H
  27.         {
    " Z. n& [+ r6 l( c1 L4 t! p% i) Y; V
  28.                 perror("connect");6 b5 O: W! j0 H/ X4 {% l
  29.                 return -1; //exit //pthread_exit
    & {: Q! `6 z7 b5 d+ z8 F
  30.         }. N2 `" u2 d# A
  31.         printf("connect..............
    ! q+ N3 J0 i& Z) q! K% j
  32. ");
    % S. L' F* n# j0 L# ~
  33.         char buf[100];
    , ^6 ~' p% ]) d/ ]' a
  34.         int ret;
    : D1 _8 |0 L7 `
  35.         while (1)& H/ l+ q0 i1 v
  36.         {7 ^2 [' u; O+ n6 O  N/ `
  37.                 printf("send: ");
    4 h7 `1 y6 w& Y) ]+ Q  J5 K' ]& V- N( i
  38.                 fgets(buf, sizeof(buf), stdin);4 Z, W( R8 C( w) G9 ^- f5 f- V8 E
  39.                 ret = write(sockfd, buf, sizeof(buf));
    # [) ]) {5 _+ p1 A3 n  |
  40.                 if (ret < 0); T7 \  D  }$ I, D
  41.                 {
    $ d, r9 {1 T0 a2 F) [, F
  42.                         perror("write");+ c; J, ~8 F5 X! n2 o
  43.                         break;% D* Y8 [; ^! S' A
  44.                 }- c% @5 N0 m# K$ i: C( `1 O) s& D
  45.                 if (strncmp(buf, "quit", 4) == 0)
    - A5 \! ?2 [( N  v9 c
  46.                         break;# h% q( E6 f3 B% @% \* ], d
  47.         }
    7 Y5 C/ a! B* w8 a9 ^6 B% S; ?$ R
  48.         close(sockfd);, V/ r5 E+ w2 O) K/ _
  49.         return 0;& U8 d! g+ J# m* E/ V! S/ L, B, Q
  50. }
复制代码

0 k- j6 @  d7 l1 V  y  z! x; m( r2 J9 _
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 02:39 , Processed in 0.135197 second(s), 22 queries .

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