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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

9 F2 u' d( B* A1 }" e& D
+ u  C; P8 D; ?* {) n7 M7 g
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。/ Q# C2 p- u" }% n7 U2 I

4 [4 h' c& t6 s0 p  [! o2 p

1 b: d$ X7 n5 V& H  y; }TCP协议
3 W/ t; u# ~3 P2 r! q. i4 Z- lTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
+ z8 V& c* W9 L
+ w/ b; u) z" Q. c, g1 F2 [7 _4 G

5 x) G) E; w, r关键词:三次握手,可靠,基于字节流。
1 b7 F1 B! H3 y- T" w8 y- j& o5 P' z  G) d" `" z# e2 f( {. L
% N, A! G. S3 @. ~0 U
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。, U- @) r4 o' U+ m8 T
1 |8 n* k1 c- O' Y" H6 k
TCP服务器端和客户端的运行流程' @7 v4 o! d# }7 n. ^" `2 o' B
! P8 a* c/ Q- @1 n$ ~* B' T

7 @. N$ r7 |! V; V+ b3 G- h如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?- H, B% |+ f' |! R, x9 w; f3 j

2 I4 k- T6 T6 b6 Y* O  k$ q5 j
1 R! Q; P; ^6 e+ j2 ^/ C$ P
1.创建socket
  {3 ]3 _. m$ F' | socket是一个结构体,被创建在内核中: p8 [5 u' {2 ~5 q& U* ]
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
+ J6 v6 K) H+ M/ D0 j8 ~) Z  N8 F2 [: |; s6 V6 j
1 B+ `  t4 ]/ q2 c$ r! W. N
2.调用bind函数! c. N" d) Q. j* Z: o8 \9 M
将socket和地址(包括ip、port)绑定。- n0 W, m8 ?( W$ M& ]) g
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
* S$ `3 h4 u+ J0 U8 S struct sockaddr_in myaddr; //地址结构体( Z3 D0 R3 T! K0 ?1 I1 H* {
bind函数, j: ]; A: I( _
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
1 d! J7 F4 s1 ^! ^" Y
/ K( b3 Q/ x  V

- Y. j. g% {) H9 _, \8 J3.listen监听,将接收到的客户端连接放入队列
! ~' M2 R# s$ n listen(sockfd,8) //第二个参数是队列长度
* H4 A( k$ x  t$ s/ U% d3 J( o, V9 Q( V$ M

+ m$ G/ x) |. w6 [: W; @- p' n  p  W4.调用accept函数,从队列获取请求,返回socket描 述符6 z+ I2 _& ^5 g' j/ c, \& [
  如果无请求,将会阻塞,直到获得连接
4 }! L3 C8 J& ^! I% `1 J3 [  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数  {- C. N5 C) l# T3 H" V; e2 [

" C2 H1 f! j5 k4 K0 S
( z+ A9 |) ^& G
5.调用read/write进行双向通信
7 n5 X( A9 \$ Z: m* \& I1 Q6 i; x) ?# F: S4 C/ m4 I

+ {& h0 A/ a# r/ C* W2 H6.关闭accept返回的socket
  }5 G+ i' f  {$ l) e; ~' W  close(scokfd);
* i# D7 s' q2 {$ A. Z4 Q" t1 T" [6 ^6 o  s9 L4 h3 G! t& E

7 N8 W9 S2 y% C. s* }7 E  b) \" l& m* ?, |9 h8 k
; l: G. @4 a5 n# H0 L; {
下面放出完整代码
0 R: u9 }, Z0 j* T
- r+ C- d( U! C' V5 `8 [
  1. /*服务器*/
    6 X: C, X% a$ @( Z
  2. #include <stdio.h>
    % r0 z( ?2 _( k6 k8 X* t4 [; I( X
  3. #include <string.h>0 _8 o" U8 e3 P& {7 E
  4. #include <stdlib.h>9 Z% r3 ]; ~8 u! D+ ^; ?. @
  5. #include <strings.h>
    8 K) |/ j% ]8 y0 f7 u
  6. #include <sys/types.h>3 ?8 \( R  S0 X
  7. #include <sys/socket.h>6 q2 Y# q2 `  p3 p& u6 ]+ e
  8. #include <arpa/inet.h>
    # p. `$ }4 h' s' N* r
  9. #include <netinet/in.h>) W9 \4 \( z: F) g( Y6 N
  10. int main()% `0 o% y9 s% z( }, X  f. |
  11. {9 Y2 c1 D9 w  C* @% s
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字6 h+ R2 K4 j2 L+ V3 i
  13.         if (sockfd < 0)
    7 I; |3 w2 s3 T& E/ ~
  14.         {
    * }8 p3 \) _) {5 m1 C
  15.                 perror("socket");+ k. R3 C5 \+ w3 r/ \6 s+ b) `* O
  16.                 return -1;( L  Q* b; E6 M4 o, H" j
  17.         } //创建失败的错误处理
    , v9 M0 A# _7 D: p; [0 ?
  18.          printf("socket..............
    . j3 ^5 Y+ W/ N3 C& P9 ~0 Q
  19. "); //成功则打印“socket。。。。”/ c% T. ^6 ?$ ?  f  O
  20.          + u0 V1 \4 V& V
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ! u% |6 U" m: g# ]  h( ]& h
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见): b3 d, J* t& e' D) G
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型( ^" ~% e( M  B6 U
  24.          myaddr.sin_port                 = htons(8888); //选择端口号8 Z% v8 Y# q, T: ]' s
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    - L' ~, m$ ~! S9 Q4 H& w
  26. " i: M) U2 k9 ^, A
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    , `5 }0 w9 q; J* U
  28.          {
    7 E- E3 O/ ~; \4 k3 K1 u8 H
  29.                  perror("bind");
    1 K- @7 J0 i* K
  30.                  return -1;( H/ O) C, w! B0 @6 Y  f2 l3 o; F
  31.          }; q; i6 f" g( n! ^" e4 l3 j
  32.          printf("bind..........
    3 X( k& q; i7 M! k1 W, l
  33. ");+ \, G$ s$ T& B) `3 S& t( G% ~

  34. 9 i1 a6 w# \: n' M7 n' I
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听7 l9 H+ j4 W. O+ A2 F  V/ t8 b" U
  36.          {
    % x8 \5 V6 u3 I  x+ p
  37.                  perror("listen");$ V4 M6 i# l! x) ?6 f2 \
  38.                  return -1;
    2 T! N' U2 w4 z" ]& _
  39.          }
    ) p5 V$ R( [, k. F/ N
  40.          printf("listen............
    , E) p: b" q. j
  41. ");( V9 O: z- c6 y) z7 P. d
  42.          
    1 [" D  d% W( n7 M6 t6 g# V
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求! g: e; n! i& d, o* q
  44.         if (connfd < 0)3 D& t" h) ^1 f1 ^! H0 e
  45.         {' {0 _$ d; M2 }5 R" T
  46.                 perror("accept");
    9 v! A; c; J4 t* D3 z# w
  47.                 return -1;" g; O* E8 X0 V. G  ~5 Z+ i& a. m
  48.         }* Y# d. S' k- m/ o
  49.         printf("accept..............
    . M' X) N3 R1 l- H4 q
  50. ");
    6 ?; `0 H% u7 b1 l8 f3 X8 c+ Z( k4 i
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    $ a; p' w, W% j  u3 d) @) Z) w
  52.         int ret;
    3 N8 ?1 M0 F% L: j, |& Q/ u2 \! Q
  53.         while (1): P# z: o9 L9 C' Q7 I
  54.         {, z& v* ]5 \! }  M' e6 Z
  55.                 memset(buf, 0, sizeof(buf));
    ) S  S4 p, Z# J, E! j- Q1 I
  56.                 ret = read(connfd, buf, sizeof(buf));) I2 A6 J! F; z! y# q, t" [& o
  57.                 if (0 > ret)
    , y% V! J& @0 R
  58.                 {
    ) |7 k* b8 R0 O; p8 `6 r3 ?
  59.                         perror("read");7 G4 I( H5 g, @0 A7 M0 Y, W
  60.                         break;/ W1 @/ m$ ?" w& B3 \  N* W- t& O
  61.                 }//执行while循环读取数据,当
    - x7 m- U. I3 d) x! C/ n
  62.                 else if (0 == ret)" X" t9 h$ G4 H$ }& n
  63.                 {
    ) p! O  }' L* E. g0 W: O
  64.                         printf("write close!
    % ?. b5 j. G& j2 }
  65. ");" e& Y$ a/ Y% F
  66.                         break;
    $ F; K" u% J' l$ B* E
  67.                 }; g0 B3 f7 @2 f( L1 M
  68.                 printf("recv: ");0 r7 h7 h' K% w$ M$ n$ d. C: q
  69.                 fputs(buf, stdout);//打印接收到的数据
    2 _$ Q" x" h4 ~5 n, k# Z
  70.         }
    / m; R' ]' U! D) C5 U
  71.         close(sockfd);//关闭套接字8 V8 Z4 h& s* {9 J
  72.         close(connfd);//断开连接; K9 }) _' W# x3 f9 {6 @4 S
  73.         return 0;
    2 K- z  v2 }1 s  q- C% {1 f" K
  74. }
复制代码

  ?' I1 @# Y; x8 C* ?. [8 f8 e2 K1 y6 u5 }. a
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    - W" R( L; G6 @9 e
  2. #include <stdio.h>
    8 ?5 a, N4 B# O0 f
  3. #include <string.h>
    7 S+ b+ Z! n, ~+ {0 ?, v7 n; B
  4. #include <stdlib.h>
    ; u9 ?4 Y$ m2 k
  5. #include <strings.h>$ [9 ?5 j2 i, ?$ v% O7 |9 y
  6. #include <sys/types.h>
    ! K: u  K6 G: N% ?: H
  7. #include <sys/socket.h>
    - r" o" u5 x4 k
  8. #include <netinet/in.h>( d! \2 i* l& S/ |
  9. #include <arpa/inet.h>
    ! K5 R- H3 C% `5 y+ y
  10. int main()% [+ l  b5 N& d7 i3 l3 f& |9 \
  11. {
    9 `/ u4 o4 ?9 P  q
  12. int sockfd;& I  Y- W; {: T+ m# z
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))6 ]4 i1 u1 c8 U; G" R) F
  14.         {
    , ]) M" o  |  c9 U+ D- Y6 K
  15.                 perror("socket");, l, y; K* D7 I8 \3 H+ b
  16.                 return -1;3 T8 [' R, o8 I& R4 _$ L
  17.         }
    ! B. r% i5 |" e! Y  S- A% q& _
  18.         printf("socket...........4 Z% O( I5 K, r3 h  w$ u( {' s" M. e& P
  19. ");- M- X3 R* N2 I2 ], E3 o1 B
  20.         
    . O4 `; s% r& Y
  21.         struct sockaddr_in srv_addr;
    5 U: ^5 p# G* i
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    % K0 m3 j4 A& G$ B  c, m- @: r
  23.         srv_addr.sin_family                 = AF_INET;. l& N9 o0 P. W
  24.         srv_addr.sin_port                         = htons(8888);4 V! f1 n+ k, Y9 P
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");: c' F9 H1 v) L: q+ _
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    7 H2 {# h( }( r0 H
  27.         {
    ( [; z  S/ y7 a# n, a; W' c1 {
  28.                 perror("connect");# \1 ?+ A4 L: f5 V9 X
  29.                 return -1; //exit //pthread_exit
    + g' M) H: f# D8 R% z! ^- F% n7 L
  30.         }
    $ \1 Q$ ?7 H0 S* v! Y' B1 M
  31.         printf("connect..............
    ) B' ?! x% B& u/ C/ ^7 I# {
  32. ");+ o9 }) h' P8 ]# @8 v
  33.         char buf[100];) V# h+ Q+ x7 w, p0 F; R
  34.         int ret;0 B- ~* l; m1 h  y7 D" v7 ^
  35.         while (1)
    ! C  C/ `  T  @5 V+ l: l
  36.         {5 x# G$ C$ S* W% J
  37.                 printf("send: ");
    " H+ C) Z; U; @) Y6 r
  38.                 fgets(buf, sizeof(buf), stdin);& y; P8 E2 l- _2 x" [* @; N
  39.                 ret = write(sockfd, buf, sizeof(buf));
    2 D7 p! C( ]' f+ c% b( l
  40.                 if (ret < 0), u! Q6 ?$ h7 c7 i* K, S( z% F
  41.                 {
    - L& o6 _% g* h5 g/ f2 M. ^( l; {
  42.                         perror("write");7 L8 B: ]) T, V" R% p" N+ @6 U
  43.                         break;. c% r0 q' e/ `: F5 \+ s
  44.                 }
    , ^) I5 r0 y% A+ P$ k: {5 \
  45.                 if (strncmp(buf, "quit", 4) == 0)- r/ N9 z2 k" O* D& ~! z5 n# r) i
  46.                         break;
    1 v! g9 t6 T; {6 q1 d9 X
  47.         }& J) |1 j9 i4 B$ M
  48.         close(sockfd);+ h# z( }4 n8 N2 b: p
  49.         return 0;
    1 Z- y. @4 O; Q. p
  50. }
复制代码
9 ~( H4 z+ q* w3 }/ p

: d8 l% }% I" `
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 13:02 , Processed in 0.061300 second(s), 22 queries .

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