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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

7 y6 V# f6 H/ ?; ^  ^% R0 D
3 L0 S% B: p% s8 O, r
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
, K1 o) z+ e% k' U3 k  q0 \5 F% P3 I" c) `* s( E! X& u2 L
8 L& J$ {7 Y$ o3 y8 d
TCP协议
5 @% g  M0 o) a0 }TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。: x5 u' D- T/ |& J6 x& W( n

+ \6 P: f5 E+ _/ _2 W9 t
7 u. M$ S6 v1 c2 I
关键词:三次握手,可靠,基于字节流。
: F- l6 F3 v( o# J2 N# R0 }& ?  g' L# _

/ a6 a) ~0 G. S6 L) m. b; {可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
/ f: |) B* e4 {  T3 h+ ~  w
+ {# x8 b/ K9 GTCP服务器端和客户端的运行流程8 t) _) `8 P  u$ g3 s; ?/ d
! u4 i" f* ?4 e$ B1 Y
6 g: w  D" U# n8 `: Z. R
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 O; k. X* H+ ?. B

  b1 z6 d+ p$ Z% ^4 L+ ]$ U
3 `0 f4 w7 D) e9 S% ?) }
1.创建socket+ ^6 t5 \# G3 v) u" Z- O
socket是一个结构体,被创建在内核中
2 \4 |. z; \7 I+ Q sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
7 q% X4 u; ^* I* _
; x4 b. s2 X1 j% s) A

; Z9 v' z# [4 y" u; w4 q2.调用bind函数
- x, Z# C" y; o8 K0 P+ U 将socket和地址(包括ip、port)绑定。
$ Z& t# C6 b; K, s, \ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
8 i9 {% `- b4 S# [: C: f  Z- r struct sockaddr_in myaddr; //地址结构体/ Y% J8 b: X  x# {8 e" j
bind函数
2 q7 D/ _* Q/ q  C bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))" N, Q  o* H0 L0 `: [& ?- s
6 x; P2 X1 x: ?1 c9 l

1 x) a& I9 x  G' M- Z3 w" Y3.listen监听,将接收到的客户端连接放入队列: t+ A7 r1 ^4 \" }5 }
listen(sockfd,8) //第二个参数是队列长度, D1 J8 E( |- S/ g% E4 D4 r

4 R2 o1 B7 D9 G" q2 t: S6 M
/ i) q$ G/ q; G0 ^7 [0 k
4.调用accept函数,从队列获取请求,返回socket描 述符
9 f/ {0 o$ L- M  如果无请求,将会阻塞,直到获得连接
  K' @7 o- m' k) e8 y  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数! t, O2 l& I  s; U+ ~. o# U

# X2 i; ~  O% k8 G0 i, j: G
# y5 ]6 R; k6 f- _4 \, M$ |2 e1 m
5.调用read/write进行双向通信
) o+ Z4 Y! k% R( `+ t& d2 y
7 v* `; W  }& |" z5 s
3 c) k2 d! B' Q. ^8 j1 C
6.关闭accept返回的socket
. ~5 D, ^: G$ n  `- W  close(scokfd);
: j& T/ H6 W5 p( r; q  p
. [% g6 `. ~. K1 ?$ r% h8 U0 ^
) R' I8 _6 P( L) r4 w! R9 e
' Z0 X7 n4 O9 t. I

