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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

2 t/ y# X3 N4 z  Q( i: c- @

5 c3 D  e6 S! G. F& A9 F" zsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。' P/ y% U  X( V9 h& _
0 X/ t6 [' {7 c, Y4 [6 z
7 f1 b/ l& P5 ^8 W0 J8 F
TCP协议* {# |: @& L+ R: j
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
6 H5 I3 M8 |, ?+ c6 |2 j
$ L0 H  G* A6 H6 j; p$ H2 d$ W

7 i- Z5 K9 ^" P3 l关键词:三次握手,可靠,基于字节流。. Q! x0 p9 o7 C2 I3 G
, c& \: E5 x$ F+ @
8 R) G3 @% w6 E7 \
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
% i+ t" X+ {3 S 9 k8 v" s) f. j
TCP服务器端和客户端的运行流程5 {1 `, Y, V) n' I- ^9 J
5 l& V/ b) P, c: K. c* v( H
1 `% |5 @/ b- [9 ~
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?: P1 ^$ ]& ]4 y. o

* B, y6 ]; R  Z( Q. n  E
  i0 }2 D# s. r* Q4 i
1.创建socket( v7 ~) S3 q% @9 T6 `
socket是一个结构体,被创建在内核中
3 q0 S8 c5 i6 H sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
" ]- e$ S/ w) F: m& h, `
4 M7 d- k- F# c; b+ i0 \
7 Q# J6 _# _# _6 u6 B9 J, i
2.调用bind函数3 ], z) d/ L/ j2 W, A% X
将socket和地址(包括ip、port)绑定。
# |/ G) }3 S4 q' m 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
: ^! y% X2 g0 T" [- Q; i struct sockaddr_in myaddr; //地址结构体9 @, C6 S" \- c! s+ U" j
bind函数, _/ h) X2 O  _+ ?  S
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))5 v6 }0 @( X" `* r  \( K2 Q
5 _" o8 w5 p# Q3 M( S; P# ]! p
- B( S% {" ^) [5 u/ @& K
3.listen监听,将接收到的客户端连接放入队列
) E$ I+ |' v+ ~6 m6 } listen(sockfd,8) //第二个参数是队列长度4 D7 Q: a& s7 H( M; r. O/ U

1 R8 f% R6 q, ]* u) k/ a
, M3 |2 X  m: Q. K7 W
4.调用accept函数,从队列获取请求,返回socket描 述符
: U0 X$ O3 g. T( ]; g  如果无请求,将会阻塞,直到获得连接
9 ~" U8 A/ K+ R  f7 f" o% \  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
# r4 R. |$ W, j+ i) a) e* ^5 L4 [; f5 z) K- I4 ]

