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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。: ~7 A8 M5 v& t# K
& T; d# H4 G) m; s1 D2 E6 e+ U8 p: `

- u8 E- X: O& m5 Esocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
1 G3 ]$ _) k- c& l9 ^' n7 A& ~7 q' q- _! V9 H  ^
: Q6 I+ E% u: c( C3 r" \3 `! v5 n
TCP协议" U* a5 [3 O3 U6 [9 L+ Z+ N6 C
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。+ X7 V) j& i& j5 u8 Y1 i
: G/ {8 ^2 l0 [$ u: q9 B

( o5 i4 g0 H% p0 `1 C关键词:三次握手,可靠,基于字节流。
( R" h8 d2 P/ w4 c
& j0 G# y. [% a/ v
& A4 ~7 \; ^0 h/ }
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。+ Q8 z! q& {* j( l

' x2 {8 I4 Z8 ~5 STCP服务器端和客户端的运行流程
! X6 b1 p6 C7 V/ f* h; u7 _* O1 h. I. J/ {( t1 N  i( D# _
; U) o9 X  f; j! {/ B( W* H
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
3 ?- _. G0 I; o
# g5 S% p/ g- ~" L3 T6 M. c$ E
) C9 _* l/ N4 |$ {! S! ~: W- \, ^
1.创建socket
5 O/ f, D" Z/ p5 ~: w0 ?. P/ f# W; O% ~ socket是一个结构体,被创建在内核中, T3 t" h9 k  @# H  Q5 m8 W
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
3 n1 P# Z! P, K* V. z
  a# R! e' A3 V6 J. `8 B

' p& @* M' R  v. D2 V; z& ~2.调用bind函数
, u# G$ k5 M5 T. u% z; J 将socket和地址(包括ip、port)绑定。( Z" p& q2 m/ v3 x1 O
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序0 l; B' ?; `! U2 c# @8 k6 j
struct sockaddr_in myaddr; //地址结构体# g- n* S0 E# `( B- ?8 R" D' H) N# M
bind函数
' _, {4 g% z, Y% r bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* {! m6 j; Z+ e! I4 d( F# v% @' g+ {/ X
4 t! R: ]- M2 A* v3 x5 Z
3.listen监听,将接收到的客户端连接放入队列7 d$ l2 }+ E6 G9 G9 i
listen(sockfd,8) //第二个参数是队列长度! K7 O2 b& ~( U6 ~* j

% S! E7 w- L; X% W( ]4 o% o1 @
" v. T+ s) `9 J3 M) e) o% y9 n
4.调用accept函数,从队列获取请求,返回socket描 述符
5 Y6 G8 G4 Z: N  x' ]8 ]5 ?  如果无请求,将会阻塞,直到获得连接
7 I6 |+ K& N0 H2 e3 }) q0 j" o  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
' i3 C, R3 S; m% u  O; F  S* V: {# Q/ R' M& ~
' v6 b5 o) _; T  v: ^
5.调用read/write进行双向通信
, z! C2 A1 i! ?" A- Z8 }' D) [" N5 D3 ]) |! _

, n7 y; J& h4 z' C6.关闭accept返回的socket
  ?; W4 m# M. P+ }5 Z) x1 }  close(scokfd);
9 @0 O  X, {+ T& E7 a' p) c4 b# ~" k8 J: Z9 T3 s8 |
: `- h4 q- F% Y

* [7 e" ?& }: ~  [$ N* l

# Y5 o( W$ p6 ]1 U下面放出完整代码1 Q1 ^8 `; t) v# O* F

