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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。; W4 K" ?+ N; E& V! B4 {
% O2 b# z, O3 m3 V# ~; x9 D7 p
. R+ d$ J& Z5 U5 b
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。6 i! R, |$ x4 X% b; o4 q) R  Z2 W

4 \( n% m! O# a. R' K+ n7 K6 B
$ Y- u- c* S$ f9 e& b5 i
TCP协议
8 V6 F1 f4 m" MTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。! K( }2 P4 C8 B

4 t% m) U/ d# e" {3 e

% E: \" o1 n. p/ ]1 u3 G, s. M+ ]关键词:三次握手,可靠,基于字节流。
1 y* M; h! q% E) p% D. i$ n3 s% F1 Y; g# a0 ^+ v# y% y) o
1 a. c! c& f! s# r$ W
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
" M3 l( M% o/ z+ r* L" ?
0 N( ?3 ?9 ~+ E8 T- \" ?+ a' b3 kTCP服务器端和客户端的运行流程
" a4 R0 Z+ o1 x$ s
, L) ^' l& R; Y) S8 t+ o" A

  K8 c7 U3 c( z, D4 x如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?' h% E  R; e- h  S. ?# Y

! x" L7 O9 f! ]. q% z! E& {
/ }& E7 M+ j7 Y, W5 m) |3 o
1.创建socket2 F/ _9 Q; R! O5 F$ W
socket是一个结构体,被创建在内核中
8 N7 l0 ^6 L6 A/ ^5 x sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议1 P9 F9 M9 z- [! d, {

$ _2 R( W' m( P2 b. |: x! A
5 l) U% J  [, I/ n5 C
2.调用bind函数2 Q& N. \5 g9 F
将socket和地址(包括ip、port)绑定。
" a( K: G# |$ B; F9 d 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
  z' e+ Q( \3 y1 ~* r( M struct sockaddr_in myaddr; //地址结构体
. K: t3 a  H8 [" Z$ @/ e bind函数
, G9 y* H, n3 X  v( l5 M, c bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
, X3 {, A/ c  d8 z7 |5 O3 ]: ~: [" r6 C/ l

' R2 R' J% b& w* n4 n9 l3 e3.listen监听,将接收到的客户端连接放入队列8 m1 ~- h* ^% Q7 V
listen(sockfd,8) //第二个参数是队列长度. ]7 r. j" J7 _- l7 k4 d

% {5 m; |' s0 z: w, B& _0 u" y

+ `  V( C2 x+ D4.调用accept函数,从队列获取请求,返回socket描 述符
8 Q* ~; ^% s  y8 `! e/ u; L  如果无请求,将会阻塞,直到获得连接
3 E* }9 ~: X  _- z" L; C0 {  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数9 Q6 i) J0 h+ x. S" }5 q
4 ?) a7 g9 ]" |1 m

* Q5 |% s5 C; A( [( h5.调用read/write进行双向通信9 f- \9 a; H# `' E, u
, D- B0 H6 U3 y* c# |; S  L& z

! A  B0 |: V$ ^* z/ l# ?3 Z3 {' t6.关闭accept返回的socket
' C9 D4 e; Z4 ^, M/ ~: U  close(scokfd);
% ^5 v+ k( z9 R# h4 I. J  X- ?+ c9 b* T1 T% Q' E6 {

. N2 {- v" J& ~  f6 D8 z" g: g  [. Y, p0 {
1 l3 C. q1 M$ F) d
下面放出完整代码2 C7 v: t. S: t
1 }. I, T" y9 l3 \/ c
  1. /*服务器*/
    6 ]" e" u* r0 I& y
  2. #include <stdio.h>
    5 S- a3 [; X5 h+ ?
  3. #include <string.h>- G3 h; ~; C6 A
  4. #include <stdlib.h># @/ _3 N7 K" P- U
  5. #include <strings.h>
    ' e* `& _5 R5 V9 z$ T  C
  6. #include <sys/types.h>9 w' _3 @) C" a4 }& e
  7. #include <sys/socket.h>
    ; ^- ^9 z# v' ~7 [( e* B' \- K
  8. #include <arpa/inet.h>3 w7 F$ [2 E$ I: ^$ c0 d
  9. #include <netinet/in.h>
    - j% L* i0 c) [4 l/ O2 m9 z
  10. int main()5 e" t* `! h, {, \
  11. {
      ?& q1 z. i1 E4 \
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字- `- I7 J, I' l( c+ I  g2 A( g+ }
  13.         if (sockfd < 0)
    ( j4 M4 S( N" \' _: j+ q
  14.         {
    " G+ W5 B. T2 i1 H$ b- u. U
  15.                 perror("socket");! Y3 A. @4 d* a
  16.                 return -1;
    & a1 z5 N& p" X! [( A
  17.         } //创建失败的错误处理8 d+ c, m! y, u9 \
  18.          printf("socket..............) m( j; z3 w, [' N# w8 N
  19. "); //成功则打印“socket。。。。”; z- R) C- j: `
  20.          
    0 ?% x8 y* O5 o% V! o9 D
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    9 X9 n+ A( q0 n/ L/ K/ p0 h% n
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    5 M! R7 ?/ `& f$ X8 r! S/ t
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ! m; n" D/ S: o4 M3 X' ?
  24.          myaddr.sin_port                 = htons(8888); //选择端口号1 M+ w& i0 r. Q
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址1 L4 ]" u3 C9 K8 V7 X1 O2 o4 Q
  26. ( H: s- v  F0 ^5 a# |, M- u
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字, n8 z. N5 c) l
  28.          {- }7 p; O# B* Q3 R% O  f' \' b% A1 n
  29.                  perror("bind");. E1 u# V6 D' i0 s/ k- J! W2 b
  30.                  return -1;/ s  L, C3 D! Q5 [
  31.          }. f# D0 N0 t8 O( N
  32.          printf("bind..........1 b/ T( Y) f% H/ |2 }
  33. ");9 Z, l& I5 p" F5 A$ N
  34. ! k, G. p6 r4 K$ a6 a8 ?
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听7 L' U4 v2 {3 c2 P4 P6 g7 T: B
  36.          {
    & ~% X" H4 Y, n2 [9 m; k8 Y  S
  37.                  perror("listen");
    4 L! j8 h) ~3 r3 i  C
  38.                  return -1;" Y6 r5 N1 t# ]1 ?) A; W
  39.          }
    1 Z' U5 k1 C" N+ h
  40.          printf("listen............/ z8 ?6 K) ~' [4 b
  41. ");7 c; G+ ?# x# ?
  42.          ( k0 I6 B  u/ S( G
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ( ^& M9 X2 @- r$ U, C$ D
  44.         if (connfd < 0)7 ]  r5 X5 i* @- V
  45.         {
    3 ^9 S* ~7 k. t# `5 X* j8 ?" h
  46.                 perror("accept");
    8 P6 v3 h0 o2 Y" E; H' j
  47.                 return -1;
    & a4 T0 @* I; N9 ?5 |$ U  u
  48.         }$ y1 E2 @. a0 D2 }, o3 M: U# U
  49.         printf("accept..............8 V, ?# ^* n* ]8 y2 d& m
  50. ");! }. `2 Z* d% V6 m. y  y
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    9 G) U9 f! I0 |) M$ z0 w
  52.         int ret;8 p$ w. X) c& \  X  l$ V" i: [
  53.         while (1)
    4 G. E+ X9 o4 ~" |
  54.         {
    9 L. l2 J: ^$ f; L
  55.                 memset(buf, 0, sizeof(buf));
    + M0 P% _, `; o  ~
  56.                 ret = read(connfd, buf, sizeof(buf));
    2 O- x- q3 l# o- Z4 V
  57.                 if (0 > ret)
    ! r& C5 f% }6 A
  58.                 {
    ( G# d6 X2 s" |* O& Z* o9 g8 P
  59.                         perror("read");* O: g2 U3 L3 U$ [+ `4 p0 W# K* Y
  60.                         break;7 R" w# V2 t. R
  61.                 }//执行while循环读取数据,当6 ~, {# m* ^9 w2 A& x# K3 g$ {
  62.                 else if (0 == ret)" W& Q& f/ [% W; I# o! y; V
  63.                 {1 ]( c" ?% l  z5 ]) R
  64.                         printf("write close!
    ) b# i* a: O4 d7 i  H% B) u3 y. m
  65. ");
    8 S) ]4 |- w. k9 J
  66.                         break;
    5 N/ Y' A3 }; t$ P
  67.                 }
    ) ]' W9 h* T% Z, I" }- J
  68.                 printf("recv: ");. w3 }5 n) I$ e; H$ d9 s; Z
  69.                 fputs(buf, stdout);//打印接收到的数据
    7 O! @' q- q" M1 ^" J& C0 T" P
  70.         }7 P0 c  V: T) L( u) S  Q* o
  71.         close(sockfd);//关闭套接字) J) i* d7 [  b* `) a1 m
  72.         close(connfd);//断开连接
    ; u: Q; g7 X9 B$ g* x
  73.         return 0;5 s9 S8 u/ R# f3 {. r
  74. }
