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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
" C4 O/ L0 o0 l% Y5 _! i& r
  j  g+ S/ w1 S/ Q9 `4 ?% n2 T' q* e
5 k5 _) _# Y. g
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。: a0 y8 B+ h7 E- a! |

9 h+ D$ B3 @8 p4 o

- }, a! |0 j2 U5 U4 FTCP协议
# ?) W  a; O. @# yTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
1 u- Q( y- E- |  b- P
3 g) q1 `% L7 k- x
, h0 a- C( Y% w8 X6 b* ]
关键词:三次握手,可靠,基于字节流。
0 L* @3 Y' O. e" Z6 y  }5 n6 p. o  L) \$ a1 T9 K

/ c4 L* x- O; C/ G1 @# {. G/ {5 Q可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
0 f/ }7 g  c+ X* j2 | & s5 C5 T3 u2 \7 B8 O* _9 d2 U
TCP服务器端和客户端的运行流程; {% m, q- c: z  x* ~1 f: b

2 h  @5 S8 ~% W; `5 v

. b6 u4 ^( r  J$ k如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?2 D. w0 m0 R* [* v: o9 \
0 q( p" b+ O) K4 \1 g1 N
# k, k# ^3 T: H; u( Y
1.创建socket9 Z' w* C6 F* c8 V
socket是一个结构体,被创建在内核中6 o" N5 w) ?& n# s  ?( E1 }3 e/ `$ E
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
2 z; b$ U) @4 A3 x% f
: l# u- `1 H: B

