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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 显示全部楼层 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。$ H# f# c$ w5 ^' A: \' u( E1 F

1 H9 b6 k7 y$ t5 j
4 R1 e; S6 K% w1 W" ~- _
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。* }" J3 q* P4 I

' X  W  l3 |' \1 z7 Q( K
/ z4 j# [" o) o+ K/ \
TCP协议3 @4 E! W% I  d5 d( t
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。/ F4 W$ A) @( o" i1 D/ @
! k$ o7 G: l1 `; P1 B
6 n( I3 O5 j+ t1 D9 e# f% v0 q
关键词:三次握手,可靠,基于字节流。: N! K" v* H, f
% U# t. H3 \% q; h. \+ ~

6 |+ _" R% B& p4 N" ]可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
  m% I1 |2 Y2 ]  n& _ % k; ?) F$ `8 L( T" C7 K0 Y
TCP服务器端和客户端的运行流程
" N2 }+ T' J1 _- \. Y( L# \! K
2 k& g) t5 ]. V- J4 C, `" s! M; l
$ `+ o& N4 H, }0 d) T: ?7 `) _! ^
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
6 f. K/ Y  B1 U7 c# D' I8 E7 B& ?1 p
$ k4 K! {1 S8 T& b! u
1.创建socket
* Q9 d( y8 L% O6 _# c2 \ socket是一个结构体,被创建在内核中' y' u) X, V/ l
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
. v8 M% u5 h! x2 H# z
( C2 K& `5 z) q0 Q  b8 Y6 g7 }" a7 a# T
: s" k! }% a- T* h; y
2.调用bind函数
2 S  K' A9 j+ `/ ^) V6 \ 将socket和地址(包括ip、port)绑定。
" H4 O4 |0 @1 u5 r5 a4 ~4 Q5 F 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
0 _) S6 r( G$ p* w struct sockaddr_in myaddr; //地址结构体
  {" J1 p. Z# f& x- x6 X; @ bind函数
, q' A2 Q& b1 K6 k3 i bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))5 s* ?% C; c( X0 \/ l+ M1 b

; O5 E. {0 ]7 W: _. ?9 `) y

; _$ l2 M& P0 c/ b7 g0 r5 c3.listen监听,将接收到的客户端连接放入队列
3 `' Z9 ]" C  X4 {7 ^% X4 W. ? listen(sockfd,8) //第二个参数是队列长度1 T3 f( Y0 l& j" Q
! s% A9 @, y$ Q

, P& i; w% {. C) g: b% f0 f4.调用accept函数,从队列获取请求,返回socket描 述符) w, v' O: K# n
  如果无请求,将会阻塞,直到获得连接
- w" {& |$ y# c) [  w: B8 ]" a  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
9 z+ I% a9 O  G
+ ?! e( f: v* r0 {

, F; T5 G" |- W2 c, @# h) y5.调用read/write进行双向通信
2 e0 i* r* |& p$ d9 a, [; U+ L( L
- Z( R# a3 L1 v- }( s

9 [0 R3 L: y. r4 ^. W) c, h6.关闭accept返回的socket
6 a6 W4 T; q; k  L  close(scokfd);: b; l. }$ X3 ^* F' l
+ ~- T4 l4 B' H. G% B
- V5 N" w$ D$ U! o3 K

3 Y, |4 B; }& i- ~. i* ~

. M5 ~9 ~( a. f1 J下面放出完整代码' l% v. e( M& F7 e

9 F' h. `5 P5 w3 R& e
  1. /*服务器*/
    . |+ ~. o5 \2 F; z
  2. #include <stdio.h>6 V' K7 s- G3 u) `4 X+ D$ c
  3. #include <string.h>1 C7 Z5 H0 h; L0 M; F* B
  4. #include <stdlib.h>
    ' L$ t: w" A4 C, b5 Z1 e# N. v
  5. #include <strings.h>
    + H; c2 R  I+ a0 [& L3 g) o* n
  6. #include <sys/types.h>! t& z& M7 A! w) |  {1 h7 j+ X, ~+ X. G
  7. #include <sys/socket.h>. C9 b! U; `6 U0 a5 R( ~/ d
  8. #include <arpa/inet.h>* [" Y* c5 l/ C9 @) \$ k
  9. #include <netinet/in.h>% ]& B- \$ |6 d' D
  10. int main()
    : M9 g! d; r6 [) Q5 B8 C
  11. {
    % e7 m0 Q& N+ ^2 k$ Q
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字! `/ S$ K% t) O, w+ \0 }% M
  13.         if (sockfd < 0)4 p/ x* N" |% z! L6 I+ F# \
  14.         {: r3 d; q4 u$ D4 m0 y
  15.                 perror("socket");' g/ ~. h# j; y0 J# i  Y+ p
  16.                 return -1;
    : R& t* P5 h& }
  17.         } //创建失败的错误处理
    6 u3 [7 `1 @5 J, n
  18.          printf("socket..............
    8 q. ?$ E1 F' e* C0 o
  19. "); //成功则打印“socket。。。。”$ D7 O5 g, k- E/ g/ L& f
  20.          9 [! S# v0 p4 m
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ( J' x  F  U2 z0 \2 O; s) X1 h
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)* r* N- W* H+ T" L! c5 J7 q
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型' i$ e- C. T, ^3 n  l
  24.          myaddr.sin_port                 = htons(8888); //选择端口号! C! Z; J% a, N- Q8 l1 h
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址+ U$ X7 B8 g: u" p0 X/ Y

  26. ) f3 W& F  R- @6 W6 d0 ^* `; x) Y
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    3 S! w; a: H, X2 M; H. S
  28.          {% c7 H  A$ @+ I  _9 V& a
  29.                  perror("bind");
    ) j* T& X! Z8 s$ |- S8 }
  30.                  return -1;
    9 w% y! F" O! j$ e/ ^
  31.          }
    : x& d1 r0 F6 _% G. s3 T
  32.          printf("bind........... A% x0 ~+ A  c, I# E
  33. ");+ j/ W: ], [, P/ C

  34. ( g: K9 d; p4 c9 Q8 ~  l$ h& s9 c
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    8 n0 L( t# n" |! U# Q+ b
  36.          {
    5 x2 A4 D: J/ S, a! y8 |, o. p. {
  37.                  perror("listen");
    $ a2 w  K0 S  o! ?4 T7 u
  38.                  return -1;+ X3 z0 n/ p4 [% T& F8 W% ?
  39.          }# d  B$ p; z, a1 _) k% }
  40.          printf("listen............2 H3 a1 U8 E8 m4 V' [/ v
  41. ");
    4 I+ C/ D& W! O; X  ]
  42.          
    0 i. L, }  G2 P. W$ N
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    1 V" ]8 h! G) w: \( d
  44.         if (connfd < 0): Q, z7 b" D/ c2 I9 M( T
  45.         {
    & R4 [8 o# o0 J1 \3 {
  46.                 perror("accept");
    ; o8 S( `* v; T+ r
  47.                 return -1;5 u0 |. Y/ c/ X$ m/ {+ I
  48.         }0 i2 ]2 t& {6 [, w2 O1 }
  49.         printf("accept..............3 Y- e2 r) H! @* W. t
  50. ");
    # N. U: A, b3 g3 a
  51.         char buf[100];//定义一个数组用来存储接收到的数据. r, Z2 L0 j( l- g/ W
  52.         int ret;: O; C( h+ {0 N+ \  `/ l( ]' r
  53.         while (1)8 ?5 P, [1 P* \
  54.         {
    & _, Z7 ]: U7 m
  55.                 memset(buf, 0, sizeof(buf));
    - ^1 W, l& u% z/ K, |3 y, I
  56.                 ret = read(connfd, buf, sizeof(buf));
    & V5 G5 F2 A( I- u) u
  57.                 if (0 > ret)
    4 A; Z* n' ?  t& ]+ k+ m
  58.                 {8 ^, h- {9 D" p! \9 Y" n+ A( @
  59.                         perror("read");
    " Q  A2 v% u2 ^1 D4 J" S. q
  60.                         break;4 R6 o8 S" R( U. J, ]* a. I3 n5 R
  61.                 }//执行while循环读取数据,当- H7 l7 u/ P8 p; \' _2 b; G
  62.                 else if (0 == ret)
    , T% N6 y" R+ _+ {; o* L- S
  63.                 {0 ^6 q  J" P+ K; n  J- U
  64.                         printf("write close!# T; a1 t' |: x$ p* `
  65. ");7 H, E/ }7 O5 W, T" a+ b% z
  66.                         break;# m+ V$ v  k6 A& Q# o& h
  67.                 }7 t: r# B6 f- B  d  Q% [. u, {* k
  68.                 printf("recv: ");
    . W* v$ r& D; D" Z! D; q
  69.                 fputs(buf, stdout);//打印接收到的数据
    / w$ |# d; n6 A) T
  70.         }  {# b( B7 N' q5 p
  71.         close(sockfd);//关闭套接字% L4 N3 D. y4 W, v1 H3 a
  72.         close(connfd);//断开连接/ [5 |( }! h: u& r
  73.         return 0;
    5 S$ B; h* I0 V. g' R; N- N+ C5 {* {
  74. }
复制代码
3 V' d- N" |! ^# D" C

; {* c! Q1 ]2 u3 K& K
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)9 _" `; _; c3 p- z6 G5 D% Z
  2. #include <stdio.h>1 v9 R# C& n* O( Q. c
  3. #include <string.h>7 [3 B. e8 e, M, w+ o  b8 c& D7 c! b
  4. #include <stdlib.h>
    ' n+ {% V; Z  c: Y3 E; R
  5. #include <strings.h>8 d8 d7 G4 p% B6 H& }: p" ?- E
  6. #include <sys/types.h>$ y' }  W2 r- D8 d) P2 N% y% s
  7. #include <sys/socket.h>4 b3 j1 w' i/ ^% q
  8. #include <netinet/in.h>4 g" V+ E& \" a
  9. #include <arpa/inet.h>9 `7 Y" L5 v8 g; ^- S4 W
  10. int main()" }0 E9 i- O  p+ T, X* z
  11. {- O  _: @0 C+ m) b  \2 {
  12. int sockfd;
    2 D0 M7 ~: I  ?: }6 o9 L8 Z
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))  g* V5 Q$ h1 V
  14.         {
    5 z8 n% z) T$ Q* h% n) |$ P
  15.                 perror("socket");
    6 m* ?# B0 ~  `2 `7 D
  16.                 return -1;' s% D5 p8 j" k* Z" X% j$ D
  17.         }
    9 S0 ]) z, m; ^8 V9 \; J
  18.         printf("socket...........# {( j' d" M! }% v, l. z6 ?# f; @- j
  19. ");
    , t3 L" {/ M7 _  @1 u1 Q& O
  20.         
    - [6 L! ~! c$ o; j3 ~- W
  21.         struct sockaddr_in srv_addr;
    - P: j, h4 g: L/ g, {( ?
  22.         memset(&srv_addr, 0, sizeof(srv_addr));1 }4 Y9 R! O4 S# d: d
  23.         srv_addr.sin_family                 = AF_INET;- c1 b, q( Z& q- E0 [, x7 \3 M
  24.         srv_addr.sin_port                         = htons(8888);7 [9 E$ F* i( N& l
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");0 S& y. E% [! c+ {$ E9 R# f0 ~
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    5 a2 r8 d+ e9 p4 v1 g$ H! \
  27.         {$ x# g' ^- ]3 f* ]7 |5 g' S2 y' k
  28.                 perror("connect");
    + l( u' K& _# Q+ s2 i5 w* N
  29.                 return -1; //exit //pthread_exit
    : ]" P5 Z- T5 |  y5 A* e0 \
  30.         }! p) N8 w9 B1 d9 M1 w+ \
  31.         printf("connect..............: ^) _5 y# ]- N$ n# p6 F) x8 k% s
  32. ");
    6 G0 U# N( M3 k, ^# @8 a7 {
  33.         char buf[100];
    6 F7 h4 H" T* \; x2 B
  34.         int ret;
    , i. [% S3 U, d7 g1 [5 y4 H
  35.         while (1)
    ) S0 r* j4 g  Q* D' n' U" a& n0 \
  36.         {
    - U; I  E- B( G' O  f+ N+ f  g
  37.                 printf("send: ");* U) v4 h$ J# O& r
  38.                 fgets(buf, sizeof(buf), stdin);+ V7 @, a1 A) V: T, x
  39.                 ret = write(sockfd, buf, sizeof(buf));+ j$ T9 D0 W- F6 F) s9 f: I2 l# `
  40.                 if (ret < 0)2 f2 W% A* k- f/ b# H5 V0 V( i
  41.                 {
    5 P; H; I+ l0 p# H9 k. W" q1 `
  42.                         perror("write");2 a& t2 K. i; Z" f" H
  43.                         break;9 s! U1 q- n3 w6 |( |2 K3 T
  44.                 }
    1 K& h& R1 r# @4 A/ M8 X
  45.                 if (strncmp(buf, "quit", 4) == 0)4 G2 ~; F8 I! T
  46.                         break;3 J8 d. h  M: S3 d& [! P
  47.         }
    & }) _. u/ U7 d3 D! _
  48.         close(sockfd);
    - l. N9 W% N% H2 Y
  49.         return 0;
    % R* \0 o, S' h4 Y( o
  50. }
复制代码

- S0 z5 y& ~8 N  m" z  l1 h& f9 G, J0 @# O! n' Y
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-2 19:34 , Processed in 0.130267 second(s), 22 queries .

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