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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

, ]6 B. u: I: B( P
  [- |) D5 g  K3 f  P+ o3 A
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。4 A/ T% o, ~3 N+ ?: J1 l

# ^5 c) }- l. a$ _4 V8 k  k1 t
* r/ v& U6 S, g# q9 k* ^
TCP协议& r) o% A! F( M  @; P7 y% ]
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。3 }# b9 U: J/ o4 n5 E$ N& x

5 {  s! l" }0 F& w( S6 f
" t. D8 i0 U3 k. V  q
关键词:三次握手,可靠,基于字节流。
) V5 p3 R) F# G- V* n' ~2 `3 G
7 C# N3 L1 J, Z

: T/ h; Z! V; H9 Y  O, |可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。) Y+ y$ T1 Z9 f

1 M3 s& r) ~: M4 s$ s6 yTCP服务器端和客户端的运行流程
+ c  S2 }- V  O! u$ w0 G0 q0 c' y" F* q$ v: y& i6 i+ {

! l% ]2 D! l* R" c如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
  N4 Z3 I" w  I
9 [- q2 u7 i/ A  Z8 ^
2 Z$ I; f7 n. D! k2 g
1.创建socket
! c1 {% [6 j& F7 L7 o! ` socket是一个结构体,被创建在内核中' g2 b) @2 r6 o/ M
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ H8 S& v! l; M' r5 N- B

! i6 k. s8 Q$ H/ P" K. G8 Q! M  i/ Z
+ r: {3 @0 \7 c8 _  e" F
2.调用bind函数
' }& l- C$ L9 j" ]0 d 将socket和地址(包括ip、port)绑定。2 S' E" T. Y0 f3 }7 ~% {& e
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
+ Z2 `$ U+ c) f0 O4 {+ m# a, Q struct sockaddr_in myaddr; //地址结构体
7 w' @. [" X1 n: T4 m: E5 g1 }& s bind函数
* D2 l; h' M  E7 C: V& R; s bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))8 O: j6 F( [' b/ |8 V6 ^6 c: N

3 I9 A" W3 a3 `+ {! ^% c* P
( ]9 A1 G+ \) x$ m) R! d
3.listen监听,将接收到的客户端连接放入队列6 `7 I; i3 h3 Z. K$ K
listen(sockfd,8) //第二个参数是队列长度  q% I; v# x$ e) p

  X& z0 W1 G& Q6 c% Z. V

% {. _( ?: |& W8 A6 R7 h, K" _4.调用accept函数,从队列获取请求,返回socket描 述符4 j* M+ W7 A) ^
  如果无请求,将会阻塞,直到获得连接  d: W4 S& i- C  r  [/ D. W
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
$ c1 o% q2 m: C0 a. t  s& J" l& H, j9 P( K8 w) z1 r! [

3 s8 G6 w6 G- d+ I% h7 o8 Q$ F5.调用read/write进行双向通信
$ p0 ]% w  J1 _% V& W5 I% Z; V/ x1 O  {( j/ L
% T- D8 r+ R$ O- d5 }4 c
6.关闭accept返回的socket- |. C! u) _# Y- q/ \5 h
  close(scokfd);
, i- J( q. Z1 p3 W7 z
6 x" \6 n1 G$ W
+ F- p5 d1 x; n4 [* j

2 y1 m( \. S; A8 G4 |7 X' w" F

, ?9 J% C% r2 x0 p1 u' c4 ?9 S0 E* o下面放出完整代码
- x! m; t, \9 R/ V9 U7 ^5 E7 `* D& I
  1. /*服务器*/9 G* e' Z3 m' i) q& C; L
  2. #include <stdio.h>" b8 G) v1 {/ @' r& k* R6 |
  3. #include <string.h>0 b2 S. ^9 S3 a2 i
  4. #include <stdlib.h>4 y+ p7 V7 w, X: E( u
  5. #include <strings.h>
    . j/ U: f# x3 T
  6. #include <sys/types.h>* w2 M" T( V0 o, C9 Z
  7. #include <sys/socket.h>! q* X2 X8 D3 s/ I
  8. #include <arpa/inet.h>7 W6 C! ]  u- Y5 }: n% l
  9. #include <netinet/in.h>" ?( q  ~7 \. P1 y  Z( X- w) s
  10. int main()
    ; ~* p8 t, l/ I) P4 y
  11. {5 c  a0 G1 m' q
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    + |; Q! p4 P! Z; L: W5 h0 B# l8 C
  13.         if (sockfd < 0)
    8 I5 Q+ h: k1 ~+ s$ D( Q
  14.         {
    8 F/ h8 t( G' X
  15.                 perror("socket");
    % @9 ?7 O! W) e$ |& |. u* L; K% W/ S
  16.                 return -1;
    " ]8 b+ a! U& Z& j
  17.         } //创建失败的错误处理- D# a% K" ^- G8 B6 a
  18.          printf("socket..............9 H# W. Q; C2 X. O
  19. "); //成功则打印“socket。。。。”4 ?" o( `! w0 b: P! u4 ?4 M3 T) u
  20.          # j: l$ d: z& g$ y4 T7 u
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体, k- |6 }& y/ b/ k# U2 N
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见), @6 }0 d: B6 `: S1 n( U, s% G. B: k
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    : h! e0 O1 _$ W, Y4 `- H
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    4 `# \; G0 x% M1 z. ~: m8 b
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址7 g& T- e- E. A; w

  26. ' ^! P" k  ?/ s- @# ?" J
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字/ F) R9 \6 x% m& Z
  28.          {
    % F$ @: J7 s7 V/ x. z+ [4 r
  29.                  perror("bind");* b; N$ F" ~) }+ A
  30.                  return -1;6 X- G. X6 k6 X- V+ A  r* N: W) C
  31.          }
    - V; b6 p- O; z' O. K/ F$ `
  32.          printf("bind..........
    2 D! a2 t- W1 N% U$ h. b0 k4 Z
  33. ");
    1 L/ s/ ^' L# _& @$ H6 y

  34. 3 ?' J& {! ~, F8 |$ b8 m" p
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    / w* W9 Z. P8 F$ l; I1 ^: [. h2 ?
  36.          {
    3 R; W7 R6 x' p# D- O( u- \
  37.                  perror("listen");
    6 X5 x! T$ i% p2 w0 [# f& @
  38.                  return -1;8 ~' Y2 a" u( V! Z/ k& t7 o
  39.          }
    & x! J& D- n' b: D  A: ^& l' {+ H
  40.          printf("listen............
    ; P3 F1 F6 ?; W
  41. ");
    $ M9 F4 w2 @- r
  42.          ; @" o( C1 F; o+ Q  ]0 z
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    " g& |8 }  ~1 b. J; k. E
  44.         if (connfd < 0)
    1 n& M0 X, \* |. a6 I
  45.         {
    2 `; o3 F' [) c5 C% z, ^
  46.                 perror("accept");
    ! A' r  L& a" F  V( `
  47.                 return -1;# ^. t- V( r. ?) ?  R" a) ~
  48.         }
    # u3 ~0 q/ ^5 F  y
  49.         printf("accept..............
    ) N9 R! U% b- ]) C4 y
  50. ");! b  J1 I% d, ~% ^, C; Q
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    ) R' L7 f: ?- h) z1 U
  52.         int ret;, G, G  t4 j. j+ J1 x
  53.         while (1)
    # {# ~. g. R/ _) N% {: Z3 j
  54.         {
    2 i9 D: @% u) K7 S' F. n
  55.                 memset(buf, 0, sizeof(buf));
    ' a# Q: j( W; b
  56.                 ret = read(connfd, buf, sizeof(buf));+ T" W) c/ \3 f) P' A% b
  57.                 if (0 > ret)
    9 z3 z/ Q5 K+ y# ~
  58.                 {
    7 S2 l3 L* J& J8 V; X: d' Y& a
  59.                         perror("read");, K- I; f( L' c. I# q/ n
  60.                         break;4 k, g. A* H2 u
  61.                 }//执行while循环读取数据,当/ {5 \. K$ R% ^" r/ }
  62.                 else if (0 == ret)
    1 G6 q  g3 [; U1 j  H
  63.                 {
    0 s# b# i4 f2 Q: M# t
  64.                         printf("write close!/ I8 _& F# P$ c& Z
  65. ");
    $ E/ ]) o( f- ?5 ?
  66.                         break;
    ; ^+ k9 y6 K# d
  67.                 }& c6 w/ Q6 q  b4 r0 R
  68.                 printf("recv: ");' B( K  v: D: O2 J) x! j
  69.                 fputs(buf, stdout);//打印接收到的数据& n5 u# m- }1 ~6 Q1 m0 d8 b
  70.         }. ~& U' L" z3 G: g) A
  71.         close(sockfd);//关闭套接字
    - W- A% ^3 Q! i3 I7 Y( x
  72.         close(connfd);//断开连接2 X& J4 h# M4 e2 g
  73.         return 0;2 o2 E" o0 X  W3 R7 H
  74. }
复制代码

, S3 ~. r% ~' F0 ]
$ n; E/ X/ ~/ a- H
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)$ C+ l1 k0 H! h& V3 V) I* G
  2. #include <stdio.h>2 e- e( O! O6 B5 X4 R% S7 B4 \9 j
  3. #include <string.h>
    7 L; P7 D' i; q
  4. #include <stdlib.h>* v3 v6 {0 l7 |; w; z
  5. #include <strings.h>& |5 x3 h) E; \0 O
  6. #include <sys/types.h>
    % _( B$ ]& y+ s4 ?1 k; G0 k
  7. #include <sys/socket.h>1 r. m" g; l; c2 q0 y% Q4 A
  8. #include <netinet/in.h>
    ) }' H! y4 i. _/ [
  9. #include <arpa/inet.h>  t  x4 T: B( T" ~+ g# M/ S7 L5 p$ l
  10. int main()1 a/ c2 z. h& j+ P
  11. {& K7 Z1 B! B/ \
  12. int sockfd;
    & c! h8 B3 U) }# D, P
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))4 M! a, u) _0 w3 B' _' |: M
  14.         {. r+ O1 s7 _# U9 c5 A
  15.                 perror("socket");6 q" o* E6 I) O! L. |
  16.                 return -1;
    - A$ r$ D7 ~# U8 f- J8 h9 R
  17.         }
    8 s; M8 O" T2 b% b! }) S5 H
  18.         printf("socket..........., @% Q( |, ]' U
  19. ");8 {0 q2 l' D- A) ^
  20.         
    5 }) W6 |; E0 w+ ^
  21.         struct sockaddr_in srv_addr;
    0 \; B5 c/ n' E& {+ A; {3 ~% S
  22.         memset(&srv_addr, 0, sizeof(srv_addr));" U7 \3 h: D! o; r- o8 F- `3 p
  23.         srv_addr.sin_family                 = AF_INET;
    ; r% a1 i3 A' v2 M6 Z" [% ]  W7 T
  24.         srv_addr.sin_port                         = htons(8888);
    ' j$ p: w' r1 s" M8 q7 ?) I$ X* S
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");8 X3 ]5 ^% E6 D: _
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    . O. ^" M. U9 N* _* n+ [7 J
  27.         {$ o8 o. l) C* \/ n2 V& b
  28.                 perror("connect");
    : o5 H3 b' }8 Z4 [7 C7 X
  29.                 return -1; //exit //pthread_exit0 Q6 w( b8 }; x9 u( }6 y
  30.         }- E, T% p% n: t/ _7 N- D
  31.         printf("connect..............' v, W4 z: I. x# b7 g
  32. ");1 X; G9 z. D- d/ _+ o6 _+ h
  33.         char buf[100];
    8 A  O- x/ I( d% I6 u* O
  34.         int ret;: I; t0 O* h6 Y  s/ V" I6 R
  35.         while (1)
    ! |: F2 j9 v1 _4 G6 J
  36.         {% o0 z6 z: e: U5 u5 V# E) @
  37.                 printf("send: ");4 x9 P/ J+ i# O
  38.                 fgets(buf, sizeof(buf), stdin);
    # ?( B* L% m( b6 U7 K, r3 \
  39.                 ret = write(sockfd, buf, sizeof(buf));8 `; [- ?! C4 v! x; Y
  40.                 if (ret < 0)
    6 _: u! L; }# F/ ^* `# ~' W
  41.                 {
    $ e9 C3 U- X# q7 M4 U! `
  42.                         perror("write");, Z! S; [2 D' Z" n- p# n
  43.                         break;  C; ?/ }. I  `; y: S
  44.                 }3 h: I' _- t' o# R
  45.                 if (strncmp(buf, "quit", 4) == 0)
    ( y- p, j0 A/ p. p* ^0 E" T% L. |
  46.                         break;! I9 [# }3 I, s7 z  k6 U- r
  47.         }. w$ V4 f7 ~* f1 V& Q- P9 U: c
  48.         close(sockfd);
    ; x' `. O8 @! F0 L  P
  49.         return 0;: P3 D+ t6 B6 t' j3 {
  50. }
复制代码
0 V9 d) P; {1 W, C7 x

6 a& E) F& o' N9 ?! g& g
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-6-26 22:58 , Processed in 0.145708 second(s), 23 queries .

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