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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 17013|回复: 0

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

[复制链接]
发表于 2020-5-9 02:09:24 | 显示全部楼层 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。" [) r" g- M, Q; K& j

' W. I" r: e# K
0 |& J0 `. ?6 E
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。. }2 `3 t4 `, }# R- W7 I# p1 Q

& d, @8 i' r- l, i
9 y0 B. q+ k+ \, W6 I
TCP协议
2 u3 {2 ?; K8 k7 `) S, hTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 I) ^* s/ ~0 |( z/ B; o/ c6 m, i5 n9 @7 b) {* _- F5 \
; o# Z% h6 T/ t$ ~3 y6 J
关键词:三次握手,可靠,基于字节流。
% k% d' X+ C9 w2 Z
% ~7 H& L2 \7 d7 d: j
: l& H& p) _: `8 m& R
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
) ^! J; n' N8 N. A3 @, P" ] 微信截图_20200509015654.png 3 M) c+ @' T! t2 G( K; d5 N, g5 u1 ~
TCP服务器端和客户端的运行流程
9 g3 ~# b6 T* t# |' ?* `) X8 s6 e' S
% H7 w  w# g% q1 S: V; ]0 I
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?' w9 [: A4 h& T9 z% d* _( E
: c: M& i8 G' m, G
" l' U+ R. H* T. `& f
1.创建socket5 w* b2 ~$ k+ X  J, p! @
socket是一个结构体,被创建在内核中
6 W3 v; Z/ G4 n8 P0 N5 K sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议3 w) B' |- B4 L; i( S/ ~

, G: `$ m/ b7 ?, B4 A! v2 w

6 ~8 E1 C: {+ W+ l& M/ _$ \6 M2.调用bind函数: x9 E* W5 e/ l
将socket和地址(包括ip、port)绑定。( X# t% |$ [$ i: m
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
, k$ G0 M2 a7 }5 C2 N struct sockaddr_in myaddr; //地址结构体
& i) w6 ]3 j, w4 ?5 |  H  ~; Z5 \ bind函数
) z& {2 ?6 [" x bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
1 F  d+ \; i+ l4 v- Y6 @$ T/ U' p! W# s$ B7 A( ^

6 p- j  G6 h: t, x7 n/ b3.listen监听,将接收到的客户端连接放入队列
! z) k8 L6 [9 c listen(sockfd,8) //第二个参数是队列长度
0 `! F- Z+ \1 @/ S
! T9 R: _7 Y7 n; a

- y2 F' k8 |1 T: {4.调用accept函数,从队列获取请求,返回socket描 述符
/ C: w' O. N4 Y" q  如果无请求,将会阻塞,直到获得连接
5 J) u, M+ V3 `8 }  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
$ B6 `# F1 g2 A
+ s% n/ m" O8 D% L+ ^

2 u: B8 P# p0 a  N. P( p5.调用read/write进行双向通信# I# S+ m# G5 @+ T