5 W& S4 R9 W/ y$ j: G8 b, o$ p9 F下面放出完整代码
* M; U. n3 V; }8 s: F5 L: l
3 E; ]1 G# b! @0 M- N
  1. /*服务器*/
    ) {) U6 G% D# f, @- P1 ~1 l
  2. #include <stdio.h>
    4 a$ M! t$ w# D6 J! V2 U% O
  3. #include <string.h>
    . T3 c1 |3 [( p$ d
  4. #include <stdlib.h>
    , |: D8 k0 n1 N$ j" H% f6 o% ~9 O
  5. #include <strings.h>
    / `% t: l5 i4 R7 v, c2 m
  6. #include <sys/types.h>
    9 b0 Q' G) y: h; ?5 X
  7. #include <sys/socket.h>% t. }# ^% D$ v& F' o! p7 H! X
  8. #include <arpa/inet.h>
    + K" G& I  _9 p0 \7 p$ X" g# {1 b
  9. #include <netinet/in.h>9 H' c( T! @7 S4 \+ `$ U
  10. int main()4 ?- c# `. J" f
  11. {" Y; s9 a) J. }( A
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ) e2 ^# o# ?. f0 J! U
  13.         if (sockfd < 0), v: E. h) w+ U. T6 N
  14.         {
    2 m; m# W' j: G# P1 p6 C) ^. V
  15.                 perror("socket");
    1 ~) T$ `* C: d0 E% [! m
  16.                 return -1;
    / }( V4 [& r7 S: U; S9 c
  17.         } //创建失败的错误处理
    ' j- o9 r: m, ~3 v7 v
  18.          printf("socket..............
    9 T4 u7 y) c9 r  n3 U  V
  19. "); //成功则打印“socket。。。。”
      E1 D  ]" Z6 [# ?8 F& a* e6 a
  20.          
    % W0 T5 A, {3 x% a8 q& O# {9 X
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    5 v( a$ H# E* H* d! @" h6 g! N& U
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    & D' B/ K4 y- o  A/ _7 \$ r
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型* w. P3 @- D1 k7 s/ N
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ' J$ d5 o  A8 d9 E
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    6 S( Q5 X/ l  v: A

  26. ! I* W& }" J4 K% I* O, U
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ' A4 U* v( @- P0 A% n0 {& I
  28.          {) u! O$ Z) A* {4 j/ z' [
  29.                  perror("bind");
    " a1 m, Y: E: i+ ^6 ^1 Z0 N! ~# a' k
  30.                  return -1;: y9 z) }9 W; ?
  31.          }0 q1 a& a$ e/ z9 s( _9 F
  32.          printf("bind..........
    " ^8 S! [# k9 a  f8 p1 x
  33. ");
    7 K4 u) r- W! C  s( k2 s
  34. 2 B. v8 M6 x! ^, {, i6 c* F& F) F. Q
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听# h. M& Y" ~% o$ Z
  36.          {  m/ e) c& Z% d% V3 _
  37.                  perror("listen");
    3 {+ T7 w1 h2 ~4 j  c4 [, z
  38.                  return -1;
    ( D3 x+ T% Y0 G2 A  e$ L# V6 u
  39.          }! O& n) O( E& @1 x. d2 y
  40.          printf("listen............8 V6 T9 z. M: e) J9 e
  41. ");
    + Z4 u1 z. G8 Z: c" ]1 `- q) {5 a0 c
  42.          % j1 e2 a) u  F/ {! Q+ U4 h* R
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    - N- n, Q# u: n! @8 J
  44.         if (connfd < 0)+ |# q4 N. s! Y. }1 B6 T  j7 ?, [& ~
  45.         {
    # O0 @4 J7 J3 Q. V6 B; E
  46.                 perror("accept");
    1 D) e+ B% |  M  }% k( W! L% d+ y8 @
  47.                 return -1;
    0 @. T/ o1 |1 ?3 O3 A2 q
  48.         }6 W8 |9 a, _) ]0 r, K; s
  49.         printf("accept..............
    : P. z+ V) z& a! s9 Y
  50. ");, D3 Y) n2 F+ K6 `% e
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    & o( d* t# }0 G. W4 o  [, y
  52.         int ret;$ v. @: g* l) {- U" Z
  53.         while (1)- o9 ~; g( A5 m$ I4 Q; [5 X6 @
  54.         {
    ! [) j; N4 S5 y! M0 l) f3 j
  55.                 memset(buf, 0, sizeof(buf));
    / @3 N$ l2 H# B. {. ?! m% x8 `; K+ D
  56.                 ret = read(connfd, buf, sizeof(buf));
      i3 n0 b: r! C7 @
  57.                 if (0 > ret)
    8 k7 b. h; h8 P# f
  58.                 {8 ?7 X$ B8 Z( P0 L0 k; k! R! c' Z
  59.                         perror("read");- o2 x9 ?0 G% v8 m
  60.                         break;
    " ^' i/ y5 `* J8 x1 u
  61.                 }//执行while循环读取数据,当
    / \' ~) @  H4 ~4 `( c# V4 J
  62.                 else if (0 == ret). j7 v1 Q: y9 n7 k$ ?' E
  63.                 {
    . I' u$ ]+ e2 x6 Y4 g+ L' R
  64.                         printf("write close!
    # I: W# T0 c/ h1 X' q4 x: w' y
  65. ");
    . R$ h% u, _$ ^& i- s: @, z
  66.                         break;3 F) M6 p6 J5 y6 J. H5 i
  67.                 }- X9 t4 N: Q, O% T5 \
  68.                 printf("recv: ");
    . Y) |+ H+ I0 e0 }8 P5 h
  69.                 fputs(buf, stdout);//打印接收到的数据8 H5 Y. V8 s) F' T# R7 B
  70.         }
    & `8 J- m, c- l4 v+ l' e
  71.         close(sockfd);//关闭套接字* D6 c" }6 }) R1 g; S- _: u2 K; h
  72.         close(connfd);//断开连接
    ) s% |& l: F( u0 b
  73.         return 0;
    " z6 g, O( j2 B4 t
  74. }
复制代码

: [! a* Q) J3 m$ S* a! `" @7 W; Z" L" J5 ^. G6 L7 s. g, [
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)$ K0 A9 d. S. ^4 V5 y  v  J2 {
  2. #include <stdio.h>
    0 A) }8 l- i& a
  3. #include <string.h>' t3 s8 @5 @+ m5 h* j* w' Y
  4. #include <stdlib.h>! a( P( ~! s6 O
  5. #include <strings.h>
    $ e/ o. F* m& l) i" n& f1 {
  6. #include <sys/types.h>. O0 E& a* Z3 v3 Z9 p: E3 D4 V
  7. #include <sys/socket.h>4 p0 [/ u% X4 F/ R+ N
  8. #include <netinet/in.h>
    $ y( O* v4 D6 k! _8 h, m5 v
  9. #include <arpa/inet.h>' A( \& O1 J" D! J
  10. int main()
    6 P' W* N+ d1 V  r, R' Z7 Y% @5 O
  11. {
    $ E& {2 w  f) i& c+ A7 J
  12. int sockfd;5 ^5 _: D5 a' y
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))* M) q& u6 X/ `. D- ]/ v% Z9 h
  14.         {
    7 k; ?  h, J; e& y) B5 E$ J
  15.                 perror("socket");: A5 D; @" A7 A
  16.                 return -1;6 @$ T8 Q6 y4 Z: P# b! F
  17.         }9 Z7 t5 J: i, B/ C0 @; J/ G3 L
  18.         printf("socket...........) P7 u; M) }7 u, W$ q4 o
  19. ");! H& R$ W* A8 {: w% @6 x
  20.         
    5 @' @; v6 [" p+ M3 O7 k
  21.         struct sockaddr_in srv_addr;
    $ K# _% l# c5 N7 Y  R  c
  22.         memset(&srv_addr, 0, sizeof(srv_addr));# p3 O0 z5 \1 B1 e' v
  23.         srv_addr.sin_family                 = AF_INET;
    + M9 ~# p# R% ], _2 \. S  N3 M) g
  24.         srv_addr.sin_port                         = htons(8888);
    ) m1 z* D  E' h9 O
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ( \2 A, G3 P5 l
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    , K! W% ?8 A! x5 F4 s2 @0 l
  27.         {. `+ h% w: J4 }( d
  28.                 perror("connect");; z' k4 D: b% d0 r
  29.                 return -1; //exit //pthread_exit
    4 m/ U, f2 n- V3 v. m) O
  30.         }
    $ D* H- s5 ~" F2 F6 c
  31.         printf("connect..............0 |' \! B+ U9 @" h! m" p
  32. ");
    7 k) u% A! q' p; B$ Y7 y
  33.         char buf[100];5 A' E  E, g1 T3 q
  34.         int ret;/ L% t) g% W* w& n2 `
  35.         while (1)# T7 _6 D. M8 C; s- Y4 [
  36.         {& I6 \# U2 [2 i. j+ _3 P
  37.                 printf("send: ");- e3 M& ]) k' u0 X* a3 g
  38.                 fgets(buf, sizeof(buf), stdin);
    1 ?4 z  B8 u7 D) b( v1 N
  39.                 ret = write(sockfd, buf, sizeof(buf));
    6 O' U. b7 n9 I  ~3 h; R
  40.                 if (ret < 0)7 Y2 h5 n5 {- w& t0 G; b
  41.                 {& q0 W) O: h: f, k
  42.                         perror("write");0 C, Y# y' V& [
  43.                         break;4 y- [+ X" _4 s' h5 T5 m: f
  44.                 }5 r* r3 z4 _) n" X- v4 I7 j6 I4 E* w
  45.                 if (strncmp(buf, "quit", 4) == 0)" I  ?8 Y. D/ r& d- v6 `
  46.                         break;
    5 E# V9 Y4 z. K  _& t
  47.         }
    3 x  {; }2 {) ]# p0 e" i
  48.         close(sockfd);
      h  |& m6 b+ M/ u+ M3 E+ Y
  49.         return 0;
    ( a5 Z, j6 V+ F) |! P4 ]! {8 j& p
  50. }
复制代码
5 T- l7 l7 U! N' g$ |

0 ]. g) {1 T3 n- e
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-4-27 06:39 , Processed in 0.123966 second(s), 24 queries .

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