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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

: [& i, [+ Y& P4 S2 d

7 W# N7 L- C3 ]  G6 k6 d# p. bsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。0 W; \6 [' U/ A) [3 z

) t4 k8 Q* R& K; L
8 R! C6 f, {( F8 o2 l
TCP协议
: j$ G# e: ^1 |3 F/ F. jTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
6 _% k8 `' K+ u/ ]! H! X" E( {

( |6 j# a# `1 Q" x关键词:三次握手,可靠,基于字节流。
; u1 o% ~1 K4 r2 @, b" J9 ?; q- [
& U" d" e  P0 e
* G8 `# I# f4 o' ]( v; ?
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。: b2 K7 h! ^( z/ y4 K( P; e: J% K
2 {3 n- ~" m: C/ d
TCP服务器端和客户端的运行流程
. T3 \4 _* w- \5 ^8 i$ d1 F  J
5 t- T5 o4 ?" c9 Q& U0 `$ `! z

  U6 ^, u! }; |; d3 X/ N如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
% o7 t; K7 ^" U* r. A* `5 S& s7 K" G/ V
" `2 s2 K) G8 c9 y$ U( g! j9 _1 L
1.创建socket
% }/ B$ C" {, b+ g5 C( E- _2 C socket是一个结构体,被创建在内核中/ z4 t6 M& `5 O. p8 `: p
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议7 Y. C/ z$ N9 i0 W' V0 M

7 a' \/ V$ l* x0 d! j9 i
3 ~. M5 r+ p; j8 R4 z) n
2.调用bind函数4 C7 M1 _& l4 H. l
将socket和地址(包括ip、port)绑定。
. z9 X5 K' ~, h* g  ~' H 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序! {6 L7 G( @. j
struct sockaddr_in myaddr; //地址结构体9 i1 m2 R& V( y8 [5 l) E( r
bind函数. `& e8 O: l4 ]
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)), C0 ?& F1 H5 A9 W6 y" J
1 W% q, X' k  u' s6 f3 b

1 j1 @; `+ A& m3.listen监听,将接收到的客户端连接放入队列
* R2 ~7 R' X" b3 M# l" u1 c. J5 u: ` listen(sockfd,8) //第二个参数是队列长度$ J7 p5 a3 d$ K
- ?1 `1 y$ A+ J2 B
: O  Z. D0 A2 g# G
4.调用accept函数,从队列获取请求,返回socket描 述符
" ]( s6 Q: A  g  C  如果无请求,将会阻塞,直到获得连接% E& |& o% ^; O" [* T. @4 o
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
! k; a. Q9 o$ }& {  n4 B0 V% G% P, ~" }# c2 G5 f  i
  L9 l' |$ ?2 G$ @  Y+ ~
5.调用read/write进行双向通信7 U# H" b; N8 R$ B3 [. E6 {
# e7 x1 e% _6 C/ m" ^2 G
  W- d9 e# N  ?
6.关闭accept返回的socket1 `; U% W+ A2 n
  close(scokfd);
1 {. `  N- p  I6 N) Z( W: g1 y8 {, u) e2 e# p. P

) M3 M- X% ^: M0 L6 p' ?) U& Y' o2 k* N  l. Q$ h. H+ k' E8 }6 V

: R' ~# L$ o) F  M1 ~! q  t# v7 z下面放出完整代码
* q" ?7 B. {" d7 |8 s6 J/ W- |& A- Z' S  ]
  1. /*服务器*/2 l3 q2 _2 o) L0 }* N8 e& D
  2. #include <stdio.h>
    1 N7 a( M% ]6 g( A7 K
  3. #include <string.h># x6 X3 P& p# f8 O# G
  4. #include <stdlib.h>
    $ \. v1 b5 q5 R: N2 Q* Z* k
  5. #include <strings.h>
    2 ~+ Y. u: }1 @" C5 e/ c
  6. #include <sys/types.h>
    / o, I4 {$ L& S0 j* i: c/ {
  7. #include <sys/socket.h>
    " x6 ?) S( b7 l+ f' i6 V0 ~
  8. #include <arpa/inet.h>
    8 T6 w% k8 B  n! D) q* w; w; c
  9. #include <netinet/in.h>/ D% q0 S' [, [+ q  p
  10. int main()
    5 f* c7 @1 Z2 b' A9 I
  11. {8 C  M. Q( e2 f
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字3 P( H0 ?7 O+ w& j- W, B
  13.         if (sockfd < 0)0 i; T/ F( H" y% L) u0 W* S* h
  14.         {- U" `- B; U7 {
  15.                 perror("socket");4 i, y" H. d: U; Q9 a
  16.                 return -1;* N( R& o" N' D, x0 J- c
  17.         } //创建失败的错误处理0 f6 @0 V0 B, C+ ^% q
  18.          printf("socket..............
    # A3 f9 n5 i) }% X8 D) [# M
  19. "); //成功则打印“socket。。。。”
    : {3 a, x! Z9 C/ z' }
  20.          7 {0 R8 a) m# `. t4 ~
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体* b& u- Y' D; W% F2 ?2 {$ r; G
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见). ]8 d$ G$ u/ k- A0 B+ x# V
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ; {- K9 }6 F$ k+ b" P
  24.          myaddr.sin_port                 = htons(8888); //选择端口号( g1 d% k' `, j0 L; k4 J
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    . `. U0 O. V3 v2 Z

  26. / h: W) z3 I5 l6 G6 l- ?
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    0 I; N) Q- d! K7 m. u6 s
  28.          {' P' u) Y5 H. D$ w1 G3 D5 V
  29.                  perror("bind");6 ~% `2 ]6 o6 R6 t
  30.                  return -1;
    3 u( W9 ]) N2 u7 B/ O/ b
  31.          }
    " x/ `5 y  b8 L- Z' ^4 R* T
  32.          printf("bind..........
    - n/ [7 q2 g1 _% F
  33. ");
    " f/ F' e$ O0 R  e

  34.   H$ W" A, p% H: s( H9 t" S
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听3 `5 L  L/ u. [2 f0 K! I# m
  36.          {
    8 j  p' f5 O8 k! e& u
  37.                  perror("listen");
    0 g6 [# `/ n2 J+ q. P) R
  38.                  return -1;1 K, m! j; N2 G: s, j- @/ V
  39.          }& L- `! w9 z+ O1 s2 l
  40.          printf("listen............
    ! ]  j% v; \+ c: Q7 L* `+ ?+ o
  41. ");% A# A2 I8 S7 k( U# |
  42.          " P2 J( L- A. R
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    + }3 H* |% ]+ q/ z
  44.         if (connfd < 0)
    8 {+ k: L- k1 c3 @  @4 a1 d% y  t
  45.         {
    ( {) ~  V! {* }6 V( o% A2 T
  46.                 perror("accept");
    / ?1 K) ]2 w5 Y0 g4 q
  47.                 return -1;8 A/ t( Q6 ~$ _* m4 J
  48.         }* F: D5 l2 P, w# S
  49.         printf("accept..............; ^- y( W# `+ C, s
  50. ");
    4 `6 X/ u5 R8 X
  51.         char buf[100];//定义一个数组用来存储接收到的数据0 v" C& r: }( k2 T2 a' W
  52.         int ret;+ T7 T$ B: ~  f( V- [
  53.         while (1)
    1 @& d8 W3 i% j8 n
  54.         {
    / Y# L& W5 o/ ?
  55.                 memset(buf, 0, sizeof(buf));
    # T+ B2 [9 X9 ~
  56.                 ret = read(connfd, buf, sizeof(buf));# m+ D- o2 L/ w3 V  U
  57.                 if (0 > ret): s7 j2 C5 R1 @2 a$ ^9 i) ?
  58.                 {
      ]3 u( V  b! @4 n
  59.                         perror("read");9 F1 ^. u) P$ j6 Z  u
  60.                         break;
    1 m, U0 C3 A5 W- p. N! ~
  61.                 }//执行while循环读取数据,当
    1 ]7 P, j# j6 P$ H- N# m
  62.                 else if (0 == ret)5 g& L5 I, s  l. m' R
  63.                 {
    7 W1 ]3 E' Z5 T
  64.                         printf("write close!
    4 ?! N! L. B& Q9 K/ x  A$ N, \
  65. ");
    + r; u! w+ S$ V+ t. u' o
  66.                         break;0 O' }; O* C8 Z
  67.                 }
    0 U4 w, U! q, q. b# h4 r
  68.                 printf("recv: ");
    * {0 u! \2 l# x" o- a
  69.                 fputs(buf, stdout);//打印接收到的数据
    ) k. u8 |9 A/ B, @0 s
  70.         }! `, j7 E3 s% v, h4 i6 b; M; k8 @% ?
  71.         close(sockfd);//关闭套接字3 ~0 p) T: ]. z3 S4 r
  72.         close(connfd);//断开连接
    8 d, B7 P; Q& S9 `
  73.         return 0;) R2 @( ^  z) M& p$ v5 K% Z
  74. }
复制代码
5 N( c7 K7 m2 x/ u1 Y
9 d' o9 r" A/ T+ O) M* _
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    * Q# w$ q8 x5 _( E) X/ n9 c7 m! H
  2. #include <stdio.h>
    , f2 k  `5 q/ L0 R. r# o2 g
  3. #include <string.h>; P7 [9 N. h6 G! i; O4 d- \
  4. #include <stdlib.h>& O& Z) V8 g+ i% h- x
  5. #include <strings.h>
    0 o- S# q- ?! w9 q* T6 Y
  6. #include <sys/types.h>+ J% l; L* n, X& u
  7. #include <sys/socket.h>
    2 |- U7 Z+ m1 I: S2 R9 }/ ~
  8. #include <netinet/in.h>
    7 y' L6 Z" d! s0 J8 K
  9. #include <arpa/inet.h>4 l! D6 o! i- `; R* y
  10. int main()
    ; ~: a4 u0 ~/ G7 _0 u' {8 m
  11. {( A$ Z' {$ }/ F$ Z( W! p; m- q4 p+ S
  12. int sockfd;% d/ {/ @3 Q8 H1 b- i9 J: o" o5 G
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))5 s5 }! e  j, W, l6 `" v; q* w6 l$ [
  14.         {
    + e0 F2 J: L7 R. T  L/ r. I
  15.                 perror("socket");
    9 Y- V* ?, Z( s! l* G3 w
  16.                 return -1;
    . }, X& Y3 O: @9 J  H
  17.         }
    ) ?# w( d; S% L8 h! _
  18.         printf("socket...........
    - I  L5 P- g/ ]  }8 L
  19. ");  a! H1 [; q% @& G
  20.         ) ?! `% e4 W- H0 y8 U9 U/ }: Q9 H8 z
  21.         struct sockaddr_in srv_addr;
    & o3 m) J9 e0 w, c" |9 D
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    - R! q( N0 |; }& o
  23.         srv_addr.sin_family                 = AF_INET;4 m2 @. N- J) n3 X3 V# O/ C
  24.         srv_addr.sin_port                         = htons(8888);% Z# Z  B; d* R% {' t4 ]
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");! V5 I7 I7 u. T0 y# N# ~' B. _
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))# L) @% @$ n/ x% y
  27.         {2 Y6 L7 {7 x: U* E) n
  28.                 perror("connect");
    % g% P9 f/ {! |* {
  29.                 return -1; //exit //pthread_exit& H0 ^" d* R; l5 I9 ]% U
  30.         }
    ! h/ q) `- |0 y9 s* p0 a8 ?
  31.         printf("connect..............$ I+ i! v6 s+ J, j
  32. ");
    * T1 |9 I' I2 X& }& y. G
  33.         char buf[100];
    1 E2 y; G0 ~) m" b0 s+ M& D% p
  34.         int ret;5 \# D$ d2 ]' }  K) B2 t+ D7 }) i
  35.         while (1)
    + X) }4 Q5 |% @9 c( Q
  36.         {
    - B7 l6 [* T/ d7 i! r
  37.                 printf("send: ");+ z1 j# q& p1 g8 z! W( O
  38.                 fgets(buf, sizeof(buf), stdin);1 [$ U9 r7 i9 \* V4 v
  39.                 ret = write(sockfd, buf, sizeof(buf));, c  r6 ?# j! y) i' V; Q9 G0 O
  40.                 if (ret < 0)
    7 y8 r3 M- n) N+ H/ a
  41.                 {
    % Y9 _9 N9 I9 [+ c! K
  42.                         perror("write");9 Z+ j. E6 T1 a
  43.                         break;* C* M& j8 k5 {& }4 g8 U/ l2 r
  44.                 }% A0 r( L: f# S7 w% k( B
  45.                 if (strncmp(buf, "quit", 4) == 0)
    * E6 j% a& G8 a
  46.                         break;
    ( `* x, Q; A! }0 G
  47.         }
    2 I2 S4 u' D; {1 m* K
  48.         close(sockfd);
    7 }1 d" t, E+ k3 W2 C+ s  R6 x, c, @
  49.         return 0;: k3 r) q6 b, Z1 D, q! t
  50. }
复制代码

% T# g" N  A4 x6 z4 f0 Q9 X% ^( G5 n* G7 h7 p7 H
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-17 18:06 , Processed in 0.074084 second(s), 23 queries .

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