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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
4 H7 N7 N8 F9 _$ Q0 R1 R$ s2 H6 V( y3 O( J
1 C) O+ |" Q+ G: R& T' O
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。5 x& i) |. N* ]8 o' H" ]
2 L: X' y0 H0 v& r, \* H

/ C  b/ V7 ~; E1 TTCP协议
9 |6 i: H" t; F2 l& rTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。) |+ c" c! W9 W
! d& L+ H' e% \$ s+ O6 C7 o/ `
% s+ {4 S" Q# r; t
关键词:三次握手,可靠,基于字节流。6 z) x" c& e) p9 i# D' b; h) z

5 N: i: K' G' F- C. M

- E+ K6 y) j( A& ^. ]可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
+ q4 A/ l9 F/ I/ n2 D% y3 D
4 Y: N0 d" K1 F% \3 zTCP服务器端和客户端的运行流程" h! I0 F. D1 I2 p; t
8 t( x4 X" d" B# d  `) P

- B& x' r% X; `" ~1 [6 y+ B' A如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?  W3 S  z& N8 M% [2 H4 }4 B1 M

+ T/ g# U; t7 ^$ C8 u! i# A

0 Q. N& P% k" @8 v1.创建socket. D9 m" Z- }" `# A" |5 S( E
socket是一个结构体,被创建在内核中
1 e: }# s: e. v3 t. x sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议9 V5 E( I+ u( i

