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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

! t; ?/ Y2 R3 b2 a! \  h5 r/ E. @

0 d" v9 \1 ]1 f  B% R0 A' q& \socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。' d3 b6 C/ M& d, y5 L: c9 `  b4 v

7 {( d, u" c" f% q! F' ]4 L3 O

0 F3 o$ y3 }3 KTCP协议
& w0 z% `- y) pTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
$ ?+ ~3 g( q# P
! g$ j; P; W: g5 _. E. k  d

% s: A$ n7 J. L" T  L$ i/ v关键词:三次握手,可靠,基于字节流。
9 Y/ ]* F6 d9 j9 S% o3 a- o
/ j: V- M, ^4 x- T
$ y% h7 t" k6 N7 Y. z; y) J+ }
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。/ i  T) [( r" Z( g+ @  w
. I: B; _8 u7 r6 f& h
TCP服务器端和客户端的运行流程  S1 X6 h+ J* J  L

' ^4 u/ \! w. L& l- g

, K' e% W  T7 f如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
) c0 q. F6 Y( y
( I8 E" h6 V) Y' Q2 C( q2 V
) ]) Q5 T1 q2 ]$ p# s
1.创建socket# v. }, G9 z3 {
socket是一个结构体,被创建在内核中0 y7 m' L& ]0 L$ F. O
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议( J0 L- ~& ~2 M8 ]2 X
/ L4 H, q5 v* [6 d* Q) |; L4 q
5 j5 k* r! J0 }0 A' V! A
2.调用bind函数
8 G& E' U+ ^  k! d1 _: X! i+ u 将socket和地址(包括ip、port)绑定。
7 Y5 X2 Q- I# J7 u 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序0 B, x* _+ J' Q' h
struct sockaddr_in myaddr; //地址结构体( c5 {5 ]7 a$ |! O3 e
bind函数6 Q4 k5 V# g3 R
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))' Y( Y. @) \# ~

3 Q) \: J! F: x

+ }# f+ t# L0 J+ H3.listen监听,将接收到的客户端连接放入队列& `! `: h3 T# }3 w. a; v
listen(sockfd,8) //第二个参数是队列长度9 n4 Q4 d: r2 \2 s# b( m% N0 r: T

( J9 C$ W3 `: q6 {) F
0 {( S/ k  W4 l+ K5 @2 ~, m: \
4.调用accept函数,从队列获取请求,返回socket描 述符
& U8 c: v. f/ D) V( f" U" W" a+ [5 G  如果无请求,将会阻塞,直到获得连接
, P- [: J, G( m. x  Y5 C5 ~* w  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
6 [: M0 m! D6 @; H0 T: O- r. B" L( R* o
: Q- f4 O4 E# V& [& N1 m+ }, C

! P  Y+ \  ^1 Q! ^9 K; z5.调用read/write进行双向通信
* s/ W% t  E  t, p- s. g( C( k
0 U8 x, I' ]' V+ i+ @6 C9 h4 Q1 z: F
* x8 Z; j( ?. ]& s8 C8 v6 T
6.关闭accept返回的socket
: p7 C& q+ u+ f" `" s, V  close(scokfd);9 p- K; \6 g7 b! t

+ @# o+ v- o% I) M3 I

  c- |0 L0 o0 Q' q1 {. |8 v: Y0 s' _& |. S4 B8 L

# r; G" ^/ ~, g" b( a下面放出完整代码
; P, D; Y. T0 ?5 f
9 P$ x4 z0 O% C9 n
  1. /*服务器*// C5 C4 z! S9 D) M" a' ^
  2. #include <stdio.h>" b' v3 N8 ]7 A, G8 o; k! P6 K
  3. #include <string.h>8 z/ ]+ C" d' J. L- Z( v
  4. #include <stdlib.h>9 i( G4 o# d- x: x  f
  5. #include <strings.h>
    + r4 L: L8 P7 z( h1 l/ M7 M
  6. #include <sys/types.h>! H+ v0 b" i: V1 E, ~
  7. #include <sys/socket.h>
    6 q) i/ T+ s8 u
  8. #include <arpa/inet.h>
    $ U2 @# r. p* ?3 ]
  9. #include <netinet/in.h>
    - `! F9 Z& ~8 H( z7 C
  10. int main()
    0 R( W' M# {" v3 ~2 D
  11. {+ S& ^4 g6 _6 F( x3 @
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
      @) }' v1 W, j+ B4 j7 {
  13.         if (sockfd < 0)
    & g2 D2 ~0 y9 {: e! C7 R3 U6 J* E$ u5 M
  14.         {# g5 d/ m$ _* T5 _" _
  15.                 perror("socket");
    ! r- \3 L; w9 f, u' d& \
  16.                 return -1;
    : L6 S: U5 ~& o2 ^3 b) `+ \! Z
  17.         } //创建失败的错误处理( N# A! m7 L4 }
  18.          printf("socket..............+ Z# j* F/ c, B+ v$ ^
  19. "); //成功则打印“socket。。。。”3 t; ^$ e1 I' U% H. `) k' D: k
  20.          : |6 c. q0 E* |
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    3 M3 i* j+ X9 q; {2 F4 k6 k1 L/ a9 h
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)& \- D, l- H! \7 m$ E- f6 p+ g, G
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    - k* S8 S2 `5 U: I
  24.          myaddr.sin_port                 = htons(8888); //选择端口号+ }5 Y" E' E% R4 S  B
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    3 s0 `! \6 t4 i8 d. Y' a

  26. & W0 @1 B4 ~7 c+ R
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    / \/ {' l, o# x$ f5 W& x( ]" U, Z
  28.          {
    / J( a' y/ X; B! u$ |/ F
  29.                  perror("bind");
    3 a8 Q0 R1 Y* e4 b7 s) y
  30.                  return -1;( `, k$ k, b) C; K4 P. x
  31.          }
    2 M# t8 K$ A  f/ ]8 ^! Z* |, i  N
  32.          printf("bind..........
    % t" G4 F* w% {2 R- g
  33. ");
      b- T, Q$ Q- L( g  A# p$ U
  34. % q4 h& ^, q# j
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听3 ~3 P2 x$ G* P; X+ d3 c+ ^
  36.          {
    - _( d. r. w, @+ w6 Y& V. H
  37.                  perror("listen");7 n+ e" q' s" k$ Q) E. Q' t& A
  38.                  return -1;
    ; v5 r, q! I. m
  39.          }
    0 z% J# |& x; C5 C
  40.          printf("listen............( U' j& a' Y; t9 Y
  41. ");& T+ s! Q' O3 @$ H6 q  e
  42.          8 V% t$ ~: \! c. D$ {7 C' S
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    5 p. h' j  [9 Z% |6 n) v
  44.         if (connfd < 0)
    7 ]  M! U- a. T5 L* w2 C+ m
  45.         {
    - v9 P, ~. x( @" D/ F
  46.                 perror("accept");
    $ G' F7 ?$ L+ \8 p
  47.                 return -1;
    $ p8 O1 u& y) ^
  48.         }7 @/ e5 Q0 \; v9 _8 D: q9 D+ N
  49.         printf("accept..............& D; ?8 t; y# r# P6 J1 l: Q
  50. ");
    * w# t. \- T( V9 B( l
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    4 j$ O' E' [; ~7 A
  52.         int ret;
    ( W8 f) U& T. y: I* g, R! v
  53.         while (1)
    & j* ?7 r1 f' \/ f7 C# n, `
  54.         {
    8 k: F7 d/ g, ^6 V
  55.                 memset(buf, 0, sizeof(buf));
    ( ?" }+ r6 ~: m8 A
  56.                 ret = read(connfd, buf, sizeof(buf));
    + h- E( N# |+ \, V- {: u' t
  57.                 if (0 > ret)0 H9 i+ ]# n" v  t) A+ |
  58.                 {$ O' U; N& j6 _7 y$ o8 u
  59.                         perror("read");
    8 U' M8 q! g/ P+ l. ~3 w
  60.                         break;; k1 r0 O! I3 m
  61.                 }//执行while循环读取数据,当5 j! N$ @( U% a+ S- t4 N7 J4 R. }
  62.                 else if (0 == ret)' G% |/ P+ g" r% E6 U& q; n
  63.                 {' a. |7 e" {3 k/ Z; v; L
  64.                         printf("write close!; s) |! ]7 c  \: E$ p1 \( q2 J
  65. ");3 @2 Z) I0 ^! J, z7 L! Y
  66.                         break;
    + M8 @6 H: E2 U% [9 T6 F
  67.                 }
    # h! t$ Q' t8 ]6 x- X7 e: J
  68.                 printf("recv: ");
    8 m! y+ `7 Y# R' l, `, P
  69.                 fputs(buf, stdout);//打印接收到的数据4 x. t$ G5 L7 [; x6 J/ E8 y
  70.         }6 X' A- a% q' c1 R/ U
  71.         close(sockfd);//关闭套接字
    - v( D2 ?0 {( D) z9 W; E- _
  72.         close(connfd);//断开连接+ f( U$ h1 @& W7 l% ~* l% D
  73.         return 0;' q) Z( t: h  a4 x. o. ~+ ?6 q8 E
  74. }
复制代码
& }/ J6 i% W& h  t: j' D- t0 h4 e$ g$ E
7 e7 Z# w* p2 W9 r$ N% f, v# |( C
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    . H/ ^" M# `3 L) E/ t, l. Y2 O
  2. #include <stdio.h>
    - i$ Y5 U5 Z8 G4 [
  3. #include <string.h>+ Y* h7 R; X) A2 n0 K. R8 C/ C
  4. #include <stdlib.h>  x2 V9 C# W! ?: J6 l: n
  5. #include <strings.h>
    / E4 b9 {" Y: J) Z
  6. #include <sys/types.h>6 g+ d. [2 y* ^4 @! o4 q" T  d; i
  7. #include <sys/socket.h>+ ?  @+ Z+ E* }: @  N' c
  8. #include <netinet/in.h>2 B, p, M5 J- D  I/ Z
  9. #include <arpa/inet.h>
    4 n, Q- ]" d8 ]; b! T9 L4 [
  10. int main()
    + A9 k  z0 _+ @' c' u& [: b
  11. {% M- G7 F: U! e) O) p& h: H/ ]
  12. int sockfd;
    + X3 [, l) y% B' T2 }+ J
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    8 U1 `8 t" e3 B
  14.         {
    4 b) Q8 J/ J( x5 b  O" F
  15.                 perror("socket");
    9 J8 I. B3 W; C
  16.                 return -1;
    : B: D' g1 M, C' }7 y( l% x1 Q4 h
  17.         }5 s3 D2 L8 t8 V& w6 x
  18.         printf("socket...........
    - Z( l2 j; T" M6 Y$ T# a' m
  19. ");" O, S, F) J9 _% x( _3 W
  20.         ! J5 i. K( W* w+ x; u
  21.         struct sockaddr_in srv_addr;
    ( v6 E1 C1 J" K: o" E, O7 k8 J  v
  22.         memset(&srv_addr, 0, sizeof(srv_addr));) F$ y- T2 ?1 q( B2 M
  23.         srv_addr.sin_family                 = AF_INET;9 z: Z! h1 T  M% ]! {( [" s
  24.         srv_addr.sin_port                         = htons(8888);
    1 `: M; M0 z! H5 |* h
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");6 n: X+ Z0 C6 }6 U9 q( O
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))): y% Y3 v5 Z# e
  27.         {+ r0 G- u- R5 c
  28.                 perror("connect");  D6 a+ F# S0 d8 U
  29.                 return -1; //exit //pthread_exit
    7 q& D% p# `! L9 m$ B
  30.         }
    , Y9 A2 J/ U. h& K% X' D% J! o
  31.         printf("connect..............1 G7 w6 k3 |! R* X/ S% C
  32. ");
    , a9 k5 q- |; v( S+ F; ]9 y
  33.         char buf[100];# E/ q' c$ M; b
  34.         int ret;- i( }* y* b; ^5 j" c5 y. h
  35.         while (1)& H6 F! \& p8 S
  36.         {8 |! a# b) s4 R- H0 w: ]
  37.                 printf("send: ");
    ! w' V" c' J$ Z: H+ k+ k( Z
  38.                 fgets(buf, sizeof(buf), stdin);
    . P8 B  d. q. h# {' a, C6 u
  39.                 ret = write(sockfd, buf, sizeof(buf));2 g9 T7 h1 m7 w) k
  40.                 if (ret < 0)4 O+ A+ i$ I1 }8 X2 u
  41.                 {4 X$ b; x  T' f" y% s2 H+ V
  42.                         perror("write");7 q; t4 @" }/ D9 R
  43.                         break;
    5 m; S9 v$ H6 Q
  44.                 }6 c* |0 h% `6 R4 u5 D
  45.                 if (strncmp(buf, "quit", 4) == 0)
    # I) V! O( u! D9 T1 `2 e
  46.                         break;& A1 O& a0 _' y0 {6 w* O- t/ V
  47.         }7 }2 `0 P& C. k3 x6 w% O
  48.         close(sockfd);) w8 c  A- e$ y
  49.         return 0;1 n- K0 A* ?( z; M
  50. }
复制代码

. e9 ~7 V5 X/ z1 E* L3 O( `' |# K' w3 n) h& K
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-20 23:49 , Processed in 0.060774 second(s), 22 queries .

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