3 @4 r* a1 ~7 L+ O' U2.调用bind函数
6 I# @$ S" m6 j 将socket和地址(包括ip、port)绑定。
& r% r# A4 d& M 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序  z: k" |7 D9 r1 X- W! b
struct sockaddr_in myaddr; //地址结构体
2 W# i! a, m3 B* l& G bind函数/ t9 E3 W% k- B7 v9 x2 X2 L
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))0 Z2 X  Y+ `4 r
* b  X) o, j- z. d
9 X6 k  M) B1 x
3.listen监听,将接收到的客户端连接放入队列
; t0 i' i- D6 [- ^: F6 ` listen(sockfd,8) //第二个参数是队列长度7 M( U) x1 o3 V8 h% X1 w
8 E4 w! F* X) U: _
. W# B  D( N1 T$ D0 n  a
4.调用accept函数,从队列获取请求,返回socket描 述符
& j1 r. \2 t" B8 s9 h! Y  如果无请求,将会阻塞,直到获得连接
- Z  J, x7 E! m1 a  J9 c  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
  _: `: M7 _0 j- S+ i( @/ m
- B! V8 @" c. V6 S
4 [5 u+ B( C; h8 R, Q
5.调用read/write进行双向通信" Y( C* t7 b3 n, L9 t: G
9 ~" R, y4 W9 F$ Z2 o. B

3 _+ t: J7 R! ^& D; W6.关闭accept返回的socket
9 M0 U& t% Y' Z* h  close(scokfd);2 k' [  {4 W( Z( L, Z! I( |3 M

# C1 z& i4 d( F: E* X/ k* E2 O0 c) Q

- ^' \" p$ @: d$ k7 f" |
% J5 {8 E4 Z; p4 \% ]6 P8 a" ?" ~4 ^

3 f' Z! s, d& {3 z2 L: B+ G下面放出完整代码
, e# X% d/ P! w/ U/ |) C" ?4 I
) N5 J) z1 w+ }4 V
  1. /*服务器*/0 l0 q% J& P5 Z) Z- l. ]
  2. #include <stdio.h>
    0 Y3 }8 O: d; ^" t; d' \
  3. #include <string.h>; i$ @5 k4 G6 W, T3 Q, ?
  4. #include <stdlib.h>1 F2 I" F5 \3 j; U. l
  5. #include <strings.h>1 o6 d! l& z; ]* w2 S/ T( S
  6. #include <sys/types.h>  s4 y" M: Y& n) R7 B
  7. #include <sys/socket.h>
    2 X& f+ R* O) W* {9 z3 T$ f- Q  }# H
  8. #include <arpa/inet.h>0 z$ O2 L# ?4 k8 r
  9. #include <netinet/in.h>
    0 y* y, g, ]* b1 e% d" t
  10. int main()
    4 y. e9 Q3 C& _4 R
  11. {
    $ K9 ]) u3 ^4 q1 ?4 p
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字& y/ t" z: R5 ~* y. V) N( Q+ h
  13.         if (sockfd < 0)% c+ `0 J. a4 W, V8 r# I
  14.         {
    4 ~! ~$ u$ Y! e. ?
  15.                 perror("socket");
    8 c' b/ f. s, g0 }2 d
  16.                 return -1;
    : S: V+ x( n) w, W' `
  17.         } //创建失败的错误处理
    0 L6 I" @; E3 t9 W0 S* A+ {
  18.          printf("socket..............& b# ?' b2 I* t# }  d. h; f4 @
  19. "); //成功则打印“socket。。。。”2 L3 I. [& ~0 F4 ~
  20.            v# a7 e7 u3 P" w, S. q
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    + ~' e  c8 O. L( U: g; z
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)0 l( M5 V9 r" Y. h
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
      k. J$ G* V4 Q$ [
  24.          myaddr.sin_port                 = htons(8888); //选择端口号. r" c" F' Z  |! M5 q7 Z
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址; k' v  O; k& Q2 X7 z2 U0 I
  26. # D4 Y, K( M- T! D/ |
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字8 W5 w1 ^4 ^6 M! O
  28.          {
    + ]& W4 d. b- w5 F; E+ B, T
  29.                  perror("bind");! C- N0 [' \. x: y/ r
  30.                  return -1;+ s8 h$ q' _7 }: I
  31.          }
    ! a/ Z* G- `/ I
  32.          printf("bind..........
      G- ]' j( \; Y& Q" b3 n
  33. ");
    / G8 S% z% P/ k5 K$ a8 @* V
  34. ! D/ a( ]5 L: Y7 ~6 B
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    $ U  o4 \0 G9 N4 E
  36.          {
    * {' h1 K6 h% [5 _7 g6 {* }9 m7 @
  37.                  perror("listen");/ |4 N9 d. B1 @
  38.                  return -1;  ~6 C  P" ?8 S$ h/ k6 j6 D
  39.          }
    4 \* s2 e" j2 P3 n, t
  40.          printf("listen............# }/ z$ M1 U: j0 `) W. V4 q: ~' l
  41. ");
    % j  |* b' O+ E8 Q; C5 [$ p! _' x3 g. O
  42.          8 E3 y8 H, t, M. M/ O) y. \
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    " W; W$ `% V& ?- M
  44.         if (connfd < 0)
    / o  G' I* D+ A: `
  45.         {7 l9 e5 y  A" ?" @- t: F% ^
  46.                 perror("accept");( C# W1 D0 f* ?
  47.                 return -1;
    ( g' z8 @) q  M5 y$ R
  48.         }
    6 i8 l" v0 \  N8 J; c" M0 e9 E: _
  49.         printf("accept..............! s6 ^  u" X( B; G, P
  50. ");2 _) c) ^# Q& c# h* }
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    2 r3 _7 _- n6 Z( p; l- H3 N/ [
  52.         int ret;; R% Q8 q4 K1 Y* Q7 D0 `- j+ s
  53.         while (1)
    8 u8 g3 Q# O# X
  54.         {
    + ?8 h  y2 g3 k" M5 ]
  55.                 memset(buf, 0, sizeof(buf));, x, ?7 J- i+ ^$ P( d) d! s' j
  56.                 ret = read(connfd, buf, sizeof(buf));) l4 h9 r; Z# K0 n; N" G6 I3 j) M
  57.                 if (0 > ret)
    % H& P$ K% S( H( e& Q
  58.                 {( G: U! r# c8 ?. b) D9 }
  59.                         perror("read");" k: m8 u( w, n; M$ R# I# q4 {
  60.                         break;# |$ ]% i" e& z6 ~- X* M
  61.                 }//执行while循环读取数据,当
    6 f7 {2 k! f, j( m9 S3 O
  62.                 else if (0 == ret)
    ' @# G+ J8 i% _0 b4 I5 o5 h# K
  63.                 {
    # z! ]9 u% Z: X% c, R# q- K
  64.                         printf("write close!
    ! b6 a0 q+ a9 e8 F
  65. ");
    . R& X6 ^4 ?' Q2 n, w& B
  66.                         break;! M# ^3 M* A7 L. J. m- j2 l% V( o8 T3 y
  67.                 }7 `% m$ V( {8 X/ y
  68.                 printf("recv: ");& z6 H6 A7 J3 n
  69.                 fputs(buf, stdout);//打印接收到的数据
      J9 t* D/ P, |' }5 m7 Y0 e
  70.         }9 d9 s3 j. w: [% [
  71.         close(sockfd);//关闭套接字
    9 q# u; S% C, l* G* s2 _1 v- V; [
  72.         close(connfd);//断开连接
    + Q( E# j, i: g' }/ J2 I7 u
  73.         return 0;
    - e+ G2 E2 o$ n2 A' r/ E2 m
  74. }
复制代码
: |) z. V6 e- u' r; a7 @

/ Z  |+ `, ^' T- I  W9 U
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)5 F1 q8 b5 I& z3 t; w
  2. #include <stdio.h>
    9 w% p2 G( O2 N4 j& E. q) @( \
  3. #include <string.h>
    2 `/ d8 T* {! g3 P9 z
  4. #include <stdlib.h>& p/ v) F& S- ?/ ]% }# q
  5. #include <strings.h>* D$ l8 u- F0 V: w
  6. #include <sys/types.h>  I" H. K, Q) B' Y' b( ?+ \1 ?
  7. #include <sys/socket.h>
    $ j, r! r$ R4 X( p
  8. #include <netinet/in.h>
    ! G* n3 y5 V0 L/ i
  9. #include <arpa/inet.h>  z' y, Q3 w1 P2 A& N! h; B
  10. int main(). W8 J; X) P, T& h* q" r3 n/ @
  11. {8 ~" D0 b3 Z3 V" I8 J# g
  12. int sockfd;
    " h8 k& q# V! D+ S
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))9 @% i5 [& _( X( o% Z' }1 C" b
  14.         {8 K+ L% @3 W7 Z9 \, S5 D& d
  15.                 perror("socket");" s# k/ O* g. P/ C7 R0 X
  16.                 return -1;. e% H3 O5 C( k" }2 I! Q
  17.         }# E$ p* |! ]( B; L6 n* P- ?5 Z
  18.         printf("socket...........
    6 ?: R0 @; P+ c* P( E9 i2 `
  19. ");
      N9 _: W8 N) F3 L' |4 S
  20.         ( N/ g* T- X( Y+ H+ K4 w; j
  21.         struct sockaddr_in srv_addr;% |, \6 Q' z( W2 w
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    - p+ s- i+ s5 Q9 @8 v' \0 i
  23.         srv_addr.sin_family                 = AF_INET;: a" S+ b  `4 n8 N
  24.         srv_addr.sin_port                         = htons(8888);  l& K! I8 z. A& {# t8 C
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    . j& [# u. H! T7 K
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))5 i! W5 {1 J1 u- ~& e
  27.         {3 k. f, X6 @9 j( k! ?
  28.                 perror("connect");
    $ x; j8 [5 _% r9 S7 B. K
  29.                 return -1; //exit //pthread_exit
    0 J8 W$ O. {( G
  30.         }
    $ a/ ^4 A( h. |/ ^) [0 }: M$ r
  31.         printf("connect..............
    . p* P- t. l9 r
  32. ");
    7 c* A% d  a. [4 b6 o# l8 f7 e
  33.         char buf[100];" E, C& v3 z7 G3 P- e
  34.         int ret;# a' b8 V2 ^& A2 ]: P6 {# \: J
  35.         while (1)
    1 \0 a) ^, P7 i! {& c
  36.         {
      o! l% Y6 c* U3 }0 }' L9 F( a6 Q
  37.                 printf("send: ");
    , k. J* ^1 |; U+ v
  38.                 fgets(buf, sizeof(buf), stdin);
    7 `- `/ M% p+ S5 i) \7 h; e3 [8 \$ G$ ]
  39.                 ret = write(sockfd, buf, sizeof(buf));
    : A9 w# f: `: p9 w
  40.                 if (ret < 0)
    / ?. A; \6 F0 |
  41.                 {4 g' `: I4 s' [
  42.                         perror("write");
    / l8 D0 ~9 D/ i4 a8 n# N" }. W
  43.                         break;# O, B4 O( v* \! R
  44.                 }
    2 t6 W" L+ d4 F: m) Y
  45.                 if (strncmp(buf, "quit", 4) == 0)7 ]9 t' u/ P! l4 z' j
  46.                         break;
    * U; J& X! X2 Q0 R% @/ V3 k0 R
  47.         }
    0 S4 @, i# S1 F6 s& d  a# @; U7 Z
  48.         close(sockfd);$ O" Z( }+ s! a# |
  49.         return 0;
    0 S; R$ M+ e% ^! [4 _+ y
  50. }
复制代码
$ o0 C! S8 Z6 j( n7 R& F
$ g, d2 O, L6 b0 i. P, ?/ X' p& @- n
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-8-4 08:12 , Processed in 0.054972 second(s), 22 queries .

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