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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
2 P! B# C: V9 C. E; \3 ]  }0 B9 e; T% C3 |$ x2 g

( ^; o( g6 S+ b4 t9 Isocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。9 q8 O6 i0 G  V* X$ r8 m  n

9 I  D6 t5 Y3 u" j) H0 ]
# t: w, f  g0 J3 y$ u
TCP协议) _9 t6 F1 Q  z8 |$ {- A" `
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。6 Z7 r! _9 B/ b' U! ?
# e) G% ~* j7 Z8 [1 M$ J

5 Y2 p! g4 m0 e7 ^关键词:三次握手,可靠,基于字节流。
8 Q( v8 ?: H2 @$ k3 i: s/ H; {. F. @' I! I6 D

1 e" Q) f! v7 s0 h& F: g可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。6 s6 `, I* V8 J7 P+ n5 |
7 G& ?- }" a( K' E) R# l5 g5 {
TCP服务器端和客户端的运行流程
5 |" q5 g( j, F' K2 N7 Y2 p# z6 b- `: P$ ~

2 b1 N6 ?1 h/ N* _6 g# C  S1 F/ G如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?  l# t- v. _/ {& p  @% M3 }# j' ~
4 t9 p0 \' I& s$ r4 Z* Y4 P
4 K' M( C+ A6 m# V
1.创建socket
: p0 D- O% h: P+ j1 f3 _5 T socket是一个结构体,被创建在内核中
% Z  O. J1 u5 I" E' r* L$ l sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
$ s5 u  J' f) {! P# _6 A1 t: Z, O0 P' S! r# K% y$ ~1 ^. l- K& B
$ a7 y" O: H2 a+ Q
2.调用bind函数
# z1 g  l( V  x+ ]6 I/ Y" m" Q 将socket和地址(包括ip、port)绑定。
+ v, Y! r% l$ v8 h* L 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序$ r( w; M) W/ {: j- W) [
struct sockaddr_in myaddr; //地址结构体
, F; H, P2 T8 C& i4 @ bind函数
5 j! {, U- C7 W$ I bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
, Z; A, V& b5 E4 ~0 L. j8 l
: ]5 e% b* A2 a- ?% j
( g0 F. e# }: u1 g1 D7 K
3.listen监听,将接收到的客户端连接放入队列
4 A; _* z  s1 B* A listen(sockfd,8) //第二个参数是队列长度
* e+ ^/ y) [7 O7 m  O) V5 O% ]4 M+ ^% X1 I

# m+ G1 N# I( |3 S8 n) r5 U' A) ^4.调用accept函数,从队列获取请求,返回socket描 述符
5 ]% W  a( S) Z! h  如果无请求,将会阻塞,直到获得连接* A0 a4 P+ ]5 O
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数" p/ T+ \6 l3 m# w7 F
% h! k6 J4 h6 A2 L; H
' g; J7 Y% a/ C; k( k: w
5.调用read/write进行双向通信6 k) b2 C# R4 ]+ @0 v2 {

/ E7 a0 G- }0 o) Q9 I

& b: Q, A' A4 r: z. z2 l6.关闭accept返回的socket
5 b5 I" J9 [& B6 E" r, g  close(scokfd);  v' @0 i- L* y' T  j6 |

2 }0 A1 U+ y: X9 y. T