复制代码

  o- Z' b+ ]  ?% c. F: P3 T7 f. g$ o+ k! L7 @
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    ! [, r, e8 n0 L, N& u. D
  2. #include <stdio.h>
    7 ?$ J4 \' W7 Q5 h+ @4 y
  3. #include <string.h>2 l7 C/ \0 Z0 Y* f( O2 }" L
  4. #include <stdlib.h>
    : v8 F- U' L* G1 u8 p& n
  5. #include <strings.h>( P$ V: k- d/ A7 [' }8 H3 G, Y
  6. #include <sys/types.h>6 c2 l( Z2 _1 w  {6 Z
  7. #include <sys/socket.h>
    & k* x, }* F; [) ?$ A: o
  8. #include <netinet/in.h>8 L/ u0 Q9 V" ^: J" w
  9. #include <arpa/inet.h>4 T# c8 D% v* s8 E1 G, h. w8 S7 g
  10. int main()$ @6 S( C5 p7 |
  11. {
    8 k8 L4 ]+ w) S' E
  12. int sockfd;7 l! b: T$ g) v3 R
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))% @- C; o4 d5 O: D0 J: K
  14.         {
      |& }- Y7 \5 i0 z- Y& Y: u! s
  15.                 perror("socket");& {# j! o' K& \4 c% h
  16.                 return -1;; H; b, d# @+ V- Z2 f
  17.         }4 N, y' J3 ]. \/ B
  18.         printf("socket...........
    ! n$ B; u, x! i3 N5 S+ Z
  19. ");
    ( n; k- {: S% w3 W: Y" M
  20.         $ F" a; `8 ^+ }4 c% U: X" K* J
  21.         struct sockaddr_in srv_addr;
    ' z: M2 [" I+ z* s3 g# u
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    1 A; }! ~3 L7 y9 R. x
  23.         srv_addr.sin_family                 = AF_INET;; y( b# \) A& `+ u+ N! V
  24.         srv_addr.sin_port                         = htons(8888);1 t" J5 Y6 `8 X
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");6 ^9 H) L( c3 u
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    9 r# T1 r% }' Z$ _
  27.         {  J# Q% x$ B6 y% m7 Q, e
  28.                 perror("connect");3 G8 l' F! C, q6 p
  29.                 return -1; //exit //pthread_exit1 Y3 `/ q9 [! b& m; w
  30.         }' m9 t$ ?! \$ b1 V, p3 f* o# [1 R
  31.         printf("connect..............
    3 r: [3 \& g( o& [
  32. ");+ k3 D& T/ P" \% D5 v1 N* T# A
  33.         char buf[100];& K/ y! O6 k1 j' l+ D% _( H5 ~6 ^
  34.         int ret;
    ; c% w9 O% B/ r0 {- b3 w) T9 O
  35.         while (1)
    ! e' T6 |; x6 B# h
  36.         {
    - J" T- P! x% e* E% H( s) Z9 i
  37.                 printf("send: ");
    : j- C$ v& C. b8 m' f
  38.                 fgets(buf, sizeof(buf), stdin);
    ; h! P6 w( Z: H8 k
  39.                 ret = write(sockfd, buf, sizeof(buf));) g0 }! K$ }1 S: p
  40.                 if (ret < 0)  B2 {( Y) _/ @! n; k( B; C8 ^& R
  41.                 {4 a" G1 n; i0 T/ r
  42.                         perror("write");5 X; ^1 ?" l# f0 G! v
  43.                         break;
    - S+ ?$ c  F  |; C9 J
  44.                 }
    - I: A& H4 E7 ?  I8 o  g, }! U! e
  45.                 if (strncmp(buf, "quit", 4) == 0)" \1 Z9 y5 W9 {% L( ]3 r7 ]: B% B
  46.                         break;4 g* @* z7 \  q6 _- C! G
  47.         }. Y, `: V3 I! e6 Q; l( M
  48.         close(sockfd);; `: K7 a4 b) h% _0 W
  49.         return 0;
    * ^! Q) q* ]% g  G) p( h
  50. }
复制代码
& b; Z+ z) s! p  u5 `7 t( I

1 L8 N- c% p! g) a1 v# w
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-20 12:10 , Processed in 0.122025 second(s), 22 queries .

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