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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
7 v: U5 h% ~1 l. r2 G! Q2 A0 V0 a  U( h% k( k
( ^- i/ b3 e% r1 T+ c# {
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。5 O' {; n2 _, }5 @3 t5 q

( Z2 g+ T) d1 o: |4 Z( s( V

4 O* d4 x  H/ mTCP协议; j. t: O4 @( J9 {
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
! y) p8 a. [0 U/ k( y' y
4 z0 D$ {: k( n

% h0 Y4 n; s) k% N+ Q% f关键词:三次握手,可靠,基于字节流。
! q3 u# P% `# Z9 H* R. A  `" d2 S7 w! T  \/ m
) D- ~/ p7 G$ U
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。4 H+ n0 p1 a; G( t

1 w0 @, Q; n3 fTCP服务器端和客户端的运行流程
0 N/ F, b( M  Q. j. P- x* t. A: T* J# N' O
6 s6 l$ b2 Q3 o
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?8 y0 X9 o' }/ P4 r% D: ]( n

# `2 R- H" a# r9 l: ]

# r5 [9 `1 f: m) q  x1.创建socket5 }- M3 ], o2 D& Y
socket是一个结构体,被创建在内核中
( t0 [% Z9 T  T7 o/ D sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议; l" `, U1 _0 `
, K$ Z- D8 L* i2 z0 N

' L2 U4 c% K1 N2 N: B2.调用bind函数+ ?" ?7 L$ Q/ Z8 h7 J/ D% M
将socket和地址(包括ip、port)绑定。5 {& }2 }- k' k% t! i% r
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
& T1 F' E" O, J9 A9 Y1 n struct sockaddr_in myaddr; //地址结构体! c, s0 v4 v: \6 c
bind函数
/ l# x( t+ P% \0 g9 ^( N bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
4 B1 x2 W  x; c8 i0 O7 t. N. k" j, ~6 y1 c# ?0 [! g' q! F$ z
$ P: D0 |/ J+ D: _$ }7 Y, s9 A
3.listen监听,将接收到的客户端连接放入队列% D9 D: U4 i$ v# D2 R4 h
listen(sockfd,8) //第二个参数是队列长度
, t! E" e7 S  |2 V  u! r( j6 |: H8 o2 G, K: q& l
& q& K6 n+ @6 h( x
4.调用accept函数,从队列获取请求,返回socket描 述符
; o; x! B- S. _8 N* D  如果无请求,将会阻塞,直到获得连接
; v6 T7 Z4 {0 c" ]. |# m' J% h) C( E  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
4 j. ]; l0 g9 M9 n- \0 f. o
& X$ T4 {$ M2 Q; c% P2 ^/ E

4 y% [! a) `" m5.调用read/write进行双向通信. W- a* @. S: v$ O1 B

; p$ D, ~& e/ W" N/ I, N7 P
6 G9 f5 u% S/ v4 U# E8 u9 Q( @
6.关闭accept返回的socket8 r" M% L2 y1 _  g
  close(scokfd);& k" s0 B' S  y1 E7 m" E8 o

0 }- [3 R5 U5 O" Q6 O% ~
, `3 g* i3 V" V2 x' [- u

; {7 I& x4 G5 O' s

* b- }5 J* U+ r9 g下面放出完整代码
" c% @$ ^& t) s: p
) s3 Y5 t  k# d' N
  1. /*服务器*/
    : p; {. d4 f; e& ^1 {5 L& H: }
  2. #include <stdio.h>. a" H* b& T$ c8 h
  3. #include <string.h>3 X( P/ J9 w6 z* A2 v- B: S
  4. #include <stdlib.h>
    2 x: J5 H9 d7 T; @' ^" x
  5. #include <strings.h>' M9 L% g4 h9 K, ?0 k* [& h
  6. #include <sys/types.h>
    , }* J- i+ ?, ]8 o0 f5 b
  7. #include <sys/socket.h>
    / p$ r5 F! a& U# A/ F/ m* V
  8. #include <arpa/inet.h>; K/ t& L7 v. C% n
  9. #include <netinet/in.h>, Y5 E" x* v* O3 m" Q
  10. int main(). d% U9 W8 v! J2 F
  11. {% h& Z4 u) z3 `1 N
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字" x# z3 A3 Y) C. B+ A- V
  13.         if (sockfd < 0)
      O' o& Q: p" A7 P# y
  14.         {/ G3 }  {+ R: G0 E6 h
  15.                 perror("socket");
    ; e1 S% C9 j" p1 B9 y
  16.                 return -1;
    2 P7 }% }+ y# v+ l3 W2 m5 ~
  17.         } //创建失败的错误处理- s' a' I1 u. z6 d. c
  18.          printf("socket..............% q! v& n( y9 O4 \; ]* t
  19. "); //成功则打印“socket。。。。”6 {) z" e- G$ n
  20.          
    , P7 d* Y# u3 O# a  f
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体. M3 V  T! e# @+ x# f5 p) f
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)) @6 W  }, r2 q
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ! U4 S3 i# [0 N% _
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    " f! G! e3 T# p- ]& h) T
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
      Y0 X) e" z0 O6 L8 V0 D, B1 u
  26. 8 H; P* l1 P$ m
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    9 Y1 Q$ D, ^6 o) R+ X
  28.          {, _3 T3 o4 r0 v! q! v, C4 w
  29.                  perror("bind");: d5 h0 W# y4 @" S# `: b5 q( N
  30.                  return -1;
    4 b$ z/ ^$ G; ]; f) G9 i8 y
  31.          }3 Y1 D  T0 U' B/ F* c1 C; z: B
  32.          printf("bind.........." R1 |  r1 D7 M7 z) g- L
  33. ");
    ( e8 o. w1 V+ m* p+ z. F

  34. $ T$ b2 m+ f& c- z) m
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听/ y; L- q3 h$ m+ v3 z
  36.          {6 {% V  E' O+ l/ J: T% X$ ]
  37.                  perror("listen");0 [- B+ @0 D# j- H4 y9 M- U
  38.                  return -1;
    , o- C6 k% x8 c' t- T
  39.          }" `' J( Y( u+ k8 a# n6 s
  40.          printf("listen............
    7 G7 N$ r4 E- [2 u% {4 H% |  m, t
  41. ");
    8 B$ ]; ^+ j0 G8 o
  42.          
    + R' q- E& N. t* M& t! [
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    # \$ k- M5 w4 t# W* G
  44.         if (connfd < 0)
    : Q3 ^# H; ~% p8 B0 g/ D
  45.         {3 P! x* W$ s$ b2 o
  46.                 perror("accept");
    1 N- J: N$ a. G3 ]
  47.                 return -1;
    ; ~/ [6 Z0 S  `- z
  48.         }
    # e: \3 O7 H! Y' q4 g
  49.         printf("accept..............; {8 H$ H' L# ]' N+ @8 \
  50. ");
    - G: z; c0 s- r, n
  51.         char buf[100];//定义一个数组用来存储接收到的数据! y3 j2 C3 i; [* z
  52.         int ret;! E3 Q( D( }7 K8 l
  53.         while (1)
    5 U: r/ [9 |# f+ W
  54.         {
    9 b+ t+ \( x2 D, Y6 L
  55.                 memset(buf, 0, sizeof(buf));5 X! D, Z) r- q' G9 \2 W' K
  56.                 ret = read(connfd, buf, sizeof(buf));5 u# j2 U! ~/ t, v& {1 m$ {
  57.                 if (0 > ret)* r5 o7 @& \* b# e
  58.                 {3 R$ Z0 G$ I5 c$ g. [
  59.                         perror("read");+ ?2 H. i9 e( p3 A% ?8 [
  60.                         break;# k, F9 Z1 z( T  K3 P- o
  61.                 }//执行while循环读取数据,当
    7 F/ d6 i: s3 b! |, |! y
  62.                 else if (0 == ret)
    7 |* j4 i9 ^; C$ J  M
  63.                 {
    0 x' V2 N6 m" t( @9 @
  64.                         printf("write close!7 \; x4 @/ n' D7 {
  65. ");
    " o" i/ U) M9 L6 s% U: N
  66.                         break;
    . o+ X8 a( @* A4 o9 }
  67.                 }
    8 Q& Z' \& z8 N) `: I$ @
  68.                 printf("recv: ");; J! K* D: v% X9 f2 A+ T
  69.                 fputs(buf, stdout);//打印接收到的数据
    7 a1 n9 x1 e% v! B7 _" c5 i% G
  70.         }0 k3 N$ u4 a6 K. l  ^
  71.         close(sockfd);//关闭套接字
    ' o/ P' Z: @7 ]
  72.         close(connfd);//断开连接
    6 Z+ c' T4 M+ K7 f1 J$ \# c
  73.         return 0;  N' \! m7 I3 H) J/ m
  74. }
复制代码

; O8 W- e2 p# y: I0 h! Y6 F4 g; [$ M9 Q; K  t! o* y
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)! v( z0 i, [# d1 Z; M4 v1 c
  2. #include <stdio.h>
    # P  J- o, ?/ \. e8 [, V; X, U
  3. #include <string.h>
    ! t# g& N. l; l8 `' P8 w
  4. #include <stdlib.h>
    ( p& A  _3 A3 y' f/ O/ O
  5. #include <strings.h>
    , ^" r$ [* \' D) d8 F8 B
  6. #include <sys/types.h>$ g% s& E- z( t) X& k( K
  7. #include <sys/socket.h>+ S5 ]: m8 Z/ M) T4 W9 m$ {( B
  8. #include <netinet/in.h>
    6 q7 V' l2 Q* A5 h0 N: P
  9. #include <arpa/inet.h>: }, g2 t) ]: V, O5 J! z
  10. int main()
    " J, T7 q" ?, s# r# t
  11. {2 K  }; x: e9 S* V9 F
  12. int sockfd;
    ! C, Z5 z- R% P: ~' s7 D6 z
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ' R) L0 h+ e; ~& j. s( h
  14.         {
    % D& n% u5 X( s9 {/ h4 p6 L
  15.                 perror("socket");- w+ \8 t7 _8 ^7 Q2 C+ S# A" Q4 m
  16.                 return -1;& J; j7 b+ n/ d, x' u$ F* O+ e6 y
  17.         }: m" Q' U: N' Z2 K( {- g9 v) y
  18.         printf("socket...........
    0 K4 H5 `4 L! e% c5 V4 X& a' p
  19. ");
    7 X8 K  Z. o8 H* L: h1 X1 @
  20.         
    5 _( p; Z* W6 M3 r. [, ^% O1 U- C
  21.         struct sockaddr_in srv_addr;
    ! h% m9 w6 b" f
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    5 y% s- D( F7 `0 v) j& |
  23.         srv_addr.sin_family                 = AF_INET;
    ( f" H! l3 p# q! s8 O# z
  24.         srv_addr.sin_port                         = htons(8888);: t3 P+ R" Q/ I
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");" d' l) f( B: G9 K8 k8 `* c
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    1 w6 K" D& v3 c$ j9 s8 N
  27.         {3 i! J8 c! Q# O2 S( ~3 }$ {
  28.                 perror("connect");
    " R3 C/ _$ V. P
  29.                 return -1; //exit //pthread_exit* L! E5 |1 r7 z* y' E
  30.         }. c" v: |, E& ?2 w* W
  31.         printf("connect..............
    3 _; B/ w3 U& k, x! L0 j
  32. ");; ~  p8 y9 h/ g4 A' [  l
  33.         char buf[100];$ x8 h6 T/ R: S+ U2 N+ `! E3 y
  34.         int ret;. a4 Y& X$ H; z3 J/ D: d
  35.         while (1)
    + _# i' @8 F' L  f! V) k7 i: Z- `
  36.         {1 x, V+ w8 @+ O
  37.                 printf("send: ");
    , w# |7 _: G  z/ Z7 u
  38.                 fgets(buf, sizeof(buf), stdin);
    ! s) p5 J& n* y) i& o- V" h( U
  39.                 ret = write(sockfd, buf, sizeof(buf));8 f* d  M. G" y5 p/ {
  40.                 if (ret < 0)
    . |/ S3 R: s# s, R% \
  41.                 {' a8 W+ [* Z; R9 \% l% Q1 @
  42.                         perror("write");
    3 z4 @6 M4 C" c* \& W/ E
  43.                         break;& {* m! U) N% c5 h
  44.                 }8 l" E8 i8 f4 j4 @# p8 V8 J
  45.                 if (strncmp(buf, "quit", 4) == 0)
    1 k2 J1 k8 }8 z8 O" I( G$ p
  46.                         break;
    2 A0 V# j* I3 v- W8 ]
  47.         }( Z' @5 [# s( {1 k
  48.         close(sockfd);
    : f  B, b0 Q0 e
  49.         return 0;
    : y7 {. u, g' f8 @% R
  50. }
复制代码

( A% q# P; u; i" m1 P& G& z! i, D6 {" w+ D/ N8 O. v: t
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-7-27 15:41 , Processed in 0.133524 second(s), 24 queries .

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