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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

0 }5 g7 d# ]5 D7 t

; r' {" x' O" ?6 K9 rsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
6 L& q7 L. ]! Z! o/ l% a/ {0 n* Q9 ~3 r

3 o. t( D6 n5 W- tTCP协议
& k# j2 ]0 v, G. A/ f6 D; v& ZTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 w3 A% ~2 X' j4 |6 Y) D) H
9 w9 j6 y( \& L- c/ l/ S
& K# Z: A3 h8 b" \" B% w6 U
关键词:三次握手,可靠,基于字节流。
  i' b  G) X' ?: a0 A+ L/ P8 L4 l4 Q& o* Z: I% l
& T% E1 |0 F* g; ~) o& J
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。. I3 M. \, r- k7 n

: \- W! o7 Y. `* \5 J* |' TTCP服务器端和客户端的运行流程
0 O* A2 L& J( l# [; w  h8 ]" C1 Q* S+ ?( E
8 p6 c2 E3 ]0 E5 {  p/ R5 G
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
# W% f0 _+ c8 ^5 l2 k$ q; _/ M, X# q, R3 @; I9 ^% o& l/ h
3 D# y( @+ }# F# }, T& S" ]9 W
1.创建socket
' Q4 l$ I+ f$ x( e( J socket是一个结构体,被创建在内核中9 ?. S6 w$ Y2 U5 L
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议  l4 X/ h& p6 T" d6 S1 j8 R

9 E' q7 E- I/ ?0 `) m7 g0 D
- F. G% o5 O& S
2.调用bind函数+ R/ v, o  M; F
将socket和地址(包括ip、port)绑定。
5 P# m3 n+ Z6 \  v 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序9 L( f7 G# v4 q1 d) m
struct sockaddr_in myaddr; //地址结构体
% H( j* A5 D6 @6 V bind函数
' P  t( p! i) ]* g0 W4 S4 C bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))- x1 t6 n7 j- B/ G

# C( R; p- ^2 m9 L, F0 H3 {, O
9 x3 H* c( U7 I0 s2 e: A! V
3.listen监听,将接收到的客户端连接放入队列
5 K3 d7 ]8 \! H1 m1 a listen(sockfd,8) //第二个参数是队列长度! c( ]! k. W  M( f7 i
9 L! v, f; P+ X7 c) y5 |: c% G( G
% Y* @( a. E, {. ?
4.调用accept函数,从队列获取请求,返回socket描 述符
$ X2 E" C1 I5 U" b" _6 ]- ?- x  如果无请求,将会阻塞,直到获得连接
' ~9 d# a' Q' Q" |$ I9 `1 G  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数+ W+ V. R1 `0 Q7 f& z1 O9 ~
2 ^, N: d  G! M
+ X& U8 l' e/ t1 U
5.调用read/write进行双向通信
; d6 ~4 Q' y; d- }
. L: @6 t' d7 k

4 H& Y6 s% V' Y( G2 S( B6 V6.关闭accept返回的socket
# }0 x" y6 n; I6 C0 f$ L% f, s' E4 a  close(scokfd);
! `/ T+ o: I2 M+ U) q$ d+ a. F7 {4 t" K
4 U  g9 z  O, d/ M6 [

# j+ M1 [' {) i  q

& g2 P  f2 ?1 |下面放出完整代码
# A+ M' Q  n0 n) U
  |6 W/ y+ M0 E8 D- m7 E
  1. /*服务器*/
      R$ ]! w  R6 k7 E" Z* ^, d9 S
  2. #include <stdio.h>! S6 `1 q& R& V. p
  3. #include <string.h>
    % g. b+ v. ?5 U" b9 o2 Y5 l  X$ x/ ^
  4. #include <stdlib.h>3 z  g8 W. h  o8 G# l7 j6 `+ k" a
  5. #include <strings.h>7 E  M; O( ]3 [" T2 _8 K! }
  6. #include <sys/types.h>
    3 R1 P+ ]. C: U9 J; a1 ?
  7. #include <sys/socket.h>2 f" G$ ~+ y. }  M7 }; t! y+ ^
  8. #include <arpa/inet.h>0 i- _2 x# N8 A! m# ~
  9. #include <netinet/in.h>
    3 l( q7 V. N6 E+ b/ o# s
  10. int main()$ p+ J1 _3 t3 n# p9 f, C4 ?
  11. {: ~( T" P" r: U% Z
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    & a# \) G2 }  I* ~2 J6 Z
  13.         if (sockfd < 0)
    , E0 x4 Q9 Q% c- D: K) V
  14.         {" E4 h! x" g( J1 _. M
  15.                 perror("socket");: r. t& V- b; {  B; B9 t3 m3 ~6 a
  16.                 return -1;
    4 t+ g/ y) L: U. n% u# y" o
  17.         } //创建失败的错误处理
    , N+ ~7 I! j7 l. `
  18.          printf("socket..............
    ( G" H" V9 `9 p9 a7 B! L9 h, @/ w4 p
  19. "); //成功则打印“socket。。。。”
    3 G" {$ y/ b4 q9 Q0 D# [- h4 E- U
  20.          : U6 G( ?, X/ F  Q( A$ \" j
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体4 K" [  J. g; `: l% H5 M4 X$ f
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ! ]3 F+ W! P( ^' o
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    : k' a0 I$ `4 V/ y
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    3 g, ?" |6 T4 g6 c. Z; w/ R1 q
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址: l. Z. [, d8 L: s8 m2 C& g) |

  26. 4 A- P- @' ]3 Z( ~
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字2 t1 Z1 d3 k4 f. @6 {/ x
  28.          {9 V! U* w5 w6 m( X/ `9 i3 c
  29.                  perror("bind");$ M& @8 |3 o0 [/ P, G4 N
  30.                  return -1;
    ' S$ S  W8 v- `0 r
  31.          }
    * T4 ~, e1 ?) r
  32.          printf("bind..........
    9 p& U* P9 ]) I4 `
  33. ");, `* Z! y% Y* z- K9 F5 @* r) V0 ]
  34. 4 j7 I+ S( W, @$ R+ E
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    : }( \- l; I) ?( @" R
  36.          {
    * C4 M0 _* `7 R3 B* l5 A) p0 `! x
  37.                  perror("listen");
    3 h7 u& J) r* }2 H; V$ m8 K  S
  38.                  return -1;3 Z0 J$ i& q; U/ d% _1 S
  39.          }
    1 D4 S6 T% g  I1 r* n9 H
  40.          printf("listen............
    ) W* C+ i( V6 s$ W
  41. ");
    ; L, H& I/ @. ]. F
  42.          0 Y" \5 z8 N3 M( H: k
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    & E5 i9 ?9 Q, }0 {; d5 J$ i# E2 t/ h
  44.         if (connfd < 0)- k0 ~; m& @2 J6 p' m0 s0 k2 X
  45.         {
    5 _: Q- L# ^( g! A
  46.                 perror("accept");; X9 r$ V1 n- G
  47.                 return -1;
    8 L" ?$ G, q* t9 i' A
  48.         }
    # x( k1 W8 x$ U) F
  49.         printf("accept..............
    * c" a6 g' R" {' K+ f  @5 {+ N1 u
  50. ");/ W$ O6 b6 j6 y  K( S, Q8 B4 S) F
  51.         char buf[100];//定义一个数组用来存储接收到的数据/ Q& v' V* `: J
  52.         int ret;0 j' R: l1 L9 q: \* ^
  53.         while (1)8 ?* N" K/ D. p( _( H3 V
  54.         {+ T4 ]- W& Y5 U& {8 e9 Z  I
  55.                 memset(buf, 0, sizeof(buf));- H& j  z% [& D
  56.                 ret = read(connfd, buf, sizeof(buf));
    0 Q* T* p1 x- X" c7 @- `1 h
  57.                 if (0 > ret)
    ( w4 }5 _- B& }  g3 H
  58.                 {' i$ G4 E3 y! @% z: `7 N. {& o3 \
  59.                         perror("read");
    5 G; X7 l( j" x, l
  60.                         break;! V1 w% w; v) ^
  61.                 }//执行while循环读取数据,当
    + n, Y0 b9 W% C: R0 n1 J8 o
  62.                 else if (0 == ret)
    , Q3 @, n3 p- ]9 b
  63.                 {
      T; Z5 u. B6 Y% q9 G; y% E) x
  64.                         printf("write close!
    / T" b5 b. ]5 p* b) A8 y) x, k
  65. ");+ p. B+ A; j" J* D1 P8 s2 E' n6 B
  66.                         break;' r- V3 @; H; |* [6 M* O6 l1 M
  67.                 }2 r5 K; I6 c6 w: I
  68.                 printf("recv: ");
    4 Y7 v8 m- b* |5 A# z
  69.                 fputs(buf, stdout);//打印接收到的数据7 L  ~0 b; y4 R. Q! l. K3 \
  70.         }. A) `0 B7 S1 s) o# t# ?
  71.         close(sockfd);//关闭套接字! t/ m" t4 `& D
  72.         close(connfd);//断开连接
    , k. _' e& Y( {" d+ F
  73.         return 0;; U' h5 ?; k2 @) v1 v0 {3 B6 C
  74. }
复制代码

7 L1 |, m# c6 T& _6 n
+ Q* ~9 \- E. e, L! g- i6 o! O
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    3 u0 P/ \; q+ }/ i% Z
  2. #include <stdio.h>, o: b7 e9 {9 G; x8 G+ d( \
  3. #include <string.h>' r/ m5 i: V' U
  4. #include <stdlib.h>
    " H! j; _2 l! c% n9 x( Q% a
  5. #include <strings.h>
    " q6 U/ y4 R% C2 o
  6. #include <sys/types.h>
    0 }$ m$ ~4 [/ \( ]
  7. #include <sys/socket.h>
    : F: T4 U6 C9 u) Z. ~
  8. #include <netinet/in.h>3 t# z. a7 {, s" K$ H. l; {  h
  9. #include <arpa/inet.h>8 P' f, l3 v4 s( a9 m( W) i, p
  10. int main()
    & X# o2 R; E& m) Z( i4 _
  11. {
    / j7 E5 R8 e8 R' ~- M4 h( y
  12. int sockfd;. C: b+ E! q6 Q: ?- q- e
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))/ n3 o5 `1 {# t. M1 M
  14.         {0 r- w* c$ x0 \# q, s- m
  15.                 perror("socket");/ m& _9 w$ q0 E7 x: J
  16.                 return -1;
    7 Y2 p8 m7 }' ]# Y+ t/ i: c3 ^
  17.         }- k) P* |% |1 `1 I7 f7 M' a) K4 S
  18.         printf("socket...........
    : ^9 u- a" @% s  f/ U
  19. ");
    $ B9 [( m& Q0 w8 }, M9 s' L& b
  20.         
    9 P( U' d/ c1 B
  21.         struct sockaddr_in srv_addr;
    ) }! l* d2 w4 S( A  d* U( S+ j
  22.         memset(&srv_addr, 0, sizeof(srv_addr));2 p" D9 D4 A- k1 t/ a% e9 C+ m
  23.         srv_addr.sin_family                 = AF_INET;$ _' a$ H- l2 d- K# u  ^( d
  24.         srv_addr.sin_port                         = htons(8888);8 D& }) F* Z; p" y7 F
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");- L) ?2 U( c3 @* c
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ! c1 B5 e3 `; b8 i( L
  27.         {
    5 C# W, q: O% ^9 _
  28.                 perror("connect");/ y3 M7 X  G; g9 z  I
  29.                 return -1; //exit //pthread_exit) F/ x5 U/ e/ [  l: @3 s! h; D
  30.         }+ p8 [/ f4 v: R% C5 e0 W
  31.         printf("connect..............
    $ c  C3 j' a0 ]2 [. O
  32. ");) _" N' S/ G8 A! ?
  33.         char buf[100];
    , G2 F4 u6 p/ {( d
  34.         int ret;
    9 e7 `1 e0 n  N2 h# m! [9 C
  35.         while (1)
    : d+ H) i* ?) P7 L, ]% d, _
  36.         {
    2 B4 }) q! T. h5 q! s( r
  37.                 printf("send: ");7 T4 Q4 p7 ~* `- o
  38.                 fgets(buf, sizeof(buf), stdin);3 S" V% o  i& V& N
  39.                 ret = write(sockfd, buf, sizeof(buf));4 V2 {* c% e3 ?6 E
  40.                 if (ret < 0)# h' w" P  h0 J  z# V) C1 e2 b8 \# W
  41.                 {
    & ]; b& b2 ^" |- c7 c. A
  42.                         perror("write");+ F/ R+ R+ z& n* O/ Y
  43.                         break;$ F' Z. b. y9 G' {6 \0 T( i: |
  44.                 }
    - c% k3 x2 |- t1 @3 R3 J
  45.                 if (strncmp(buf, "quit", 4) == 0)
    9 U- E! ~3 y$ o
  46.                         break;
    % k; U7 w  e& O$ M0 \
  47.         }& R; `: ?* M7 Y- d
  48.         close(sockfd);
    0 \( T  _( ]  z# S6 H6 ]
  49.         return 0;
    ' p* U! N+ T4 b. v/ u
  50. }
复制代码
+ G% _, R$ w5 ^- |9 s

# h0 N0 G$ u6 j+ B4 D9 N$ `6 ?, H
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 16:19 , Processed in 0.055989 second(s), 23 queries .

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