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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。* O$ K2 U1 Z+ O. H5 z! M2 ~  o

* m. J: s  f% x1 L8 j
* e, i$ r; y! e9 A& `: W9 I" a
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。. b# j9 H+ p- Z" m! F
' N1 l5 |) g6 N8 h4 V; \

. Q3 J1 ^- ]& U, h# q% i3 iTCP协议
4 E/ J7 ?9 {6 H  W0 kTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
6 ]" U. R6 e3 `* `" v
/ q2 \) s6 ?. `/ z

6 o3 j& P3 S5 `) a- p% K关键词:三次握手,可靠,基于字节流。6 X4 P' O/ O1 {0 W

, |! J7 ?9 i! K: [7 M

  l. z% u8 E$ _, G# Y6 A( c可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
5 I0 I& d8 Y/ a0 w" W 8 q. l7 q( [0 \
TCP服务器端和客户端的运行流程4 U) {, }! {0 f0 b, g' d
8 a1 @9 ~3 I. y) k& K+ ^% B
8 W  G9 M" s: _+ V9 Z
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 d7 u4 c: v* F0 W4 h2 ^/ k9 r

/ T' K5 P: C* v) D2 g
% Z+ ?0 M: K0 t1 c5 T
1.创建socket
0 q3 c9 l8 `  w: y socket是一个结构体,被创建在内核中; p0 s' o( f" H. n7 j8 O* U# k6 Z
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议; n! X$ ?/ f% Y8 V; m( _
. v+ ?) ?% f! L2 e. u& b. l3 M