+ u' F* p) ^2 o) a5 o# N9 x# F: v9 R2 X/ U
9 h5 `. _! |( {! C5 N
下面放出完整代码1 ^2 A, r3 |& S2 W: Z2 p6 J  ^

9 L& t! P- Y, ?4 w& W0 v
  1. /*服务器*/$ g3 S' Y% L* z* N  X7 `
  2. #include <stdio.h>
    1 I# e4 _+ ~4 r; J+ z$ q
  3. #include <string.h>- e4 w' |) Y2 P" f/ i+ X
  4. #include <stdlib.h>
    5 s$ U2 ]8 z2 a- r" ^4 p
  5. #include <strings.h>
    + N1 M6 V1 C( b! n( M$ |
  6. #include <sys/types.h>
    : @. c; u7 I9 Q; {. H5 Y
  7. #include <sys/socket.h>- D# i1 H- H% C2 b) B
  8. #include <arpa/inet.h># Q0 ?2 \, T; z7 X
  9. #include <netinet/in.h>
    : [3 y+ l3 {+ b, q
  10. int main()
    1 \9 n0 E, m$ L& G7 @2 }
  11. {
    . H! s. ^0 E  D
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    8 U9 S" G. H/ t- o# Y6 A1 n8 h
  13.         if (sockfd < 0)8 U9 V+ r, j" ~/ m3 G
  14.         {' Q+ ?1 i/ G* e9 R( X
  15.                 perror("socket");
    1 R+ q2 K' s. s8 q  G+ v0 _
  16.                 return -1;
    4 [" c+ `3 I: O5 q; A$ u1 I6 a
  17.         } //创建失败的错误处理
    : n! M; Q: \, ^: m" K: f4 o: s, q$ c" z
  18.          printf("socket..............
    ; ^. ?' w7 g% c0 P
  19. "); //成功则打印“socket。。。。”0 G6 ?" f$ ]" _) O
  20.          
    1 E4 ]6 d. D8 v5 s: b5 E; B$ w
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体" p* L  @% M- O4 a9 b% y7 r' k/ z
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)  t, G" W0 k' n1 x1 D
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    % q. q* U5 e- l6 x# ]9 w% i
  24.          myaddr.sin_port                 = htons(8888); //选择端口号# v& H; L9 s+ g) _0 f
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    0 v, X  d, y8 j9 L* A& U

  26. # L: q/ z  }( I. r5 g% B  a; J
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字: H! t$ ~$ y) X
  28.          {
    3 ?6 h7 n( h( j" s; n7 I
  29.                  perror("bind");
    , g& Y2 ]7 {* a2 o  r
  30.                  return -1;
    9 ^9 T/ q3 M7 p6 v, W
  31.          }5 i0 g- M6 b7 S* u2 S% f! g
  32.          printf("bind..........
    1 s0 E9 q. G, ~  \  V
  33. ");
    , B, R: j. j0 e  {: _! F

  34. ' ]- L4 p3 [! w: B  B* s) @
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ; q" Y1 [* y) M8 C& b' [
  36.          {. O, w+ X7 U: J$ l# l9 ~
  37.                  perror("listen");& p6 H: _4 i( J% t& O: \% y% W
  38.                  return -1;- q' @! x. F/ l: \: k
  39.          }
    - p# j1 ]; O$ h. o3 }: F9 v( i. S
  40.          printf("listen............
    & V7 c: E9 S) w  o$ Y
  41. ");
    " V5 y  A; @) @4 I; b
  42.          
    5 g$ {4 t& |, {# `& D% g8 \4 Y
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ) ?5 C- x8 j/ Z
  44.         if (connfd < 0)
    , U: ?7 B+ a3 i5 ~
  45.         {$ C8 @# g6 Y% `: K' M% N2 o
  46.                 perror("accept");8 k+ m' N  O1 o/ }- B
  47.                 return -1;
    & a- Q- `. W  ], p6 f
  48.         }/ k# H/ M" [$ _* \9 k0 _* _% q7 s
  49.         printf("accept..............
    * ^" ^. O5 Z) y! W* A* K$ g: b  d+ Q
  50. ");
    ! ~$ b, H+ @8 j9 p. B
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    # U; [2 f! S* t7 d6 d' B
  52.         int ret;" o" R6 @* U3 M
  53.         while (1)
    * U# w9 z) l) i! h9 q
  54.         {
    + T# X# p6 X1 o& m  o  z
  55.                 memset(buf, 0, sizeof(buf));
    # f0 T% j' T: \* S
  56.                 ret = read(connfd, buf, sizeof(buf));
    , u2 n4 V1 _1 J) B( f' c: b+ R/ L
  57.                 if (0 > ret)1 F% ~2 l$ J/ i. Q; V1 V
  58.                 {, M9 e* n( P5 ~. @# I
  59.                         perror("read");
    6 v) e# A1 K3 A; I3 O
  60.                         break;8 G6 W( c0 o+ J# D$ r6 a
  61.                 }//执行while循环读取数据,当, [, ?2 \" ~* R: C* q% p1 ~
  62.                 else if (0 == ret)4 z' D# d/ W, b5 H9 @
  63.                 {; ~! A  ^1 ?* Q, u# n
  64.                         printf("write close!
    : p6 ?7 A4 ?8 A/ e
  65. ");' A0 g" z3 L# f" i
  66.                         break;
    6 E: ^9 O8 K& d& f$ `9 S4 w5 q3 b
  67.                 }4 ]9 C% `0 b0 \! ]1 v
  68.                 printf("recv: ");
    # C& w( ?9 K: j7 A- R& r
  69.                 fputs(buf, stdout);//打印接收到的数据2 m# ^. Z; P2 n3 N) y! m7 ?5 k
  70.         }
    # C3 O' y/ z; x$ v  U! N  h
  71.         close(sockfd);//关闭套接字; G( v; q* V: @3 ~
  72.         close(connfd);//断开连接
    9 o9 U0 p8 N4 i8 }. ?/ K& r: B
  73.         return 0;& l( Q$ ^' J& O9 M+ ^
  74. }
复制代码

& K$ E9 G% O  W/ y$ ^  s
. e7 B3 F$ j6 O
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)) I0 _- h' Y8 ?4 }& n
  2. #include <stdio.h>
    - n6 l+ Y; F) @, N& y) ?
  3. #include <string.h>
    1 |3 `# ~1 D1 V1 P* J
  4. #include <stdlib.h>2 F+ \8 q' _% O7 k
  5. #include <strings.h>1 O1 ?& [% e( d6 l
  6. #include <sys/types.h>. ?; O4 u* r0 }" v' |
  7. #include <sys/socket.h>
    " o: i) @, A* V; a
  8. #include <netinet/in.h>
    / p/ L; W+ V+ D6 b! V
  9. #include <arpa/inet.h>& h  M, d' b& G9 M: S
  10. int main()
    * N' [! |; `" E5 {5 N/ n
  11. {
    ; \4 h" ]8 V* t0 G. t$ W' c
  12. int sockfd;
    4 k9 b  z8 x7 [
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    8 Q; Z! m) [) O9 x
  14.         {
      m2 q4 n1 N8 j; T9 ~9 S, l6 H
  15.                 perror("socket");
    ' [/ o8 u9 ~" ]
  16.                 return -1;
    9 c% A5 g1 d; t+ b
  17.         }
    & d3 G  b+ _5 H7 n) l
  18.         printf("socket...........
    % h( C+ i$ @1 Z' |( u; a
  19. ");
    ( \( W# o. W5 T  |6 l
  20.         
    3 ^: `) S( \* l. g5 J
  21.         struct sockaddr_in srv_addr;& S' b6 f/ ]9 [$ t) H5 p' O
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    5 l2 b+ Q9 Q, Y, ^
  23.         srv_addr.sin_family                 = AF_INET;' X0 J: C& P: f) c. s
  24.         srv_addr.sin_port                         = htons(8888);
    ' {5 o  t% p# L, N9 Z
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");  f4 v. ^% Y1 P) [- z8 ~# h
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    9 S- D, c( X% n+ b
  27.         {
    + A. L9 `, N+ q1 l: e% g6 Q2 |1 P* d
  28.                 perror("connect");- \) r4 c  R2 {- B; f- O$ r  V
  29.                 return -1; //exit //pthread_exit
    / m# t9 q* `8 e2 _6 n
  30.         }. A" Z( k; K! X1 t2 L3 n
  31.         printf("connect..............
    $ ^. O- G9 d: J- Q6 \' d
  32. ");
    ' V2 n0 S2 f/ m
  33.         char buf[100];  m$ a- ^2 v. M6 _2 @5 S& w
  34.         int ret;; ], @. C$ w( {9 a3 b6 y: p
  35.         while (1)& @: ^0 ~$ H) ]
  36.         {
    4 f( l$ U6 y) _2 K) r* c, J
  37.                 printf("send: ");
    . \0 V! {/ E  v4 `6 x/ F
  38.                 fgets(buf, sizeof(buf), stdin);
    , `4 h# B( w9 w1 ^; |3 {1 A6 W
  39.                 ret = write(sockfd, buf, sizeof(buf));
    3 D& ~: v" k7 n4 U
  40.                 if (ret < 0)
    / F! v  D# D8 v$ {7 R9 E
  41.                 {. A# [1 w2 l8 O" H
  42.                         perror("write");
    8 E" J4 x  v5 g. o: H7 A
  43.                         break;
    8 {* U! c) e7 E" ~
  44.                 }
    ' K+ C, G5 R6 U
  45.                 if (strncmp(buf, "quit", 4) == 0)
    * r6 K) B# \' K
  46.                         break;
    9 {+ @) |$ h% u$ `6 s2 H0 o
  47.         }
    ! @, t, B2 x3 d( B: n
  48.         close(sockfd);
    - d" O5 B1 a% X
  49.         return 0;
    " g# ~& ]$ @8 l0 H
  50. }
复制代码
: i+ K) I8 p& K* B( E

  b- J; w* V- y4 ^$ Y; c+ _5 J
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 03:41 , Processed in 0.118943 second(s), 22 queries .

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