% N- m6 P) [) T& T
  1. /*服务器*/& \' M/ S( T% R3 O
  2. #include <stdio.h>
    * N7 O( U; \1 ~; o9 W+ z' [
  3. #include <string.h>
    4 _6 ~" M( M/ K/ o- J
  4. #include <stdlib.h>
    6 S, ^. z% u' t8 C$ e9 v
  5. #include <strings.h>
    + B' ]! {9 y: u& E( D% y
  6. #include <sys/types.h>
    ' v3 |1 z* A: Q9 K4 a
  7. #include <sys/socket.h>
      z! u; Y1 K: A! x3 N$ b6 w
  8. #include <arpa/inet.h>
    : X& _& ]$ A& V" i. h# u
  9. #include <netinet/in.h>
    3 y7 h) W2 a+ |; T
  10. int main(); X! m* C2 m5 b4 d) f8 _
  11. {
    8 u$ v3 m) Z2 ~$ X
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字$ K$ T4 \1 l% |
  13.         if (sockfd < 0)4 {* h3 z% Z$ G2 b# ?
  14.         {) C" A0 P- {* {9 x8 y& s/ ~* P8 w
  15.                 perror("socket");8 d! T& R% T1 o2 @) e
  16.                 return -1;
    * J0 u& O6 n! J) k" s- }0 s+ z% g# g
  17.         } //创建失败的错误处理
    ) N  N0 u/ O, R1 T6 T
  18.          printf("socket..............
    ! ?" c2 b1 C# K  u- D/ H
  19. "); //成功则打印“socket。。。。”
      W: @8 _" \1 n, ^
  20.          4 c& S1 |5 D+ g: ?" k- r
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体% z5 U; b7 ]* K
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)+ q9 C" W* Q0 q) ~2 b; `# h& i
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    / d1 d/ E% D7 d
  24.          myaddr.sin_port                 = htons(8888); //选择端口号" N# @/ v+ G5 q3 D6 ?
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址9 R' R( `" K: j; @
  26. 1 ~; W2 x8 h- a% p+ y% H+ b
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    * s% L" Q1 m, l8 j1 L
  28.          {8 ^' O* U5 X  N( J; k  T2 E5 u. w
  29.                  perror("bind");
    $ I. }6 e" |4 |$ V
  30.                  return -1;8 _! Z2 P  _$ z: K$ F* |, R& n
  31.          }' }9 w1 v1 y! i+ u
  32.          printf("bind..........  b; A1 s" W( V$ V! V) l# Q3 i
  33. ");' p, ], h+ c4 c  z2 Z$ Z3 C" j

  34. 7 ]3 {2 u, o) V3 U$ U
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听' R/ y) S0 b5 N1 I
  36.          {* ]! p: u0 e9 h. r4 `
  37.                  perror("listen");
    : n* o% I8 U+ g3 H( I% o6 p
  38.                  return -1;( s8 o# O+ ^! O' s% W  }2 k; m
  39.          }
    0 Y2 z5 N0 Z' F' R' X
  40.          printf("listen............
    ! J* t# N# c2 j, |7 S5 A5 @
  41. ");
    3 R4 H6 d: c1 n$ B6 ]# [
  42.          1 m% ?" n  T2 z. w8 o
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    * R! _5 N2 L" x2 _
  44.         if (connfd < 0)
    # H5 W0 N; w9 c) D# ~- |; j' G
  45.         {; m- `( y1 J8 J7 R# }2 [
  46.                 perror("accept");, }( L. Y  o5 }
  47.                 return -1;0 Y' g: y% u1 M: j. r$ b( r) E
  48.         }+ p: V: ~$ _8 O
  49.         printf("accept..............5 `3 G/ d0 t$ _2 W% P- Y# J
  50. ");
    / Q. \5 O- T! c, s: k7 X
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    $ `1 Y* n, C7 R% ]9 j/ h
  52.         int ret;
    4 @# L) E7 Y5 Q: I2 N! K3 j$ x
  53.         while (1)1 N; j+ r0 O- g
  54.         {
    / D) c4 }, ?, u  _; S
  55.                 memset(buf, 0, sizeof(buf));
    : d! ^, t$ I# y; |$ A7 F
  56.                 ret = read(connfd, buf, sizeof(buf));  ?& x( @2 `0 p: p( {6 x
  57.                 if (0 > ret)! e  s: \3 ?4 l4 U, T- Q) V8 A
  58.                 {
    ( ^7 J* ]$ T( {- q4 h9 b' L
  59.                         perror("read");
    9 G: @! M1 l4 q" m/ Z
  60.                         break;
    : \* J! i+ f8 K: W  P
  61.                 }//执行while循环读取数据,当
    3 \! I" n( O( h# ?' \
  62.                 else if (0 == ret)
    4 n; Y  v  \4 q/ w
  63.                 {1 z% e6 c: I5 i* d- N; F% r, S6 ?
  64.                         printf("write close!
    9 S8 G( {5 u; H8 Y. F! l' G
  65. ");
    3 B( q; a2 G0 C9 [- r
  66.                         break;6 T4 n- S3 D* {( D
  67.                 }* O" U( B2 N! N& [% D" \0 |3 b
  68.                 printf("recv: ");
    - v5 P. V. T! ]  }6 t4 @2 N0 l1 r, k+ c
  69.                 fputs(buf, stdout);//打印接收到的数据$ }- g9 c; J/ Q
  70.         }
    / M. I# ~  }5 x. \" \* k
  71.         close(sockfd);//关闭套接字
    1 @9 v1 u7 h# R/ ~) U6 e
  72.         close(connfd);//断开连接
    8 ~$ P2 M+ |6 M+ b% w& J- @5 O7 S
  73.         return 0;" g+ Y) @8 P8 f1 a
  74. }
复制代码
0 f  D% a3 b7 k9 E

8 Y! |5 K" @  e, r- }
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
      O2 r( d6 D* P& [& }# _# a& t8 u
  2. #include <stdio.h>& a0 B! u; O6 N6 ?
  3. #include <string.h>
    " ]6 R+ G! E! t( @' z3 R5 U- K
  4. #include <stdlib.h>2 @- e% S* W/ l/ |1 K! u4 x
  5. #include <strings.h>/ e/ b# Q, ]( s7 h, z+ G* C
  6. #include <sys/types.h>: S2 N7 k) x, g% \  C* f" X  {
  7. #include <sys/socket.h>
    3 I* j7 a% j3 y5 y
  8. #include <netinet/in.h>
    / o5 I6 g% i: {" n$ }- I0 X) r
  9. #include <arpa/inet.h>* G5 W& v# k( m( p+ f$ e
  10. int main()
    3 |8 A0 F$ Z: H
  11. {
    & ^% ]: N$ a! Y- [
  12. int sockfd;
    & f3 ~5 {+ K0 m, X
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))- T' ]& k$ _* f/ k8 Q' h
  14.         {. R4 l# p3 ]+ w, t
  15.                 perror("socket");5 c; G1 k& |# i# w/ U/ w
  16.                 return -1;! l1 A! y. p' P# R
  17.         }9 V3 ^+ P/ f+ ]+ U
  18.         printf("socket...........
    7 }8 t5 ^( [* i1 y& \( w9 C2 c
  19. ");
      O' m- X# [; _  I' d5 ~" j
  20.         
    . m; x# D- t# _1 @$ \
  21.         struct sockaddr_in srv_addr;
      G" v: Z" H( E) b% q  W
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    ! B9 O+ s7 X  K
  23.         srv_addr.sin_family                 = AF_INET;6 q' |% k: P* V8 }5 R3 k+ B
  24.         srv_addr.sin_port                         = htons(8888);
    4 B+ h% i: ~/ V' r2 j
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");% Q7 q+ x4 Y" n' d; v
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))" q' T& S$ L# m, R
  27.         {. S2 B0 G+ ]* ^  e
  28.                 perror("connect");
    - X; K* Q$ a  P( G6 z, S
  29.                 return -1; //exit //pthread_exit
    / M* _* l8 F2 U- n+ c! \! K  I
  30.         }
    , ?0 P! g  ^  y1 }
  31.         printf("connect..............; Z. A. E5 y0 M
  32. ");
    % y1 `5 X4 D5 ?# o- e7 u2 c
  33.         char buf[100];  M- G3 [) H6 e5 J0 u
  34.         int ret;
    / Q8 ?* [+ u8 v8 S9 [: a/ R- J) t& X
  35.         while (1)
    : H& q9 v+ c' B' ^
  36.         {( ?! t* {% [1 S. K  v
  37.                 printf("send: ");9 Q. |  v2 p. t4 _& F2 D
  38.                 fgets(buf, sizeof(buf), stdin);
      d) [. S2 e! A* S2 m: a( q3 M3 r2 K
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ( U& D; V$ J6 Z3 s9 M# S
  40.                 if (ret < 0)
    ' I8 n, f+ {( b- P0 e# a3 s# J; u
  41.                 {
    8 K: @" S. r# a8 t
  42.                         perror("write");+ H7 B# J1 J4 v- y" o2 V
  43.                         break;% Q4 {: g/ X( T4 z$ Y
  44.                 }
    . r; T7 }6 H6 P: _
  45.                 if (strncmp(buf, "quit", 4) == 0)
    , ?& t; L& j5 e" W8 p1 n
  46.                         break;" d* B- H+ {7 c" S
  47.         }
    " e) b9 `, \: K% V+ m$ g& p, G( ~
  48.         close(sockfd);  Y$ R2 w& _/ c- |: E% S/ u+ ~% f
  49.         return 0;7 H7 g7 Q6 W/ Q
  50. }
复制代码

' \' t8 b- e& @. k! ^4 L% ]  f1 u
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 03:15 , Processed in 0.055268 second(s), 22 queries .

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