$ g8 X' Y7 n& v) X  ]* G" g2.调用bind函数
8 @7 ^6 a/ Z+ j$ W 将socket和地址(包括ip、port)绑定。: o3 H$ `# g) m6 i) F* X
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序  B# O, ^7 ~& e  x) z' t1 a
struct sockaddr_in myaddr; //地址结构体
; H5 ~3 S! {# z. l bind函数- B4 U% X; ?4 K8 N7 f8 |# Z
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))+ V$ L+ R1 Z* U2 C; S: j6 Y1 A+ ~$ M
* b$ F4 d! w: ~, O- I2 z  B) Q& V

% ]4 _9 R  e9 j% q3.listen监听,将接收到的客户端连接放入队列) [9 |. h+ F1 r& P7 ]
listen(sockfd,8) //第二个参数是队列长度5 L8 s8 W' H  M: S9 z% x8 M" @

) ^1 F, P: J7 M; j  F& g* [

/ h1 O% I+ ?7 ^. M4 O4.调用accept函数,从队列获取请求,返回socket描 述符
  ]0 i# s) }! v! Q$ ~) f! J  如果无请求,将会阻塞,直到获得连接
4 }5 w8 O' S& C5 n7 i- I. k  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数# k4 D3 F, u$ j9 t, U, X
, ]1 d* m' S$ T
- Y, Y* |5 n& |' d, u9 Y2 A
5.调用read/write进行双向通信( v* X  E# A. _0 Y( |

  |; a' |$ }0 |8 @

# N  t+ Y- u% \6.关闭accept返回的socket- P! Y1 C& N" Q! A
  close(scokfd);
) k: W* p; \# p4 h9 o9 Q) x; l; a( q; h, y" b' N' @
1 f! i" G) x  e6 f7 p# V
1 N5 w& I7 H: r3 w. P+ M- a
/ e! J# o- r7 o1 D1 D2 C3 K. D
下面放出完整代码2 }( e5 H+ g7 `  O
% i+ W" j8 |* D0 F
  1. /*服务器*/
    3 @, b8 ?( Y! Y" x! G+ e
  2. #include <stdio.h>. m" N# M9 Y( d; E" L* U
  3. #include <string.h>- }4 U3 n# U. X1 _" p  F; T
  4. #include <stdlib.h>
    + k6 w% n/ g: i
  5. #include <strings.h>' u+ N4 s; j0 O6 p/ `! P
  6. #include <sys/types.h>' K& D% f/ J3 d+ R
  7. #include <sys/socket.h>+ M7 K* J! z, X
  8. #include <arpa/inet.h>( H, _7 n1 m0 \1 P, ]
  9. #include <netinet/in.h>
    3 N5 B. N2 l6 Z/ g8 T
  10. int main()
    8 L% W" g9 Z1 |+ n% @' \/ F
  11. {
    : D" g6 T/ r' _/ `5 h
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字1 K# g* E8 b& _; ^: J2 O
  13.         if (sockfd < 0)
    3 Q" u, L) m* V0 L5 l1 b* u
  14.         {2 G: x' y9 f1 n" y
  15.                 perror("socket");
    * l" A+ {( H" u0 j. A% r% g' B
  16.                 return -1;1 ?% x7 s, H5 v" H" F; {$ d
  17.         } //创建失败的错误处理: I; ]# [% D0 Y4 h6 Q
  18.          printf("socket..............
    0 O; ^( U* K! y7 B! C- M* u' H
  19. "); //成功则打印“socket。。。。”, [* t/ [% j$ m1 Z1 R9 ^9 K
  20.          9 s7 u% S4 x, [4 Z$ k! |
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    3 [) T" Y4 K) j3 N3 G9 f* p# G$ d% y
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    * |3 B" A2 W. e* ^6 L
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    " v  i- ^% R1 N# M& ?0 `% d' p4 ~
  24.          myaddr.sin_port                 = htons(8888); //选择端口号8 O; r; C' Q+ N  e9 `& L
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    " j+ i; W* o& |' Z, t% K
  26. 7 O% O3 t) O4 s* b( r8 x) |; |
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    4 |% h- q% L. g! C. w5 B
  28.          {' Z9 ^( \1 p; G
  29.                  perror("bind");
    - P  {8 x& V, V8 n0 ~: {
  30.                  return -1;' W+ ?% U$ W6 S
  31.          }
    ; f! i' J/ W0 a/ [
  32.          printf("bind..........
    " b: P, o5 i1 k; y/ b8 Q
  33. ");$ j. N! }' ^! R6 l$ p
  34. ! Z8 ?& m) D, W' Y
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    , \/ a2 u* T! }- o9 m+ c0 ]
  36.          {
    7 P# C' ~. K. r; ^: c# \0 A
  37.                  perror("listen");
    9 a( e+ m9 d9 x+ y, z7 W
  38.                  return -1;, v' V. z/ c; f' S9 ~
  39.          }
    # Q- D$ W7 o9 U; N5 U+ P9 Y
  40.          printf("listen............) L# g( n0 J8 ?9 O& [# V
  41. ");/ }- Z1 r; Q+ V, U8 Q) c0 Z, Q% l$ q
  42.          , b  O4 e& d6 a( _, S" o0 O: b  Z
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求* w( a3 v. u% d) [. l
  44.         if (connfd < 0)$ |; q- w6 v1 }. K$ ]+ M
  45.         {  R; I) x/ a5 C$ v
  46.                 perror("accept");
    % S8 z9 O! a1 L5 l, ], w" O: f
  47.                 return -1;
    2 }' t* Y6 Q, I! M. q
  48.         }* L5 F, C5 N" F7 b; n! @, \. @
  49.         printf("accept..............) o& v$ c9 D& A) ?. h2 ?4 Y
  50. ");
    7 \9 j$ I7 I" B2 e
  51.         char buf[100];//定义一个数组用来存储接收到的数据9 A+ I% [* d" ], d* x$ J' g
  52.         int ret;
    9 v! ?4 B" O) r2 D
  53.         while (1)
    7 K) c/ }. v% U7 h; i* L
  54.         {
    ' c: c6 @' }# J" `! w; `' m
  55.                 memset(buf, 0, sizeof(buf));
    6 J* H: e2 N3 ?; _9 j' O- A
  56.                 ret = read(connfd, buf, sizeof(buf));
    4 n" K9 D! b. Y; o5 U" u7 |
  57.                 if (0 > ret)
    8 ~1 {% P( e, x" o  Z) N
  58.                 {; a- b+ f; T3 e" r$ P
  59.                         perror("read");% A0 k% O" W+ W' z1 J
  60.                         break;2 n1 f5 N* G( t/ q/ G
  61.                 }//执行while循环读取数据,当
    ( J: V/ {4 M# x0 N6 l" R
  62.                 else if (0 == ret)
    . X4 g4 I. U2 L' X
  63.                 {  Q- I) ^# @$ d7 R
  64.                         printf("write close!
    : ?8 C  c3 N/ h- ]* [! _1 v7 B2 e- ^
  65. ");3 o- Q4 r+ a  G3 h5 ?( |& X
  66.                         break;
    , M" j4 G1 l% p& C6 h% E
  67.                 }5 C7 h( m! _& B0 Z* j7 s1 W
  68.                 printf("recv: ");. U8 W! c. w, p- `0 X6 _  D
  69.                 fputs(buf, stdout);//打印接收到的数据
    7 s/ y; _: h1 o. p2 Z
  70.         }& R2 Q  g6 F$ a- O+ E8 q. n6 ^- ^
  71.         close(sockfd);//关闭套接字  X9 {! s4 y) n: P( b) k
  72.         close(connfd);//断开连接
    / ^6 Q6 Q9 g0 K7 E
  73.         return 0;
    3 C% F" e* {0 a
  74. }
复制代码

% ]. ~) c! F$ n9 R3 _
# x2 T2 M4 j$ ]% T  t' }7 u
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)" U2 L0 e% |+ F# k5 n3 r
  2. #include <stdio.h>
      S! r4 z  C- `, J, Y
  3. #include <string.h>
    / n- I/ Z- E2 N( R+ S- \' e1 t
  4. #include <stdlib.h>* F! S2 z8 g7 r% _/ [, k. ]- H/ Q
  5. #include <strings.h>
    $ `" f, w* }7 X& H. u" d) z
  6. #include <sys/types.h>
    % b7 ~$ }  a& V" r2 P- V  \& y
  7. #include <sys/socket.h>$ T) D; K1 q% L: S
  8. #include <netinet/in.h>
    & U' w$ z: k5 }* U$ U$ i1 r
  9. #include <arpa/inet.h>% O$ y" ^# j  L3 Q' R
  10. int main()( `" l* T8 l0 H0 ]& u; y! K1 y
  11. {( a3 r5 q, L' U; V9 {0 u
  12. int sockfd;
    " h5 b- O/ P- V0 n/ a
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    5 P' Z: G2 k4 Q, z) d4 u
  14.         {9 V8 J6 p: N8 C8 }
  15.                 perror("socket");9 K) h+ m; ?9 J+ l9 E0 J
  16.                 return -1;
    - g4 j8 Y, z  H- I
  17.         }& t) Q% _4 w9 l
  18.         printf("socket...........1 f' y+ o7 ~! ^' n5 y, y% U
  19. ");
    ( h( v' R9 E) s5 z% J
  20.         
    # H9 C' X5 ?- ]' u; {- j+ t
  21.         struct sockaddr_in srv_addr;' A* v4 ]1 |: {. Q; [4 o' l
  22.         memset(&srv_addr, 0, sizeof(srv_addr));+ M5 ~- x( P9 O9 T, p# p  g
  23.         srv_addr.sin_family                 = AF_INET;) }9 ]$ D0 P3 U+ K; d
  24.         srv_addr.sin_port                         = htons(8888);* b* H" v- x3 w7 D& [
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");3 r& h$ H* R& L+ J9 }
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    $ `3 g" f+ H8 g; n
  27.         {& G6 W/ _# ^! Q7 T, m
  28.                 perror("connect");8 Y  h8 z' p- p
  29.                 return -1; //exit //pthread_exit
    ; k! b# I" n0 P8 U
  30.         }8 {3 q+ I/ p1 _1 m
  31.         printf("connect..............
    # k) t* T( b: e! Z/ m6 P% _
  32. ");. t8 p. ]$ e& j' ^% E
  33.         char buf[100];
    0 i& ]+ k8 z% C' v, L
  34.         int ret;+ }7 g4 J7 A+ C: G
  35.         while (1)
    ) H% B8 `% B9 ^6 {9 q1 u* U
  36.         {
    $ q: `! M, T" c; p
  37.                 printf("send: ");
    $ q: o, U# k1 {3 |8 X0 R
  38.                 fgets(buf, sizeof(buf), stdin);
    4 A1 o% T3 C/ {. p3 G" B* l1 ]
  39.                 ret = write(sockfd, buf, sizeof(buf));
    + L- r& ~* D) K6 v# q) `5 k4 A
  40.                 if (ret < 0)' w: R/ q3 Z3 U4 `
  41.                 {) Y% N" }( x& i1 g: L
  42.                         perror("write");
      n( S7 ?; n0 U, c
  43.                         break;1 q: A( m7 C  i" _' B* n: g
  44.                 }  P! b1 V6 y7 i) Z; b; B
  45.                 if (strncmp(buf, "quit", 4) == 0)& Z$ s9 S9 D1 W+ m
  46.                         break;) T2 K# l! {$ S5 i* G( d3 G* X: l( O# F
  47.         }
    8 |1 N  E2 f( _  Y  v* s
  48.         close(sockfd);6 X- ]0 g3 _& ~' B
  49.         return 0;7 F& c. E/ Q' `' V6 |$ k
  50. }
复制代码

0 _" W# V# V. B0 a# |9 |! G. z4 _, K+ w% @7 z. u9 \
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 06:57 , Processed in 0.126210 second(s), 22 queries .

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