+ j7 |: s& x2 |) @8 @  v- h" V5.调用read/write进行双向通信
) N* `. G1 t/ y$ Y3 b# B
/ b$ s% m  m" T( c

! Q5 Y$ K3 h8 ]* y6.关闭accept返回的socket* ^" i4 T* o" R9 l/ F
  close(scokfd);$ i% y# D3 W3 d' i$ F" }0 c
- t6 o: ?; E0 e  i; i
4 a% k  L' ]0 B+ H
* k# [. X% B% u, u5 f; @
0 Z# j8 P5 [* H6 R; ]
下面放出完整代码
* x2 K% u( F3 l  [0 g) _3 p6 x* F+ a/ o
  1. /*服务器*/
    3 ]( D  f: m; |
  2. #include <stdio.h>9 D: u! F8 M9 `+ e, W+ K
  3. #include <string.h>4 u7 ~  c$ @% f% n
  4. #include <stdlib.h>! ?- z$ }% M6 X+ j" z
  5. #include <strings.h>) c. {. b0 O& X5 O& a
  6. #include <sys/types.h>: p+ t" T0 w1 Y: W
  7. #include <sys/socket.h>
      A' ?8 I5 I9 R* c, L/ d' ?+ e
  8. #include <arpa/inet.h>
    1 v- W4 F% k; x7 w
  9. #include <netinet/in.h>0 m8 u! z4 d& K: U$ ?
  10. int main()8 I: @# J% O4 v5 r
  11. {+ H4 ^; O# y) J7 q, h* F5 X
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字# L- P  }& @, ~3 T4 f! s: |% ]# f
  13.         if (sockfd < 0)
    3 G. o& e8 e, E, e& b- g
  14.         {- s; J" R  m# O2 O& X, b/ N3 s
  15.                 perror("socket");, `: P; g- i% b& J
  16.                 return -1;
    , ~, n1 J' @, r/ A6 u3 A/ |- H* m
  17.         } //创建失败的错误处理
    3 O8 _/ I: L9 m2 T# e0 l
  18.          printf("socket..............
    - l* G0 F8 {( |: i
  19. "); //成功则打印“socket。。。。”
    $ y1 V* v# A2 h( |* Y
  20.          
      _$ q+ U" I  J6 i$ D6 N
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    : V2 c, |9 x5 ]- D# [( f0 s2 a: I
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)4 M% m2 D2 d6 }, X* d- i
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    , I+ Z2 v, \" k( ?# e3 A5 X( T" [
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    8 @, g7 B* V( i& r/ {5 F+ f* z* N$ Z5 h
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ! b7 t  `/ }  t4 [
  26. 7 Q3 K" L: v2 T  Q
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字0 r7 I$ n5 a$ g" G1 G
  28.          {' r5 E; t; E- V! B
  29.                  perror("bind");7 a7 G# o* \0 h8 Q; I. B" ]) @
  30.                  return -1;5 L6 K1 D: c& G- `, e
  31.          }
    , h4 b3 q% F9 W) A" }! q8 w2 Q6 J# e
  32.          printf("bind..........
    ' F, X' B4 |7 A  y4 C
  33. ");
    $ k  o: H4 X. M0 |) K& u
  34. 7 D  X3 e1 m1 W, n" a
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听+ K. \! E$ Q) I+ R' _
  36.          {
    , b# Q# H5 T6 \8 D
  37.                  perror("listen");+ c3 e" M3 d& x& h
  38.                  return -1;
    : j3 r% e& m, W
  39.          }
    1 [7 \  X. P3 f
  40.          printf("listen............4 d5 i1 z" c  v; A( \
  41. ");
    8 w0 v: m4 }- w5 X+ `  M
  42.          
    ) l: v6 X' d) g5 e* H  `0 v9 ]
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求2 P+ }. J8 D0 j- }
  44.         if (connfd < 0): f3 F$ [0 X3 ]- D7 d1 W1 `) I
  45.         {+ Z' j: m. Z2 U6 w7 z2 Q; D/ T) D
  46.                 perror("accept");5 [+ L) P7 X* \# W9 O: h
  47.                 return -1;
    ! C- H) w# s1 U* [, p: ?+ T
  48.         }* s: W: z7 l1 I8 f) F( u5 L: ^
  49.         printf("accept.............." K8 d* Q# ^( A3 v  t/ k0 y
  50. ");
    7 {$ Z# v+ a& U0 w
  51.         char buf[100];//定义一个数组用来存储接收到的数据  V2 [  g7 y0 T+ B2 m
  52.         int ret;1 k' Y' K! C* x# j) g7 K1 w- {
  53.         while (1)9 p( X9 ^1 N. L
  54.         {( e% }6 e7 v1 Z1 o: M, m  ^5 a
  55.                 memset(buf, 0, sizeof(buf));
    $ z0 R& Q2 S1 Z) g) _9 L
  56.                 ret = read(connfd, buf, sizeof(buf));$ M6 }+ @+ r8 h2 Q7 R
  57.                 if (0 > ret)
    + E# q+ v' C- ~. C
  58.                 {
    / T. n% Y! ?+ R# h* N5 k0 u
  59.                         perror("read");' F/ P4 V  E7 @8 ?+ E  x$ l
  60.                         break;
    ( M3 N. y3 e! J$ J. z
  61.                 }//执行while循环读取数据,当7 O7 Y% S6 j; y( ~
  62.                 else if (0 == ret)& y+ p+ w5 K/ E
  63.                 {  h# B* ?; p1 U8 w. ?# Y2 c
  64.                         printf("write close!! w3 f1 X2 g( `9 v* }  \
  65. ");1 J2 R; K7 l6 Z! Y( R2 J
  66.                         break;4 w/ t0 o2 `& Q, h% S
  67.                 }
    & K/ l8 n; @: h! d! h
  68.                 printf("recv: ");  d' Y& J5 s# I' f2 |! a
  69.                 fputs(buf, stdout);//打印接收到的数据
    7 j. E/ Q5 H7 b+ ^" l2 |6 n  }, e* C7 v* U
  70.         }
    * _( j+ S1 @4 `3 h
  71.         close(sockfd);//关闭套接字5 B/ Q/ d4 h$ ]3 T4 a5 E. Q
  72.         close(connfd);//断开连接( q0 `% q( M0 w- T+ P" r" o5 w
  73.         return 0;1 Y+ ^0 x- _: r! b! `- K
  74. }
复制代码
) W6 V( o& @2 d, g

; ~0 u6 k: T5 x% |* k2 g! Z0 O+ D
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    3 q) y, y7 Q$ `  \4 |
  2. #include <stdio.h>4 L* Q9 r6 u' z. B( }" l5 u
  3. #include <string.h>& p8 Y1 b. J0 S0 q$ `
  4. #include <stdlib.h>
    ; r  L6 G4 r$ i6 P
  5. #include <strings.h>
    6 Y8 r: n$ M+ E; ~; ]
  6. #include <sys/types.h>. ^. c, q* p1 N; {- m- x
  7. #include <sys/socket.h>, T. r4 O# O4 B
  8. #include <netinet/in.h>% A9 b% M+ \/ |$ Y7 k+ y4 i
  9. #include <arpa/inet.h>
    8 `- O" ]8 w$ Y- {
  10. int main(). ]4 j8 g6 T" p7 ^
  11. {
    + m4 m3 ^/ l: O( F2 o
  12. int sockfd;
    & K7 D9 V8 W4 q- @, \
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))' {1 E8 v7 g8 q" |
  14.         {3 ~7 @- G* b- j. `' i% C- @  a1 @
  15.                 perror("socket");8 j/ l$ z. _* X3 J" j0 h/ e- o0 H5 p
  16.                 return -1;
    $ g7 X1 y8 D0 z/ O' W& T- _
  17.         }$ H1 l9 P8 K; _9 h* k; a1 }
  18.         printf("socket...........' n! A. k$ Z/ n5 S
  19. ");
    6 N* @2 [8 x+ O" T; ]
  20.         
    9 A2 F/ a0 E( T/ c$ f: T
  21.         struct sockaddr_in srv_addr;- m# x! C$ B; ^4 H7 A
  22.         memset(&srv_addr, 0, sizeof(srv_addr));! n. m/ S* k* Q
  23.         srv_addr.sin_family                 = AF_INET;4 a8 n: M3 K; w, H! E
  24.         srv_addr.sin_port                         = htons(8888);
    / O" a) x6 ]4 |5 T, l
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");# u" k, R3 d  w6 Y' t
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))- A. g3 N- A6 w4 y5 W4 W5 l
  27.         {& I5 o8 Z$ n; q" M# R/ o1 o+ u
  28.                 perror("connect");
    $ t0 ?( m5 g3 Y5 q& F; L- t
  29.                 return -1; //exit //pthread_exit6 k# W! \" ~' T7 s0 n9 t
  30.         }
    * B8 b) p1 j: E
  31.         printf("connect..............
    9 l1 x! ]& {4 r" @( |
  32. ");
    ; R( J0 I( @! y
  33.         char buf[100];. G  ]: G8 U- l9 o4 B0 _( m
  34.         int ret;' l6 r# X, h# ^0 Q# B6 l# u
  35.         while (1)
    ( ^; N# C2 m, x: H- w$ @
  36.         {
    : r8 n, e8 n5 a7 Y2 @
  37.                 printf("send: ");
    ) b' ^- l# n% O/ s  `
  38.                 fgets(buf, sizeof(buf), stdin);
    0 v5 S. Y- E2 G& W
  39.                 ret = write(sockfd, buf, sizeof(buf));
    9 |$ J$ m) n7 Y# J; r. k
  40.                 if (ret < 0)
    5 |* n  Q0 }1 K) {) p
  41.                 {" a. t: w6 |6 f  j( B: m% ]: q
  42.                         perror("write");3 ~' T, x, U4 Y, {- [
  43.                         break;
    & j3 ?9 J$ U) b1 }4 P" P
  44.                 }: R8 b2 _# R0 _" u, l0 x
  45.                 if (strncmp(buf, "quit", 4) == 0)0 G6 k5 q" y0 l  D
  46.                         break;
    7 b9 ]# h! C8 a" @/ n5 w6 X. H- r
  47.         }. S8 T# N0 c; c+ p
  48.         close(sockfd);: r% O' U+ ~5 y
  49.         return 0;
    9 g6 N  p2 X% @- x+ S: }; I, X
  50. }
复制代码

8 o6 I6 h' X9 [7 ]" g; l
- }( _; i  y( ~) H% {; J
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 09:48 , Processed in 0.063471 second(s), 22 queries .

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