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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。( Q7 ^( a7 f( F# ?  q( y* ~
2 L5 K4 m% s7 @& B9 F
5 u( d  B' H8 T% [7 Y+ ^/ |6 |
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
) H8 ]5 e. J7 f/ o; b( W& Y6 B- j9 z& P. F5 y7 e/ x/ E/ c
! T; ?+ k( u7 X2 D, f8 s6 g
TCP协议' R5 C2 X! b- v( v- w
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
% S1 ]6 M0 r1 O5 p& a5 ^! l! X4 Y4 f' z/ t

4 ~% j1 z- C. i7 J2 Y关键词:三次握手,可靠,基于字节流。0 [  J. `9 L3 F0 t. L2 A
0 P5 a3 @; j& N+ i
) z! C2 [8 `+ Q7 ?6 Z- s
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。, j5 e% C, `3 Q  }

% ~, W+ k  }/ I6 A7 ITCP服务器端和客户端的运行流程/ ?+ H( q2 @) q* W
- t3 e" v9 C+ z& N! ~0 d
2 w& y- w# v. F! |2 C1 L( G
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
1 b3 v: [3 F  V" _( T. B" q' Q( z# n" Y: M7 X

4 O: m: l4 a! l) B4 B* n% o1.创建socket- H7 d8 p" j5 R: z3 b. a
socket是一个结构体,被创建在内核中
! A, C4 }3 \" L/ ^7 E sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
/ G3 @' G6 `# R  E" J1 I& w( V! o

1 D: r6 l0 w  j! v( E" E9 x2.调用bind函数
4 F% k' s4 g' ^  C. P& z 将socket和地址(包括ip、port)绑定。0 @8 o; G2 S6 K
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序! ~5 z5 k7 [; S# ^
struct sockaddr_in myaddr; //地址结构体
+ t: ]8 z6 w' ~. G' G3 w, l4 L% m' O bind函数8 H$ M7 h0 x5 U; S7 @5 i
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 B, C0 l  J* L
, n; [0 l% ~. |
* |' ?. o) \! x! x- K4 P0 H
3.listen监听,将接收到的客户端连接放入队列
7 A0 C' B' m! e0 I& v, D$ g listen(sockfd,8) //第二个参数是队列长度6 R# k8 p; W6 ?/ d

, F! R2 M1 p7 a8 Q8 J2 I

, ~7 |  R4 {9 n- y- {4 ]1 N4.调用accept函数,从队列获取请求,返回socket描 述符
4 d' H# T& t6 W: I% N' y. |  如果无请求,将会阻塞,直到获得连接
+ b4 B% P, u2 V* G; S) h% N' k  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
( a: B% D4 o9 T  B7 a1 k: Q  V( o1 @
7 C( K: J3 z) u" ^- O8 _
5.调用read/write进行双向通信
$ l' _1 q. M- X3 J" P' E: i: Y8 ^& E8 M5 c' o
- i* y* u( y# }3 ?
6.关闭accept返回的socket
' g/ G( \. t4 R+ e  close(scokfd);
8 ~. O4 g- {9 M' q$ M9 [1 {7 {- _) t0 K" e/ Z5 N0 Q4 J/ D

$ p. U; r$ G: P( R8 R9 W3 V! u- B2 t' F6 d/ p

, x- Z# s; t% Q  v下面放出完整代码" n+ \" d7 L% |1 A/ e0 V
; }6 y# W' `+ m$ M+ w; U/ c3 T
  1. /*服务器*/
    * L/ m* K, x, F' Y, o
  2. #include <stdio.h>
    & o+ h3 \9 O, F- T
  3. #include <string.h>
    5 w: Y6 d# x7 j2 ]" C
  4. #include <stdlib.h>7 T6 a- O: g  a& D; v
  5. #include <strings.h>" J( W! e1 o" I( T- m
  6. #include <sys/types.h>
    . Z- N' k3 \" Y+ M2 ?5 N9 C
  7. #include <sys/socket.h>
    ' Y% K. i0 @; Y5 a& P0 u3 u0 v; |$ _
  8. #include <arpa/inet.h>
    0 o) J' Q5 W+ B* Z8 G% P
  9. #include <netinet/in.h>, c) h1 U" V: d% V& j* m7 u
  10. int main()8 r1 W7 K2 q# G6 b
  11. {
    : H* z; k. Y; I/ T
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    3 P( ^" x7 |# X: L0 h2 n" G; x+ R0 ~
  13.         if (sockfd < 0)- l8 S+ y5 X7 C& ^
  14.         {  q! m7 m, w2 ?+ `8 L: ?: ^/ k
  15.                 perror("socket");
    ; I0 G: z8 E+ k! {( i- S8 U
  16.                 return -1;6 A2 L9 _* n% [2 @9 H" g& W
  17.         } //创建失败的错误处理% z9 u9 M6 v: y9 w2 d% }7 Y
  18.          printf("socket..............% A3 j- K+ ~  O- _
  19. "); //成功则打印“socket。。。。”
    7 F" O7 a8 ]0 a$ E
  20.          
    / m# @0 H! p9 L' \# o& Q; x
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    4 ^+ W3 y; Z6 g
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    - X) A* b% }+ ]$ T: r
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型* b/ K3 o& X$ Q6 f4 g7 n  q
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ( ^8 R  Z- u. ~) F- `- l
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址& J# y2 `" w$ K3 r5 b: y: }! q- g
  26. $ D6 ?9 ]) j, x6 L
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
      q0 R# R6 `8 H) D3 B0 `
  28.          {
    0 q; e" G6 H5 g. H( x4 @
  29.                  perror("bind");
    8 {$ u) p1 s5 |+ c- S
  30.                  return -1;+ }. {, n5 r. {1 R7 T; \9 H
  31.          }
    . z" W5 {2 K( }. G* s+ {% r" ]$ A
  32.          printf("bind..........
    ) s  F' y% h! ?0 A1 u1 L' ]  `3 w
  33. ");. e% Y/ X9 l8 ~( X% |" L5 w

  34. 8 Z' U9 j2 ~' [( Y( ^; S$ a
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    5 V: V# h, V" U! r. v: M7 P( M
  36.          {- @) @; S8 Z+ x  c$ {' J5 j# v* z! J
  37.                  perror("listen");
    0 k8 x; \( D. d- H. H0 J7 _5 K% K* O
  38.                  return -1;
    / \% n: r" @* i  ?( w! p- \
  39.          }
    ; C* t' J) J3 @6 J9 W  O7 Q8 \# X  l
  40.          printf("listen............4 J# `0 A! B5 R$ c
  41. ");1 [7 Q# ~: G% r2 E9 [1 I
  42.          & G6 c6 w' S6 a( t7 D9 H
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求  }0 c& K' }: h: H! E
  44.         if (connfd < 0)! l7 g4 U6 c) _5 |/ P0 J) I) t5 v% l
  45.         {
    9 N, l# W9 d# ~
  46.                 perror("accept");
    ) q0 U1 x" v' V6 V# {& D# p6 J
  47.                 return -1;
    : v6 i  [7 F; G" e6 ]
  48.         }
    0 e, |- i% x1 D/ E! M
  49.         printf("accept..............
    3 T9 D& j2 K, r
  50. ");* C% X$ S+ r. V8 I1 F  P0 N; f; L
  51.         char buf[100];//定义一个数组用来存储接收到的数据- Z2 }- z' H3 L4 o
  52.         int ret;' D' O, x7 u2 D. P1 h
  53.         while (1)( y% R" M  M" j& h! J* {1 `8 ?
  54.         {  E  \3 ^4 ?: b/ u! Q
  55.                 memset(buf, 0, sizeof(buf));* I" u% r4 P; L! T) i
  56.                 ret = read(connfd, buf, sizeof(buf));
    2 U" D: q3 Z( [# W
  57.                 if (0 > ret)
      O! _0 B& V7 i0 e
  58.                 {
    , @" x0 C  E  `0 _; _& }  {+ l
  59.                         perror("read");& q  \+ T* t2 \' n1 U! {5 }. B
  60.                         break;* W0 I" a% D# J; W0 X
  61.                 }//执行while循环读取数据,当9 @) h' I; t* v$ f. B
  62.                 else if (0 == ret)
    4 @) j4 i& |5 b: k3 T5 d
  63.                 {
    7 W: l6 s" l: }
  64.                         printf("write close!
    3 X2 j# ^" i3 y7 k
  65. ");4 i9 [  @: U' b. f
  66.                         break;
    2 G9 O( x; N1 I0 a3 ^/ }
  67.                 }
    / A! M7 L5 h/ T% m3 r# s) I; @& B
  68.                 printf("recv: ");/ F9 _: m$ g$ T) c& W/ s! j5 c
  69.                 fputs(buf, stdout);//打印接收到的数据3 X% {5 ]5 U) E: H4 p% }4 P# s) S. J
  70.         }, _" _3 \# Q* \9 q$ }+ [. X. J6 A
  71.         close(sockfd);//关闭套接字' k) P- u# N/ Y) f2 u& g" S/ u
  72.         close(connfd);//断开连接
    $ v) r; e0 s: `1 F
  73.         return 0;8 t& ~, n! s' J
  74. }
复制代码

2 y" `: I6 W# a* `! H
/ w+ y- C. M( n- i% C0 k
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    5 n: c6 G* j2 @, ?' K# f( t
  2. #include <stdio.h>4 m- r8 S( _. E
  3. #include <string.h>
    : U. K9 r& O- f
  4. #include <stdlib.h>% n6 s8 U5 {1 Y
  5. #include <strings.h>
    # i5 `( S5 O% \4 i+ R
  6. #include <sys/types.h>
    8 o6 H" a* h" E: t* ]1 O
  7. #include <sys/socket.h>
    9 x, t2 k" X1 k9 J4 \& |
  8. #include <netinet/in.h>
    + I$ w. y* Q" K) E5 w+ m
  9. #include <arpa/inet.h>
    6 M; v8 @% u$ S
  10. int main()- [7 I& N0 M/ u' P- s- v
  11. {/ t6 c7 Q) h+ L; o! E$ q, z5 d
  12. int sockfd;
    % e$ n. @& U( L+ w1 [' L$ m  w
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    - v1 L* I% m6 x9 b' H" s0 U
  14.         {
    ' R5 E. M: \! s* _  K9 R
  15.                 perror("socket");# I! W8 C3 C5 s$ L- D/ Y4 }: s
  16.                 return -1;
    ) M9 }9 o: b9 ^; j& y8 ]
  17.         }
    5 @  l1 _( Y$ F& ?) b9 l
  18.         printf("socket...........
    7 x, h. h" v" \' h6 i2 j; l
  19. ");4 \# a: B! `, ?  q) Y, b9 a5 I
  20.         
    % z' E+ a# ^, e" l# x
  21.         struct sockaddr_in srv_addr;& @# t* {1 c, A  t4 U
  22.         memset(&srv_addr, 0, sizeof(srv_addr));5 q# n" q) p7 h. L  X6 I
  23.         srv_addr.sin_family                 = AF_INET;" a! }# _5 |9 m% n9 A  h
  24.         srv_addr.sin_port                         = htons(8888);0 W% h" }- R' K2 q" E
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    7 |/ ~# L0 _2 x, |
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    8 J# D8 E2 ~- Z% b- h0 k
  27.         {
    ( |3 S: U( V' Z$ |5 [, r: n
  28.                 perror("connect");
    0 G! C& N: P# y$ R3 C
  29.                 return -1; //exit //pthread_exit2 B( R, K! T; S- Z
  30.         }
    3 v" s* Z" f* ^  \" d
  31.         printf("connect..............
    0 Q: W1 h# M+ ]+ g) i6 G
  32. ");$ [& Q! o! x/ ?8 M+ D  t
  33.         char buf[100];4 k! A- x1 x7 f; w. Z0 ^; q7 O* r
  34.         int ret;( C( ?6 @: G: Z* t; P
  35.         while (1)
    ! E& z; e1 G8 Q/ Z8 Y0 q" x6 i
  36.         {
    + C8 k2 q0 @& Q- y) c/ L
  37.                 printf("send: ");
    " M+ \: O; ^1 D: N
  38.                 fgets(buf, sizeof(buf), stdin);
    6 |9 p6 V5 Y2 x# b2 E3 U) Y
  39.                 ret = write(sockfd, buf, sizeof(buf));) V3 T* h/ K" }* @
  40.                 if (ret < 0)4 ]$ h) ?9 \5 \( u5 Z; j: _5 V
  41.                 {
    3 ?1 \5 I+ f1 B6 r! L
  42.                         perror("write");
    8 M/ H  |% K; E
  43.                         break;0 C- _1 P& }: F# }9 f
  44.                 }4 K! w) O4 z; }% j6 e! j
  45.                 if (strncmp(buf, "quit", 4) == 0)$ ?* q# O: q) m3 v8 q- c& O
  46.                         break;) l: n8 s6 t8 y* a/ m; Z. C
  47.         }
    , \- U& e4 B- I: I( O. v2 I) w
  48.         close(sockfd);: J: `' Q! Z) e- @2 e8 ?/ J2 C
  49.         return 0;9 T9 b+ _3 Y/ s! A) a, f
  50. }
复制代码

( o7 \! a) s% C4 Q# m. v! Y- K( ]5 L  t
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-17 16:42 , Processed in 0.170576 second(s), 24 queries .

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