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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。  k5 R( J% S0 u; h
5 T' U# _4 M, U
, u) u+ h  V5 D8 D, M
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。. L' d2 q  M4 d5 e! R# g; w7 U6 ~  \
4 y% e# ^  M) S/ h: l7 ~9 Y
- N) d" y! P$ W5 Q! x) H2 C; x
TCP协议
( s9 M4 u' d- Q# OTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。  Z6 Q4 q+ s7 Q# j; g: ]; {/ [
% C9 A! @/ W$ S+ x7 `9 q4 s( g% k1 r* {

( Y2 o8 U* W) p1 w0 G0 l" e, I; j9 S关键词:三次握手,可靠,基于字节流。, f1 K# Q$ N) s: ~) v
4 `- ~5 U  S, c8 ]( ~

$ W8 l7 e7 h5 k. A0 w可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。* Q' @, ^- C' v0 a

! x; _: g* ?+ |& rTCP服务器端和客户端的运行流程
$ }6 A, x+ m  J; J+ Z5 q6 Z9 h  Z. o& L' w/ v' P6 h

$ J* E: i) p0 M5 j, F: Z: x0 y. U如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?( ?! j: Q. }) Y$ q
: R9 U6 k& h) h; G; c, O

* G& q* c+ P7 A3 d( `% O1.创建socket
, ^& N2 k  k( [7 t4 Y+ w/ g$ ~ socket是一个结构体,被创建在内核中
6 o- \9 y: W) W' P: F! o8 Y* I sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议! m, s. h0 i! P0 s6 m: B

8 v& u" _& J/ M% B3 r2 F! }
/ Q' w  f' U4 d) Y
2.调用bind函数
7 l, d+ r) [( y+ n 将socket和地址(包括ip、port)绑定。
* \$ `: D4 k7 O! A9 G! p1 K 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
- F. `; N5 d6 D, Y8 V struct sockaddr_in myaddr; //地址结构体' E6 `& Y+ T3 H9 ~
bind函数0 z+ j0 _  Y" Z* q
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))! V: @* S4 @4 v8 L# `

. n+ d1 b" l' X6 I! F3 v
: r# w( t7 O$ O  E+ f4 G
3.listen监听,将接收到的客户端连接放入队列( z1 i' D- l3 m, f, h5 _; L
listen(sockfd,8) //第二个参数是队列长度4 m, G$ P* z4 ?
+ O" h# @9 P$ x- M
8 s) H6 h6 o5 n( M7 g
4.调用accept函数,从队列获取请求,返回socket描 述符
2 K) c) R" K0 M9 Y: _# ^  如果无请求,将会阻塞,直到获得连接3 |' T& o0 J; w) N" T" ^
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数; S1 w1 {! F. ]8 t' d

: z5 O  Z' f2 Z5 z  V% E

) i1 c! a( Y, z$ j5 j5.调用read/write进行双向通信
6 U& u# |) f: c- @2 x8 l: a, u) b  ^+ x4 P5 o# T2 ^
0 }# v. M! `0 e! K0 i
6.关闭accept返回的socket
9 a0 k5 @; v" |* P/ ]" q  J% q  close(scokfd);
9 x' C7 u0 ]. K0 x2 S9 s8 Z: }3 ]9 e: E

" |4 X5 {) I: \7 i" P% h) D  o5 z' V9 b8 Y