4 n8 ^4 R7 E9 Q$ s, W# W
4 ]/ h- ?% Q( n3 [5 K4 G+ b
6.关闭accept返回的socket
$ S) W  S7 v3 u0 r8 ^! p2 {! J  close(scokfd);
5 C7 e1 B% S) G7 g- Y2 s
0 Q! P; ^  w( N* f
3 t9 h6 ?5 m9 U# D

. `& G' _5 H2 z9 g
, j0 g! \6 f- F0 {
下面放出完整代码
2 B9 a( D  ?% j- G! C+ T9 T' X' Z- H  k* @
  1. /*服务器*/* |9 }! \# Z' X! n+ p
  2. #include <stdio.h>
    # K7 l: G0 R, m( r( v4 l- }/ P2 a
  3. #include <string.h>6 m0 B3 ^# m) R7 o+ f6 I
  4. #include <stdlib.h>/ n0 ?5 ^3 b9 e- h
  5. #include <strings.h>- E: Y) K# M  i: B. a3 l; C' k3 O
  6. #include <sys/types.h>! o2 h7 L( C# w) n8 j
  7. #include <sys/socket.h>9 ?7 Y% r; I5 E
  8. #include <arpa/inet.h>, y9 t. W/ D5 \1 W& _% [
  9. #include <netinet/in.h>  j  f4 O' \$ B4 {
  10. int main()+ R! Q. [" h- Q7 _" k6 D/ m
  11. {+ V" l6 K( c0 Y+ ]
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字1 V- g$ L0 E+ E' G' @/ L/ }
  13.         if (sockfd < 0)6 N% t; B5 I5 @, j
  14.         {8 R: J" k" ]1 b8 D' r6 D
  15.                 perror("socket");
    + v* U' }/ A' v% A! U
  16.                 return -1;
    & Z; l+ c! R% |6 N& g
  17.         } //创建失败的错误处理
    , R, ^1 J# ?/ {9 Y
  18.          printf("socket............../ Q, n: W3 f) p$ c! G) b! {
  19. "); //成功则打印“socket。。。。”9 k, g3 Q; W7 c8 a& U  H1 N( @
  20.          + ]- j: l$ ]; Z6 [- g/ a
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    & W( |1 n6 I- Q8 A) ~
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)  z- t+ `) [9 Y! q
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型( j& J  G# \* v6 R2 h6 [) [5 m+ G
  24.          myaddr.sin_port                 = htons(8888); //选择端口号) M) _% d% b4 |7 w
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    1 q& p1 |" l* y
  26. - `" w& c  Y) T% K8 u1 J! D
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字) G; Y, a4 T$ t
  28.          {
    2 z9 ?4 h  T1 |  ~
  29.                  perror("bind");: o6 {2 p% K3 i
  30.                  return -1;9 G% m2 C5 ~3 [9 G: I! v9 D
  31.          }" o6 c8 s) @+ J6 \' e7 v
  32.          printf("bind..........& T0 s" x7 @5 j- u! c
  33. ");
    , k) C( L% [  t2 O4 r
  34. . K' F8 s1 y7 K+ s
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听5 M) m% |% `2 N1 i) L+ t
  36.          {
    ; V9 R) E$ M4 }' [9 B5 o: M% X
  37.                  perror("listen");
    - x, [  o$ K9 c1 ~* M& a
  38.                  return -1;1 C, D8 }1 l/ ]; h( o
  39.          }1 `3 o, |/ J$ W$ R# @
  40.          printf("listen............( x9 `& A9 \; h3 V
  41. ");
    4 I" Q- }( _3 ]7 G/ f1 x5 Z
  42.          
    ) \" i& b5 \3 v9 r3 z
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求3 Q: ~* i) t7 h& \7 K: I& j
  44.         if (connfd < 0)$ `$ p- W) p5 u! a5 @
  45.         {
    " v! f' C. n! |, K0 v# \
  46.                 perror("accept");4 d- N( X- l( a( ?' r: h! E3 j5 l9 Y
  47.                 return -1;5 p& G. z4 t) L( U3 {; E
  48.         }" |) B. t9 U# ^7 o* y5 _/ m+ k
  49.         printf("accept..............) M% t1 q+ x& [. @
  50. ");
    : U6 O) T& c8 q
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    8 ]5 [  t! \' ^/ k( n
  52.         int ret;/ Z# B& W/ T5 K, r  x, H
  53.         while (1)
    9 H$ I% L4 t2 }: S: k
  54.         {  l# B0 E- Y" B
  55.                 memset(buf, 0, sizeof(buf));
    8 G$ g' A/ G/ p  Z5 M
  56.                 ret = read(connfd, buf, sizeof(buf));
    $ m, o  V' w: g5 u8 v" c
  57.                 if (0 > ret)
    - Z5 f4 A7 L- p: B
  58.                 {4 @( B/ f* y8 b5 W# H
  59.                         perror("read");
    . S- V- J9 U* r% w: P
  60.                         break;
    : H& l6 }% s% O4 [
  61.                 }//执行while循环读取数据,当
    / h6 y0 ?7 M. [* k. w# X( F6 ?! K+ M
  62.                 else if (0 == ret)  P6 F; g+ K- C
  63.                 {
    3 w& @9 g  m2 i
  64.                         printf("write close!9 E$ X$ `, `" d0 k9 F6 U
  65. ");9 V2 o/ _9 s" B$ b; M/ B0 t( D
  66.                         break;
    ! H: I4 b. }8 ]
  67.                 }4 t. f2 U9 \9 h+ A7 f/ ]
  68.                 printf("recv: ");6 _; ~' S" A$ {7 |7 F  d
  69.                 fputs(buf, stdout);//打印接收到的数据
    5 |: _0 E3 q  Y: W3 ^( }# ]8 {
  70.         }
    ! J, D  U, V4 w  r3 d4 i
  71.         close(sockfd);//关闭套接字7 O) T- I2 D- v6 _
  72.         close(connfd);//断开连接/ M: M2 M5 n; |
  73.         return 0;$ n$ t. |" g8 e8 X! _
  74. }
复制代码
3 T6 _3 Q9 r1 u+ {1 k5 {
4 w; ~+ i) l- x* ~) s; V
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释), j6 A( u' O) X+ T4 |
  2. #include <stdio.h>
    0 V" g0 y1 b( Q) n" S
  3. #include <string.h>
    3 v& p) X/ e) C; ]  j
  4. #include <stdlib.h>0 E  A) C: Y7 [4 u! H/ g
  5. #include <strings.h>: n2 u. s6 Y4 o
  6. #include <sys/types.h>
    . v1 _% J2 `/ A
  7. #include <sys/socket.h>
    $ ]* j9 J0 z8 W6 V, J
  8. #include <netinet/in.h>
    * @" B; |8 Y4 S# y, N2 _& R
  9. #include <arpa/inet.h>
    * c9 e+ B, I8 f# Y
  10. int main()
    " c- q5 V* ^3 ^8 D% W' R# }( q, c
  11. {' P+ y5 a$ q( Y$ I. }! ], D; k0 i# J
  12. int sockfd;# E, Q; {+ X( @# d8 P. z: w9 {' |" _
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    6 Z! M5 s/ X3 t) z6 P! [: i
  14.         {& M3 y6 C  g- _' O
  15.                 perror("socket");: ?! A' v2 P2 f3 M
  16.                 return -1;
    7 s: H5 E5 R$ M4 g* l* z
  17.         }
    ' X- g/ O* I3 I$ l- S2 @
  18.         printf("socket...........
    2 ]0 I: x& Z6 f5 Q
  19. ");2 Q5 o! S9 @6 n% ~
  20.         ( w9 P9 |) G4 K: I# v
  21.         struct sockaddr_in srv_addr;8 U; B/ `# I: a" L$ y; T
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    # U" S$ q9 \7 P
  23.         srv_addr.sin_family                 = AF_INET;8 N% [* p+ s- y0 k9 f  S- k
  24.         srv_addr.sin_port                         = htons(8888);5 y, c' n5 D, p. E" l
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ! W7 r+ J/ U' Y' e# E
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    # B( S# r# G* R
  27.         {1 m6 S. [5 t  n% P4 Q0 {. N
  28.                 perror("connect");
    0 O" @/ K( S; @' P% v& C
  29.                 return -1; //exit //pthread_exit) u4 ]" |- N: S/ o1 d8 i
  30.         }
    ! L* U+ d- X5 g/ [) t2 l! K
  31.         printf("connect..............% w# Q, @9 d2 q" T
  32. ");
    3 W0 u; u; j; d" Z/ Z
  33.         char buf[100];( f7 C7 B6 N( R. }) C
  34.         int ret;  B9 [' v" ~- Y3 h( p( X/ a! R
  35.         while (1)
    1 l6 m) q! W" h2 V
  36.         {$ c! V9 T9 J$ A+ e
  37.                 printf("send: ");/ ]7 V1 D1 O9 r
  38.                 fgets(buf, sizeof(buf), stdin);8 i! ]9 ]4 S' ]0 s
  39.                 ret = write(sockfd, buf, sizeof(buf));
    0 M/ t. p! X) F/ S) H
  40.                 if (ret < 0)
    , g- A7 A! m6 a2 o7 T- s
  41.                 {( d; \8 H  a) e* C' \
  42.                         perror("write");
    ; d4 c0 [7 f- I
  43.                         break;
    + [! I! V2 U0 k3 Q' e8 k( P
  44.                 }; _7 M; S. H! W7 {& e7 e/ w
  45.                 if (strncmp(buf, "quit", 4) == 0)
    * A% ?# t+ T# l. d  _5 o! {
  46.                         break;
    4 {6 g( \- C6 B. Z$ |! n
  47.         }! l, q" k% g8 `: q/ H* @! w
  48.         close(sockfd);
    / C- s* Q* T9 f& L* |# o# z
  49.         return 0;
    - P3 Y) e' F) S5 }* J
  50. }
复制代码

' t# @* C! o6 x
% @& o$ {+ }1 A, C! V  W8 M: g
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-5-16 01:15 , Processed in 0.062759 second(s), 23 queries .

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