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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
3 q# ?! B- I$ I' D: o. E# m  o& a: d' F, M' P3 l/ X
# e! L, @2 }5 S/ Y5 M* D$ U+ d
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。5 h2 ~, |, x3 X$ I! F; t
: I6 n1 W" o+ @" f
/ @! O( k) y5 _: _8 V  z' z9 L$ _
TCP协议
7 x$ @  S0 q, T& D3 c9 {TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。' W% {5 @3 p3 x- D9 ~. d+ H
& C/ w. @5 b$ Y3 L# x1 X
2 F( R$ I, B7 r9 V
关键词:三次握手,可靠,基于字节流。* X! ^, g( l8 s- f! y

- v) _; `( B, s1 r/ m  P- P: G
5 P2 f: b1 ?, ~6 m" K8 t
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
3 i7 H6 {2 ~: q. w6 F
3 Y4 l0 d6 F* w- N7 s- Z  t& }) t- FTCP服务器端和客户端的运行流程, @8 x+ D4 x+ u' u# S
: S  Z+ U% s) V: ?7 Z! B
9 u$ c/ o  k9 k( v1 U* B! v
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?& K6 n6 ]* R; _; s: ^: `
; E! `) ~/ n3 N: L) O

3 M/ a0 e1 c8 o9 Z2 w6 L; c1.创建socket
* c  }: r# V. W$ B1 m. m socket是一个结构体,被创建在内核中: t6 B5 a2 s+ G- G  g- m$ {) E
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议7 N4 s. {- M4 @

) H, I5 _3 w  i4 X4 E- J
6 Y3 G" Y( K, f/ e$ _
2.调用bind函数
7 u' N2 J0 t' ]" m 将socket和地址(包括ip、port)绑定。
* W* Q8 t4 r: S% R5 o, m' V9 d 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
7 Y5 V+ g$ Z$ ?+ Z+ Y, X5 \1 X struct sockaddr_in myaddr; //地址结构体
, E+ v3 e+ P/ _/ d: W. F  R bind函数8 @& x6 t4 F" j" a6 b; a0 `- n
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 B- T8 O$ t6 Q! B9 _) q

3 s. @& R8 K( ^/ O  T
7 d$ K  s) Y3 j- u, q
3.listen监听,将接收到的客户端连接放入队列! l$ \4 @6 |, |
listen(sockfd,8) //第二个参数是队列长度
& a3 T5 B5 R. i. W$ @3 E# k6 X$ P5 G9 E: u
+ N) H. s4 D6 w8 ~$ t) y# d
4.调用accept函数,从队列获取请求,返回socket描 述符4 j! |: }9 k( }0 ^3 ^
  如果无请求,将会阻塞,直到获得连接5 @; i3 Y2 h' p0 J8 s/ y
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数8 m$ b5 g4 p( ?& P3 M+ o

0 Y: T+ ]2 y$ Y$ c' A/ \$ l$ W; S
/ O: Y7 c5 ^: C& z) k& m: o
5.调用read/write进行双向通信4 Y6 U: U. D  d' l9 X! y% t% Y6 a

0 R4 s2 }# R' e9 F0 Y
$ y+ X9 N2 q+ C: q6 _+ ~4 s
6.关闭accept返回的socket$ w1 q" j; }% N$ |5 N
  close(scokfd);* ~; x4 Y, _' y9 s! Z2 L