: D& [1 g) F1 l# A9 q$ I  f  M' Q& {
$ X  M3 {1 t6 A& M4 b) ^
2.调用bind函数
3 K' S- e  ~0 s 将socket和地址(包括ip、port)绑定。
& D0 A9 v" N6 {, l' L, k; Z0 I2 j 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序% |+ e; n* m6 k
struct sockaddr_in myaddr; //地址结构体2 n4 `* N, k; b& U% T
bind函数% X6 Y" F3 k7 b6 j: I! s
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
: j/ L( \! \7 a7 l+ `2 q# S& a  H' L" ~" a( e6 h

2 h: k; p8 K0 b. x( ]9 Q# E9 V; ~3.listen监听,将接收到的客户端连接放入队列
7 N+ V0 _" R( i$ d& O listen(sockfd,8) //第二个参数是队列长度- l3 @3 f7 Y8 P2 X. j9 P/ c: B  p; t% i

7 \" _- }8 w4 T& Y8 T
# u2 k1 U# v7 U
4.调用accept函数,从队列获取请求,返回socket描 述符! @; i4 i( K; l# @$ z
  如果无请求,将会阻塞,直到获得连接- T1 s. V: W( ^! k2 f  |- K, F1 m4 `
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数: [6 N# y( Y! ^

. u4 W5 h- L4 ~

, d" m( Q# q+ N  h: ?5.调用read/write进行双向通信& I. C5 k: l$ P) X/ N( f

) M2 n, w  L$ f3 T% g7 C# H

! P0 ]. H  K4 x2 @8 P6.关闭accept返回的socket2 ]5 c- }, j( Q" I- N- s/ [2 O# o
  close(scokfd);$ z1 G3 H% T7 l: L" Y3 [

7 d' @9 u; ^, C( ^2 f' [. Z

. e. g( x. M1 F; t
# o" B. z0 Q/ b8 C% @8 ~$ D' e. `
" F2 v- b  _% x6 w% K& q' p
下面放出完整代码
9 I9 {8 ?  x. T4 v5 h2 ~7 p" Y. z: C; w
0 x4 ]4 o# [! ?9 j& ~5 @# U, D
  1. /*服务器*/: s+ b( Z7 p7 z* y& N4 o! _
  2. #include <stdio.h>, x# d! f7 q  l
  3. #include <string.h>
    4 @2 X/ L% w9 z2 ?$ `, i; @
  4. #include <stdlib.h>* R0 ?' W$ M$ x' B* w0 |
  5. #include <strings.h>
    9 q- P; v& _! P4 l
  6. #include <sys/types.h>9 x: X' s8 P( B# B% r" _% \6 ?
  7. #include <sys/socket.h>
    9 @' J8 }6 J( \7 F% }2 h
  8. #include <arpa/inet.h>* n" m* t% R2 x; o' }
  9. #include <netinet/in.h>& ]9 U% \! R& p( z/ \; O! n8 }
  10. int main()& @" I1 [. Z; v( |0 o
  11. {
    ) _5 [4 U( A+ g, \$ Y) @
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
      [8 x: P2 e6 {/ \; M) q
  13.         if (sockfd < 0)
    0 _) R+ n/ ~5 r; h' G, n2 k
  14.         {9 q4 A/ m+ w+ G3 @& E* D
  15.                 perror("socket");
    1 o3 A0 C4 e  M0 F# D7 K
  16.                 return -1;! I3 v) @, }9 ^
  17.         } //创建失败的错误处理
    2 E. H5 e! o* ?# v6 g( D
  18.          printf("socket..............& A! s) s* i: E
  19. "); //成功则打印“socket。。。。”% J  Y2 k1 L; T6 L( s- w) ?( g- X
  20.          $ r0 ~; v; ^, W* B! U
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    2 z/ a/ w3 \; R8 \$ U9 l* S
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)2 |3 X! o+ V" B/ G0 S, v
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    0 P" Z# p( R/ S' V0 Q+ x! ~9 N
  24.          myaddr.sin_port                 = htons(8888); //选择端口号9 [  _2 P' m$ ]/ d
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址% V; Q" Y% B0 _7 X9 J
  26. / I- n2 t2 L6 D( {3 r7 S! c6 F* R
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    * }; p' j& M. d
  28.          {8 C2 F4 q/ H4 T) g- M# t
  29.                  perror("bind");8 t% P5 E" ^: h$ _
  30.                  return -1;, x! ~8 k+ h+ O# c& |% t% I
  31.          }0 A8 M5 _! b- h$ J$ a
  32.          printf("bind..........# p: P3 T! z0 A( k; C
  33. ");! H+ `! W8 S4 J' M; }6 v1 p
  34. 5 E7 \! c# E1 r! V* m$ M0 V
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    : m, t6 A! B1 l8 U
  36.          {
    1 E  y! z  _9 U! q
  37.                  perror("listen");/ ?( a9 w. x" H( N. C& s
  38.                  return -1;
    / g- D/ ]/ C* I8 V% x3 g/ w
  39.          }
    9 c# r8 w; o: q: A
  40.          printf("listen............
    1 h2 r2 c+ u6 T5 o: x% U% q( `+ y
  41. ");% c! ]4 U& q! G; Q" _
  42.          
    5 F0 {- x0 Y/ }
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求( _) {9 z! L. Q9 s" T
  44.         if (connfd < 0)
    $ V/ E; H# }  g
  45.         {# X0 ]4 p0 K" K# V$ H' |1 `0 ~
  46.                 perror("accept");3 \' F3 g: b0 y) \# r- }4 X, @
  47.                 return -1;
    # x" N5 `5 l6 M
  48.         }
    ( O. B+ j" a7 r0 M7 v9 V
  49.         printf("accept..............0 e5 k0 @3 X* S3 x0 M
  50. ");% Q- A! i$ |8 X( H( ^! P7 j1 A8 X
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    " b/ O9 @+ T, h
  52.         int ret;
    8 t8 }1 D- W* @
  53.         while (1)2 O" L6 G8 M- M5 r' l# o
  54.         {
    3 G3 D9 u6 F1 p
  55.                 memset(buf, 0, sizeof(buf));1 }: M2 q" ]% p, K4 h$ N! X
  56.                 ret = read(connfd, buf, sizeof(buf));- s. ~9 W( E* @
  57.                 if (0 > ret)+ c. H# d- f( ]# v$ F$ D
  58.                 {
    & @0 t+ C* w5 O2 @6 Q9 I% T
  59.                         perror("read");& Z; x8 r( n) a2 \$ c
  60.                         break;
    / M% j6 `9 C/ V
  61.                 }//执行while循环读取数据,当. S1 y) [3 ^- c
  62.                 else if (0 == ret)
    # }; I" m7 D7 ~
  63.                 {. U# c; W- `: u
  64.                         printf("write close!; u9 j, I, h& M% s9 x
  65. ");
    9 t  F$ Y0 `" G8 X' c$ S
  66.                         break;
    & y! U: I: j, G# X3 g& U: ^. _
  67.                 }
    6 a! r3 l5 l' g! f4 n# m4 t  U
  68.                 printf("recv: ");5 L9 F& ?7 X' Z# }; S3 P
  69.                 fputs(buf, stdout);//打印接收到的数据
    : F+ Q, E( ?9 q2 Q. U# M
  70.         }
    ( N& @) d( }9 p# ~1 p
  71.         close(sockfd);//关闭套接字7 F2 @  n2 M8 J
  72.         close(connfd);//断开连接( O9 V6 m! e/ l! L
  73.         return 0;% {1 ]! N2 S) g6 F/ ~
  74. }
复制代码
2 |- Z% O4 `- G7 D2 a( l

$ v8 J& r, J: K' [
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释); \2 u# r4 q6 d, P* g
  2. #include <stdio.h>
    3 b" G" l2 ~! T5 `6 L3 Y
  3. #include <string.h>  g/ n6 W4 o' T, e* a
  4. #include <stdlib.h>, x0 R2 k* }2 S8 ^
  5. #include <strings.h>
    4 b3 v9 S8 F- r  \0 D: U" b9 I4 i
  6. #include <sys/types.h>, v- M3 B6 z) [! d0 \
  7. #include <sys/socket.h>! T$ x" x* |0 w4 [; x( ~( M
  8. #include <netinet/in.h>5 w+ ~& o) G/ {5 t5 u  f" |
  9. #include <arpa/inet.h>2 o# Z& |% [5 H! z: a
  10. int main()6 t: r5 g: I( w  W
  11. {
    : ]0 x( }% `3 ]
  12. int sockfd;
    + o$ w$ ~: X4 Y/ ]* x! w: S+ r6 m
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))( f* Q( R$ V/ U, m1 U- P
  14.         {2 Y# i) a3 t. |4 p" D4 a+ o
  15.                 perror("socket");
    ' p8 w" D1 {8 w( w
  16.                 return -1;. _% ?; ?) F" O. e+ _. l# z% F
  17.         }
    9 [, M- m( F8 Y# X$ K" f
  18.         printf("socket...........
    % D4 U7 I4 d: r/ ~
  19. ");; L8 m' Q4 w, Z
  20.         ( _( }$ p4 T( Z8 x  {- R
  21.         struct sockaddr_in srv_addr;* e4 T) m2 a5 b% C
  22.         memset(&srv_addr, 0, sizeof(srv_addr));$ \) h9 t5 K3 W2 z$ S
  23.         srv_addr.sin_family                 = AF_INET;
    % }2 f5 S- K9 J0 ~( O0 @
  24.         srv_addr.sin_port                         = htons(8888);5 l! D; m6 G3 A0 b; \1 F# U
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");) P* v( W) {: [
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))% M0 `: G5 s4 f$ Q, l7 Y/ M# e4 b
  27.         {
    " e! j, M  P" n7 V6 a  l& C0 v9 l- Z- x
  28.                 perror("connect");
    . C8 W' a+ ]; \
  29.                 return -1; //exit //pthread_exit: y4 l7 y7 N$ b+ m) ?0 j+ U! l' |# V
  30.         }9 Z' p) ]7 J! t: R9 c  S
  31.         printf("connect..............
    5 w, t. B9 R5 ~) [+ m
  32. ");, a, B; V- q; [
  33.         char buf[100];2 F  u$ C" s7 c$ P" E0 f( \8 ~
  34.         int ret;
    ; V( D& ~4 ]# K$ `. K
  35.         while (1). @) j3 Q7 {7 r
  36.         {
    * ?3 U0 ]/ E4 G2 R) n9 N+ I1 A3 B; Q4 x
  37.                 printf("send: ");
    - {6 ?" Z  p+ z6 X. F+ m6 V+ b
  38.                 fgets(buf, sizeof(buf), stdin);
    5 L# ]9 N9 R* f- t
  39.                 ret = write(sockfd, buf, sizeof(buf));
    3 v) f( K8 Z% C- ~6 S, p
  40.                 if (ret < 0)
    / ?- S1 @9 W* X" B3 k
  41.                 {
    , w/ Y, H3 y; U5 i7 l+ A) J; d
  42.                         perror("write");* Z  Y, e! P5 @. d6 Y
  43.                         break;% f" F+ i7 ~6 \3 ^: `% G" l/ v
  44.                 }% Y  c( A! T& _, F. h3 ^
  45.                 if (strncmp(buf, "quit", 4) == 0)
    1 |+ ?# l* y& u# H
  46.                         break;
    + \( b9 a5 @2 V3 l% X9 U
  47.         }# U& p  M0 i" u# K
  48.         close(sockfd);: q1 W6 R" V5 L9 B' h
  49.         return 0;/ f* k1 a9 T! f
  50. }
复制代码

3 d2 t9 b( w& a! n. |" J; {
* |! G- n: {& q+ }% A8 F
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-4-30 17:11 , Processed in 0.062746 second(s), 22 queries .

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