9 [. q- C* Q$ ~下面放出完整代码- A4 x5 |- R9 V3 M0 G7 J
* i: p! o. h- x8 ?6 m& f
  1. /*服务器*// h' _6 d) b) M- F
  2. #include <stdio.h>
    ) t6 u9 ^# U6 D5 j% }) o: B, }3 S% }
  3. #include <string.h>
    ) Z8 _1 x5 [" ~) q6 ~4 r
  4. #include <stdlib.h>: \/ e  X% \3 u1 I) t0 z7 V
  5. #include <strings.h>
    7 D# z# l! \: Y" h
  6. #include <sys/types.h>) Z& y2 f: F: \: h/ I- {
  7. #include <sys/socket.h>( @* ~& v0 [7 h. a9 `# q: p
  8. #include <arpa/inet.h>8 j; K) v: z% ^; e+ |: l
  9. #include <netinet/in.h>
    % m* }9 r1 J7 S( C8 K
  10. int main()
    9 [0 b% w0 j5 H: A  X% U
  11. {# e6 ?6 t  e! @' I( u
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字( d0 A# Z' k3 Z0 S7 i8 t" O
  13.         if (sockfd < 0)( ~6 `8 j( i8 w' h6 v7 [
  14.         {
    5 `  W8 y; ^& k. ^; ^
  15.                 perror("socket");9 F! D  ^7 Y2 I, T: m
  16.                 return -1;
    % j- C3 \) U7 n7 ]
  17.         } //创建失败的错误处理2 ?! A3 Z+ u. X  S4 B, r
  18.          printf("socket..............
    ; m. W$ k( H+ k3 H, i. k
  19. "); //成功则打印“socket。。。。”) Q) ^& v& l( G; F
  20.          * m9 f* i- @& r/ p8 q% f  ^4 z
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体9 |, T5 [9 S; K) B1 u
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    5 l" ?) U. g& g# o- c8 K
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型" q5 }' o) G4 A+ r% ~/ a5 U( X  s2 V
  24.          myaddr.sin_port                 = htons(8888); //选择端口号- e$ k. T8 C1 j% K" ^) A0 G5 t6 V* K
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址4 @3 E+ d% O6 ~" F* Z# Y

  26. & q7 ]% f) }, z5 e1 r
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    6 f8 L: Z. q9 M; V4 b$ W/ }
  28.          {
    1 {/ _. D. N6 \9 ^! _0 Y
  29.                  perror("bind");
    2 h% J  p+ d" f. i) N+ y! n4 M# K
  30.                  return -1;: i0 t& n8 N6 S6 k
  31.          }
      |+ o9 y5 \* c" X# h1 ]( ?
  32.          printf("bind..........
    1 c9 g  M* H! ~/ [/ G
  33. ");
    ; X9 h. {& I0 j! q$ p3 p0 M
  34. 9 }' N/ O; Z9 w7 s: `
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听' }( T8 M& i- v
  36.          {
    8 p) a& T( m: |* _" R$ s+ X/ ~
  37.                  perror("listen");: K. z" A# {: z
  38.                  return -1;
    * U/ ?) a- w4 W6 Q: v* Z0 v3 I
  39.          }
    * e: |0 g& H6 l% b* h+ r
  40.          printf("listen............5 a: N( U* ]! M1 Y4 `  M% f
  41. ");( l# R! y, a% T* a0 E3 q
  42.          
    " G- o# b* r" G* W( D
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    0 i/ ^! V, F: g) R. a
  44.         if (connfd < 0), f, _0 h- j. @* }4 f9 L+ j/ t4 Y
  45.         {
    7 B" S8 J8 a/ W" r% f+ _3 n2 _
  46.                 perror("accept");
    / `& U$ E6 F2 m% W0 x' B9 G# r
  47.                 return -1;
    7 q( [4 q. w: Q3 u, k  U
  48.         }
    3 Q6 q; K* Y+ \0 y% g8 T
  49.         printf("accept..............& \. |& S2 t) a0 c$ t! H
  50. ");
      X! [* _- Z  {2 H9 E
  51.         char buf[100];//定义一个数组用来存储接收到的数据, ~- w* U% ?/ E* ^: E, x
  52.         int ret;
    6 W  Z7 k/ n  h" w. ^! u5 t+ p% z4 y
  53.         while (1)$ `& ]' e( k; i3 D0 F
  54.         {
    ) O0 t1 o4 t& H' u  M4 ~! M
  55.                 memset(buf, 0, sizeof(buf));! @5 E2 h6 P# u4 r3 s
  56.                 ret = read(connfd, buf, sizeof(buf));  n/ o  K. @2 |4 O2 _
  57.                 if (0 > ret)
    5 U4 B; i. H4 K# S- ~1 x
  58.                 {
    7 [) o" I' k# T' [7 X
  59.                         perror("read");! Z/ U0 K1 _" w
  60.                         break;% o+ `$ K$ i7 B+ i/ R
  61.                 }//执行while循环读取数据,当) d0 B+ C/ v0 x. K* O+ s
  62.                 else if (0 == ret)& v6 }( m% v3 m, ?" z
  63.                 {+ [. x" w) Z+ k
  64.                         printf("write close!: s" k: E4 a" C
  65. ");  n, k  N; a7 q8 J. u; A5 \7 L
  66.                         break;
    " U' i* a; d2 f% t; {) f" u
  67.                 }
    4 c( ?$ N. G2 H$ m) T% G
  68.                 printf("recv: ");
    4 ^8 r: T& j& P8 m: I- a
  69.                 fputs(buf, stdout);//打印接收到的数据
    2 G% J2 Z( I$ |1 |( K
  70.         }
    & Y% S& y4 x) W* J" k- }
  71.         close(sockfd);//关闭套接字
    # B# h  u1 R4 h" {  V
  72.         close(connfd);//断开连接; T8 x3 n4 t& _$ V% l5 w: o
  73.         return 0;
    0 I/ c  S1 _, ?0 J
  74. }
复制代码
( R+ Q+ F6 k* u1 L9 n3 j
- n; z' g& c) v7 ]
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)! K8 T% N2 M- X5 }& M- `
  2. #include <stdio.h>
    . E3 r4 V& T: F6 y. Y& H) {
  3. #include <string.h>- x- T5 j6 d- l
  4. #include <stdlib.h>
      `4 H2 E# T5 ?/ r, x
  5. #include <strings.h>
    . b  ]* v! [/ L) q
  6. #include <sys/types.h>8 i# W. b# \8 [3 O, h! j" P6 l
  7. #include <sys/socket.h>
    ; N( l$ H' O. n3 z4 V
  8. #include <netinet/in.h>8 c4 ~# C0 a2 h' E5 J, r) |0 B
  9. #include <arpa/inet.h>
    + v8 J; o' G+ K6 U* @) b& ?4 b
  10. int main()# ^" o8 Y) H" w3 Y: n
  11. {& x6 E4 j. e& C7 ~# |3 P1 |& q
  12. int sockfd;# s! O; N( e9 {6 j$ M5 p3 R: E
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))& v1 U+ F) @* Q6 [7 W8 [( g3 H
  14.         {
    : B, l/ M7 T! L. }! \7 Y  T
  15.                 perror("socket");
    . n9 f. E8 ~' b$ c6 x
  16.                 return -1;
    9 x+ c* @' y3 ~
  17.         }3 S( V4 I8 I9 S- E5 N5 N1 l
  18.         printf("socket...........
    & n  |% s1 @" [: \0 G( d' e
  19. ");  U  l* [* [1 w0 m( E* L
  20.         $ }  c+ N$ S# F& d
  21.         struct sockaddr_in srv_addr;
    . l2 d4 E3 D. ~
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    + W$ N+ g  R& P
  23.         srv_addr.sin_family                 = AF_INET;
    8 M) z& \8 X& k" n
  24.         srv_addr.sin_port                         = htons(8888);- Z" Z' E0 I1 [2 M; B$ H
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");4 ^4 F1 w: e+ b" ?* l, S- t
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))# ^9 u) G# I0 g  R. h
  27.         {3 Z+ _' H* J- x
  28.                 perror("connect");
    7 V, u+ @5 O. D. i0 ]
  29.                 return -1; //exit //pthread_exit8 |! R$ A( X$ [0 b+ @1 `# g5 B
  30.         }
    / B5 s5 T5 @  ]( `8 v* T
  31.         printf("connect..............
    ; }/ Z& t# L0 f: Q- \
  32. ");3 B3 y1 {" Q$ P9 @; t; R" p1 l
  33.         char buf[100];9 O& d* L* z1 Y+ Y' f
  34.         int ret;
    9 w$ f# o7 f3 F; s  Y8 U* @+ F
  35.         while (1)& l7 T! j$ Z5 R1 l
  36.         {; L& a: G! D, d+ r) I/ v3 t
  37.                 printf("send: ");, s6 X1 k5 B8 l! b- J: x/ W
  38.                 fgets(buf, sizeof(buf), stdin);* v, Z! |% A- {9 ~7 `. c8 y
  39.                 ret = write(sockfd, buf, sizeof(buf));
    0 X4 i$ W# @  a5 u# A/ }6 e4 @
  40.                 if (ret < 0)4 T/ V$ S# _4 u$ K' x3 ?
  41.                 {
    & k. t# m% [3 G3 A: p
  42.                         perror("write");) ~% d1 g3 ~+ a% K+ k  m
  43.                         break;# c4 ?4 [2 b- T# ]
  44.                 }
      @7 Z+ O7 }! `
  45.                 if (strncmp(buf, "quit", 4) == 0): d2 J0 `5 z+ E7 Z
  46.                         break;& M$ X0 L. T6 S7 l( i
  47.         }
    # S5 }! R$ w1 q
  48.         close(sockfd);
    ; v6 ], \  q$ t% ~; d% P
  49.         return 0;& M* m  s/ w' G
  50. }
复制代码
8 ~5 j) |- Z* j0 f" k5 _
/ g0 W, J8 E$ Q2 B( w1 @* t
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-16 16:51 , Processed in 0.067310 second(s), 23 queries .

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