* [. ?( K; [) h, V3 y- q
1 b, X; L7 O# k7 B5 {. h) U0 i
2 b0 g5 E( _" g: p* u  e/ X% G* X* }% k. ^

# l! ^  c% k* j, M$ G  J( W; r下面放出完整代码
" A- S' {+ d, R& v# Q; L
+ s/ T& J7 v6 u" F' h
  1. /*服务器*/4 l& U; _1 e# {% u
  2. #include <stdio.h>
    % l4 `# G9 m& x  y  T- K
  3. #include <string.h>
    7 o! v1 Q1 f: O+ P8 F! |, U) G8 ^
  4. #include <stdlib.h>7 Y6 P8 T# B* I
  5. #include <strings.h>
    % P3 e" ?: V$ }8 v
  6. #include <sys/types.h>
    % O3 J% O) x4 d6 {/ M
  7. #include <sys/socket.h>1 X% S  T7 }$ ~2 g; ?! U/ m' a
  8. #include <arpa/inet.h>& b/ a* V+ O9 ~+ [2 C" \/ \' V6 L
  9. #include <netinet/in.h>
    2 a8 X! z4 O% o- B2 L
  10. int main(). h$ O8 B9 f4 n! E, }4 x, {
  11. {! Z# a( P& g8 U
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    , T7 c, R+ I, e$ D1 O( u0 j
  13.         if (sockfd < 0)
      n' l" e! y4 u' X$ \
  14.         {8 u! [2 {3 X$ c9 }* z5 I5 w
  15.                 perror("socket");3 c4 m. o9 P- b
  16.                 return -1;
    1 L/ [" E- V- S4 c) T
  17.         } //创建失败的错误处理  B* Y- R7 X) @1 q/ W$ j* v
  18.          printf("socket..............4 X3 k. |" v" [- ^2 F
  19. "); //成功则打印“socket。。。。”
    ' e% `+ g6 V( I( `+ a& z+ F  j
  20.          
    & \% i: \8 s3 ~* V/ f7 _1 b
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体9 D- t1 O: n' G$ S
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)9 m- z9 o& K' A* Y% Q
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型* z5 z: V/ e2 b: h" H, O8 J, B! s/ K
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    . ^" @% e: \, s
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    * V) y8 S- `) i' I, b$ p
  26. . N8 P* Z4 v" {) |7 q/ b, Z
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字2 ?9 A4 z$ q/ Q6 ?/ J( U
  28.          {$ V) s! _6 z# ]. ^
  29.                  perror("bind");
    8 v* Z  @5 d. T# n  D
  30.                  return -1;
    % G& R5 D4 n( P( p
  31.          }1 Q+ H/ X' \8 o2 k
  32.          printf("bind..........0 }5 P' f7 q2 ~! _/ y, `# ]
  33. ");
    8 o' s  i8 m0 G' [" M- Z
  34. % o3 m0 j( U3 d+ I+ O# U
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ; g* A+ j, d0 A4 \
  36.          {
    / y9 V( k* B3 l# h
  37.                  perror("listen");
    - y. g1 P' y; f- B: W$ G% I
  38.                  return -1;# ]1 m% j9 e) ?3 M$ U7 k! ^
  39.          }: _" N5 h- d; i8 d
  40.          printf("listen............1 p6 ^+ v' b8 l6 D: r  G: X0 v
  41. ");6 ]( X$ W; a; s0 u( B4 {" S
  42.          
    2 V( n3 _8 F/ `3 \4 v% A: T
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求  u# k$ N) |' [1 {0 a! S
  44.         if (connfd < 0)( k) p3 |2 F' N3 B! R! Z( {
  45.         {1 S2 D, Y/ Z: M; {( _, E
  46.                 perror("accept");: k3 P2 c$ S5 L. P
  47.                 return -1;  ?* H1 _8 b/ ~+ ]' e9 T+ _2 d
  48.         }
    / v( t7 w3 l7 l' z" `
  49.         printf("accept..............
    0 b0 P% i, o5 l1 |
  50. ");
    " }( y5 E% @  V
  51.         char buf[100];//定义一个数组用来存储接收到的数据6 @1 B5 R5 \( V7 Z$ Y
  52.         int ret;+ e, K/ Z( _( U0 |: a, ?: U8 q1 _
  53.         while (1)
    $ |! Y; i; W0 _3 N0 N1 k! I! l
  54.         {
    9 a  Q$ V& ^& U  v8 O# b& o
  55.                 memset(buf, 0, sizeof(buf));
    1 ?4 X* G" J* i9 Q: `0 K- J
  56.                 ret = read(connfd, buf, sizeof(buf));
    ; I" p$ b" {# ]: \4 e! I6 O/ S: Q
  57.                 if (0 > ret)
    ) q$ G" t0 u1 y% s% Y1 E$ o8 a$ a. K
  58.                 {
    2 b2 C5 k) y2 |% c% [
  59.                         perror("read");6 ]0 Q" p9 g" S. Q2 X
  60.                         break;6 Z5 `: n+ _2 d" R( V( O
  61.                 }//执行while循环读取数据,当3 k3 d3 F# u; v# w; w
  62.                 else if (0 == ret)* i0 |! b3 D! P, B% r! G( Z
  63.                 {
    + G3 z6 [) w9 |- w0 B
  64.                         printf("write close!
    * g& }$ B' N. c: x# @9 C1 n5 g: f. E
  65. ");
    9 U3 l. W* K7 I$ H" K" Y
  66.                         break;. d, p8 @& m  F$ L
  67.                 }# R, A. J# k6 C3 Q/ G8 S5 u
  68.                 printf("recv: ");
    $ ~9 g/ N' N) k# K2 F
  69.                 fputs(buf, stdout);//打印接收到的数据
    % X' I) e! X0 Z: t+ r' u6 R
  70.         }! Z" K; _* D" [8 F
  71.         close(sockfd);//关闭套接字4 I' X, j- X2 Q* t7 E
  72.         close(connfd);//断开连接/ x& |9 u& J3 e* s( E
  73.         return 0;
    / _5 ~% l, @- B- P: G8 m/ x8 [
  74. }
复制代码

' ]0 i) q9 I" E: ]- H3 j! k2 z/ p, T: j9 ~
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释); X) b" ?1 m# i* z' @7 [
  2. #include <stdio.h>( S1 d; K  O+ H: Q
  3. #include <string.h>
    5 _# o) \$ J* o6 x% R8 i7 U: m
  4. #include <stdlib.h>% t8 g3 b0 u* x! A( E
  5. #include <strings.h>: K8 G2 P0 e( r- ~* r. w, p" ~
  6. #include <sys/types.h>  F# u. J/ A4 Z
  7. #include <sys/socket.h>
    # B5 Y( Y2 J  [- {0 x' g( t1 U
  8. #include <netinet/in.h>- J' X# a5 ]2 ~7 a( j8 ^* o
  9. #include <arpa/inet.h>
    0 a) v9 x  @7 W0 V" W
  10. int main()8 R2 ^" p' G# X5 V: ~1 q3 K
  11. {
    5 G% V5 ]5 @: {: ?! T5 H
  12. int sockfd;; z6 o2 K' n- l( P4 L0 n1 L
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    5 m) x. a7 {, _0 B* a
  14.         {
    ' c; T" m1 d. T' U
  15.                 perror("socket");
    . o0 N/ e4 I5 ?, m3 ?
  16.                 return -1;' B( A# q7 c, R, E
  17.         }
    ' c& [4 p1 d5 D
  18.         printf("socket...........
    8 h8 K/ R$ K; b
  19. ");: Q% H: G. M2 D6 l
  20.         
    & H( r7 _6 Z5 n  ?5 U  c- D
  21.         struct sockaddr_in srv_addr;: i5 t9 u1 K/ d2 {
  22.         memset(&srv_addr, 0, sizeof(srv_addr));" _0 O) n4 @' E' n
  23.         srv_addr.sin_family                 = AF_INET;
    * s: ?+ R2 Q4 z5 O; }6 M4 p/ X
  24.         srv_addr.sin_port                         = htons(8888);
    # R5 I$ x; m/ l4 U1 j
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");" Q% y2 v  f( s' o; x) `
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))$ d/ L$ E6 X% v8 D% X" ]
  27.         {
    , V+ q% |' ~3 x5 t. a" R
  28.                 perror("connect");9 U. c  j6 [+ m! Z8 T
  29.                 return -1; //exit //pthread_exit* t& \" Z$ ~: t8 U" V% [& S
  30.         }
    5 C* k# d& _4 q- U3 J! {
  31.         printf("connect..............8 ~  C+ w( ?: _2 O0 x3 ~
  32. ");9 t$ k1 S% Q+ J# A; r9 b% ~7 ^4 Z
  33.         char buf[100];
    ! J3 u5 j3 p8 g0 a' g
  34.         int ret;6 A8 w5 C6 J) }8 d  t
  35.         while (1)
    ! }7 B( k5 q5 ^; c
  36.         {
    3 W* |( T& S; E  Q) Z6 y7 |; F
  37.                 printf("send: ");
    . ]5 H$ w! A3 P! T3 [1 @
  38.                 fgets(buf, sizeof(buf), stdin);# B% b6 l9 k" t9 X8 w; {
  39.                 ret = write(sockfd, buf, sizeof(buf));. k. Y2 [! e$ V& x# g9 z) _4 Q
  40.                 if (ret < 0)
    0 G/ Z- A: y+ G0 p2 W1 h' M
  41.                 {
    $ c1 t# _( b( x; S. X
  42.                         perror("write");9 }8 {7 Y' G& Q/ [
  43.                         break;
    3 a0 i7 e* k# H' d/ d' k
  44.                 }& t, W" _4 P# U/ b0 R# |) S
  45.                 if (strncmp(buf, "quit", 4) == 0)8 q: I4 _% @  D7 E9 d4 x. P7 v
  46.                         break;
    ( D  }. p: g- r- t* k' E) h
  47.         }
    2 C3 i$ ]' D$ Z
  48.         close(sockfd);
    $ W. o" w: P! V4 H4 J; i/ |
  49.         return 0;. a$ j# I: k6 p$ |5 z" f& n& J) m
  50. }
复制代码
  u6 S0 h4 V4 }1 L3 W7 w1 t' ^

6 `7 K# P. n5 ]4 r1 V* A9 l. w
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-8 08:55 , Processed in 0.161152 second(s), 24 queries .

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