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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

0 _6 \$ S3 j  v8 N3 |8 q( B  M, tsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
! I% V- B; r0 Q; j7 U4 t
- G# ?4 g+ k% E; X1 ~

. R0 Q* f  U, a  ^) LTCP协议
5 [0 {: f3 B. d5 Z2 w. z7 [TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。( v$ u& B) B" L1 G9 V) q

9 S/ {2 G( o' o% x. \( s, |1 C

9 E& C' @: k" S关键词:三次握手,可靠,基于字节流。7 D6 P( u& n" X

9 b% `$ {" p, A: M" Z
; W0 o* C# Z- E4 [: n
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
, n! |8 n( f+ g/ Y' l/ V  k * \6 }% K) L( k' v
TCP服务器端和客户端的运行流程" e- o' I, a$ E7 k& @4 V, `5 r, h& g

  I7 V5 a( I+ K+ Y
9 _4 _  ]* A+ h" X8 C
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
6 b. ^- w  A. }# m2 H
$ G2 V; u. y( U3 f/ Q# H$ W5 F3 _
, r- {5 u6 ~) L4 {
1.创建socket$ |) Q" B/ T9 q) F+ s0 K2 E6 c
socket是一个结构体,被创建在内核中* J, C. e! p2 `. l0 R
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议3 C3 j- U9 N4 Y+ I. i
' Q7 \) _8 l* q- A# Q# d  \: j' D
( Q/ F9 h+ F3 o1 s4 a+ X
2.调用bind函数
2 ]: B5 [- `' C5 R 将socket和地址(包括ip、port)绑定。, d* n; g7 N1 q0 B- ?. U
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序/ U- Z6 v6 r# H# N& T+ ^0 V
struct sockaddr_in myaddr; //地址结构体. _9 I$ W9 l" I7 P  L$ h$ @
bind函数! ^- C5 g8 Z( V0 b7 K- l( e
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))6 l) Y0 }" i. ?, A& d. G; T
! D. x1 k, F' ^$ n% W& _4 |
' L; p, o2 w; t
3.listen监听,将接收到的客户端连接放入队列. r  a: B* Y/ y1 W# Q
listen(sockfd,8) //第二个参数是队列长度+ x7 W4 k1 j) k1 v" G4 ^9 k5 U

; _; B3 `! H, ^+ U& b% v2 H2 g

0 ~1 S" R/ c1 S; |. J4.调用accept函数,从队列获取请求,返回socket描 述符5 g; d5 R3 |+ t# {
  如果无请求,将会阻塞,直到获得连接' m' G  J: }/ g: u/ l' c9 o( I
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
6 s# ]- @7 W. ]. L9 G5 A* E+ s2 F. f3 n; F; i( v9 c" ~

& @* B7 L% b; F! M8 H5.调用read/write进行双向通信; E' P& A$ f) o, d; s

* |. W6 C0 D5 h( d. b$ H: I

" p8 j3 n: B  J- y- a$ `6.关闭accept返回的socket) ~, L0 U5 M' `7 C3 [3 ?5 p: s
  close(scokfd);- [0 m: s, {) P

  [; O. m& Z$ j  ?4 D
+ c$ ~! ?' N, U' n2 l! q' `1 `
1 ~7 o! t  N/ W; A

: j% i8 H; w' j  g- a2 F下面放出完整代码) ?* n6 F  M0 D' t) X3 V( S& S

; `) P0 W. i( U9 Q7 J) L) [4 h6 V
  1. /*服务器*/1 z; c. ]! F# N
  2. #include <stdio.h>
    ' ^' U' B* P+ M4 z- @
  3. #include <string.h>8 h; \- V' b3 o4 B
  4. #include <stdlib.h>9 r  d7 E) A2 |% R" W( y
  5. #include <strings.h>
    ' b& v9 l$ I7 _% m
  6. #include <sys/types.h>
    * U, v, I7 l! f
  7. #include <sys/socket.h>0 c9 C5 H* a8 q# M( Z9 s
  8. #include <arpa/inet.h>
    . x% P8 Y/ l& l* F- `0 T
  9. #include <netinet/in.h>! ?; r9 Z" o2 r$ K6 F; K: M9 M
  10. int main()
    & ]1 j5 N. D/ c+ \3 I
  11. {3 p$ ~/ B2 h2 e, ^
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字+ Q2 j# K6 q! M7 i, I1 D
  13.         if (sockfd < 0); C5 ^5 W8 @% d$ p& x
  14.         {
    $ o5 C! ^5 g* ?
  15.                 perror("socket");( M$ N, B2 ]$ P7 k9 Q9 N+ y' }
  16.                 return -1;
    0 G$ f3 Z' S/ Z+ [/ V6 O; c5 V
  17.         } //创建失败的错误处理
    ; m( a7 y5 p& f. c3 L
  18.          printf("socket.............., n8 \( i; Y( r$ g. R
  19. "); //成功则打印“socket。。。。”
    0 m( V* m1 d: K( \; `! F1 k
  20.          6 B7 }8 X0 D, u* ~! `% ~, t" S9 _
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    # h( t, Y) a1 s0 V4 g! `/ k
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)( j* E+ O  Z, @
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型* S) e% B# d7 q+ ?  a2 Z
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    3 l! {' |+ q1 |; j+ j( h! R8 y$ ^+ C
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    : }8 q; C2 i  c# L5 A* R" J, Y
  26. $ }& [! M" w& x  R
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字8 c& b0 S. |- I) k+ e6 Z; U2 T/ Z( E
  28.          {, u/ `! b( a+ r3 E6 m' L
  29.                  perror("bind");
    2 [, w& ~9 q+ ]6 b5 M
  30.                  return -1;+ e- U3 p$ M. {+ h
  31.          }
    / b" v6 \; k- a8 j" W, H2 H, ?
  32.          printf("bind..........
    # d3 s: x$ z& m% n) N( @
  33. ");4 O, O0 a% \- f) g2 N! v

  34. - h& d2 I! O& {2 j$ Q( |
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听9 f' I% \% A  D$ u' s# O
  36.          {
    ' d! T6 d0 ]6 G% z1 y/ E
  37.                  perror("listen");/ E/ Q  _7 T: l8 Z* @5 \- U
  38.                  return -1;4 |5 d5 P& ]+ F! W" V9 n
  39.          }
    % X  a8 ]# ]; w- Z5 X  \7 L
  40.          printf("listen............% ?% D- R/ o! Z# f9 l
  41. ");9 S$ g& F' Q9 ^2 b$ N. b4 L1 f
  42.          
    ; [" N; w) r/ M' d+ s( k
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求' e- \& B4 P- q$ c1 e6 W- I
  44.         if (connfd < 0)  U: K& K$ O( S+ F' X
  45.         {
    9 ^- T8 C* u/ Z' |% l8 K
  46.                 perror("accept");
    ; O. Y/ R4 {' }5 O
  47.                 return -1;- |2 z% d. K/ t4 s; k: W8 d
  48.         }) |4 P7 f% y1 B0 G6 }
  49.         printf("accept..............
    2 Z9 T1 d) m; i0 l" _
  50. ");" n3 B  i2 U, q$ `
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    " v$ E  z$ s1 w% ~2 o
  52.         int ret;
    3 ^8 X) p8 n3 {  s4 y
  53.         while (1)
    5 B$ k  X- v9 @- a7 u/ f+ }
  54.         {9 M4 a  _1 E" r8 g4 o
  55.                 memset(buf, 0, sizeof(buf));
    & d& t6 N% a$ X5 R, O
  56.                 ret = read(connfd, buf, sizeof(buf));
    5 N1 _( i7 e# \& {  h5 ?& J/ |
  57.                 if (0 > ret)+ _: r. b1 b9 r* Q8 \
  58.                 {1 Z1 e& s. k# S# Y- }
  59.                         perror("read");
    1 O( _4 i, k% x  {" ~
  60.                         break;8 [, S" }6 }" L& w% p$ K
  61.                 }//执行while循环读取数据,当8 \: q8 i4 x3 k, F+ @. L
  62.                 else if (0 == ret), B. W0 v9 B7 `
  63.                 {1 K* z! {/ M& o  x/ b* ?
  64.                         printf("write close!
    ) A; ]) n) {+ W
  65. ");# m. S- {3 R% \, V- ~0 U
  66.                         break;$ o" O- T% q$ }
  67.                 }3 n7 V5 O' C- F" X, P( B4 Q
  68.                 printf("recv: ");; Z6 H( U" V* }
  69.                 fputs(buf, stdout);//打印接收到的数据
    5 ]8 B% b$ ~0 L2 w6 z7 _) v
  70.         }
    0 g$ r3 L0 j( c+ }& t
  71.         close(sockfd);//关闭套接字2 g1 R5 o. }5 N  K
  72.         close(connfd);//断开连接, a7 a6 V% y+ F- n) s* ~0 R8 K- ^$ K# ^
  73.         return 0;
    9 e- m. |" c4 @6 i5 C# w
  74. }
复制代码

) a5 b& ]8 O' @- U, F6 z2 W) ]: |* i( I# U
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释): P+ t% a5 v- P. M
  2. #include <stdio.h>1 [1 e$ t. B2 P# o( J
  3. #include <string.h>
    1 f& U7 H  F% w0 E
  4. #include <stdlib.h>  ~+ l) ?0 k5 E) z& @
  5. #include <strings.h>
    0 t5 j; Z) g5 [& n0 w, o
  6. #include <sys/types.h>
    4 r4 d% U5 u: E# |3 ?  q
  7. #include <sys/socket.h>
    $ G( W5 ^) Y! ?
  8. #include <netinet/in.h>
    ; S* A3 ]. G6 }. r
  9. #include <arpa/inet.h>
    ; W3 S2 f* r/ Z: b/ v6 [  S
  10. int main()" b+ F4 N  T9 L0 |: x
  11. {/ r$ b0 ]: j# ?5 P; J. Q
  12. int sockfd;
      h! b9 P% A* F# Y( I
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    - D2 [6 ^+ J% x) N. w7 q$ [: G
  14.         {* {" A, e( v- x) _. H
  15.                 perror("socket");
    ! r% c+ E6 m: `' p$ b1 h
  16.                 return -1;  c3 l/ ^4 [6 l. r$ V! t+ D- |
  17.         }! O7 A  `; _, }. z1 [
  18.         printf("socket.........../ L; _; R+ q- X* c5 \" z, ^
  19. ");
    % I6 P! ]$ E: [- s0 {; V- [4 U7 b( g
  20.         ) u* s+ N& q+ G  N. D' a6 t0 O! D
  21.         struct sockaddr_in srv_addr;
    4 O+ N1 S2 J! c) \9 X- i
  22.         memset(&srv_addr, 0, sizeof(srv_addr));1 V4 U) e# X+ E5 K+ Q1 C5 F( A
  23.         srv_addr.sin_family                 = AF_INET;) I2 B* s  q# _  {  G1 w
  24.         srv_addr.sin_port                         = htons(8888);. G& _, t" i2 U. A5 c+ ~7 ]2 _
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    3 c% r% m6 C3 o7 a" ^: Q
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    8 c. T' `. w) a4 ]1 k
  27.         {
    4 `; p3 _* c, J' U
  28.                 perror("connect");. t. R! ]7 F; f+ e1 ]- U
  29.                 return -1; //exit //pthread_exit
    6 }% D6 T# w- s8 c
  30.         }
    % o! a* L! p; r7 ?% S) Y+ |
  31.         printf("connect..............4 }  r5 e7 \* B
  32. ");) Q/ E+ r- m; G4 g8 R- `
  33.         char buf[100];: F/ i! a! v1 b0 Z) g
  34.         int ret;7 C9 j$ [$ I- G1 _4 d
  35.         while (1)1 D( T/ K; M% p! S
  36.         {
    % C% A6 ]- g5 }8 i/ h2 o, P1 i
  37.                 printf("send: ");
    $ d" \, X( D( H
  38.                 fgets(buf, sizeof(buf), stdin);
    6 S1 I( W! V' A
  39.                 ret = write(sockfd, buf, sizeof(buf));
    5 d5 s! u( t  w6 T( V& t
  40.                 if (ret < 0)
    8 {0 F0 t- e! g* _8 u% k8 S. o4 z( M4 Q
  41.                 {
    " D" v3 i. q# U2 R
  42.                         perror("write");
      Q! R  u8 ?) P7 h  @& p; Q
  43.                         break;5 k) t; \- E+ @9 W' ~6 {
  44.                 }9 Z  t- y, e6 f# p, J3 O' U
  45.                 if (strncmp(buf, "quit", 4) == 0)- G, ~' V$ D! D3 s7 J
  46.                         break;
      Z; S" W4 H4 @: Z9 p5 ?, e- U
  47.         }6 i" z* X' }4 Z* m3 ]% R' K' y
  48.         close(sockfd);
    ; E0 r% u+ s$ }  J- j* Z
  49.         return 0;. k  `0 v9 ?! \) H9 t
  50. }
复制代码
6 ?$ S( K  j" y1 Q3 H( j+ ^0 O
. L: P: g5 j$ ?, ^
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 15:35 , Processed in 0.166516 second(s), 23 queries .

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