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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
- j& @5 I- h  j- c  n8 J% V$ E: Z6 ?, j1 G7 P% e) }8 q# V7 a( u

1 h4 U  |* e9 H5 M& G- r( E1 Csocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。, z2 [; }+ i0 j% q
8 d: _* ~% s! I
5 v- \- Z' U  R+ ]( e
TCP协议  t+ [' `9 f; k5 X' ^
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。) [2 @4 l; J0 H' j

8 @+ M( B+ ?/ R' e$ M% }

$ H; w, K8 }: o3 y  s) V( K关键词:三次握手,可靠,基于字节流。
  h* ^; t' R8 @! `$ `* X" `4 B0 ]
; L9 N& }  T- u, _5 l
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
2 n% F* i( ~8 B. G : E3 X( k+ }; x; N$ v
TCP服务器端和客户端的运行流程
" g+ r( V- b8 O: M% d) F1 s. u3 y% E0 K0 r) H4 s

: o& B1 F6 F, z* [7 B  _+ Z' _如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
+ \* p1 y- A) w% N& z" g$ ^0 d) Z8 u' J( ?0 n( g! u% Q/ C

! O- D$ V( S& C4 C0 J: v1.创建socket
8 G! V2 m# _8 K0 n. w* R socket是一个结构体,被创建在内核中- k5 L( M" V- ^0 n' S, {! U
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
: h6 Z9 O% g* l! c% k( U8 f$ p; \! v( ^  i
( Q4 @% ~6 n; I5 U4 i
2.调用bind函数
( k+ f5 d+ ~6 c$ x: e/ L' p 将socket和地址(包括ip、port)绑定。& l6 A9 Q" _* ?/ s3 W$ S2 S
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序( r- _( _. T9 ?6 `5 w3 G
struct sockaddr_in myaddr; //地址结构体) N: l! P& k0 S& z" n
bind函数
% J( [4 n6 p  A bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* k  M! ?0 L1 f  R8 W3 M! _

: ^. S" T6 n, i$ n" z
9 H; T! K$ W, k! n
3.listen监听,将接收到的客户端连接放入队列5 b$ S* U& P; d, R
listen(sockfd,8) //第二个参数是队列长度
  }2 }% o2 q( k0 H$ }2 c
5 _: N- `$ o- S1 |. `# \
* p: s/ Q! z* C$ r  G# N
4.调用accept函数,从队列获取请求,返回socket描 述符
! ?5 ~2 A' y( ~$ Q' b  如果无请求,将会阻塞,直到获得连接, B: K! U. w3 a; \: y8 {
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数" o$ z9 c3 ]- y1 M' N% I, r

  o. [( ]+ Q0 ?/ Y  Z

" z2 b6 j' v8 m* G% U5.调用read/write进行双向通信% f% Y7 L4 y& q1 G5 Z

6 ?# Z6 V7 T+ q$ N) y
* x% Y) q, Q, o8 a6 [! e
6.关闭accept返回的socket
: u3 D* J/ K) y- `5 {* g  close(scokfd);
- s% u: q7 O8 o/ H. P
$ {0 J" m9 f) G; C7 {3 _

; H3 }$ `9 ?& I
* \% p6 N+ ~! ^: @  K

7 B5 Q$ y, _9 M% f下面放出完整代码
: o) V6 n% |; @; z. z: Z7 h9 j0 I* X
  1. /*服务器*/% D4 K4 q! m/ c# c
  2. #include <stdio.h>
    / v  t+ u: e$ m+ r6 V. I
  3. #include <string.h>
    " }% }' X/ _* z4 S9 a4 {: n5 ?! C
  4. #include <stdlib.h>9 G3 d& D$ L7 h+ g9 q
  5. #include <strings.h>! u& U3 _8 ^$ X; x1 P! @/ H
  6. #include <sys/types.h>* i: N2 z% T0 W. p% N* e
  7. #include <sys/socket.h>
    & R  ?/ d, J; p% Y' Z1 {, Z
  8. #include <arpa/inet.h>
    9 `# Z' ~9 O; y' |! ?1 S6 D8 D
  9. #include <netinet/in.h>2 b! e2 g- ~: ~- m
  10. int main()- V: X) i# K# A1 @3 O# \
  11. {
    ( i2 P/ Q5 G( U2 s" |. ]
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    9 {* M0 D0 l3 x6 o
  13.         if (sockfd < 0)* O3 Q. f' g8 v$ _. A8 E
  14.         {0 X( R$ T" @3 e9 G
  15.                 perror("socket");; c9 w. p$ C7 p5 K- y1 y
  16.                 return -1;' z$ _8 S8 s- [7 x, a
  17.         } //创建失败的错误处理
    4 ~$ d' G  @- C/ L8 r) M+ S, N# k
  18.          printf("socket..............
    , V' _& e$ R& d7 p
  19. "); //成功则打印“socket。。。。”
    ( U1 s1 N, l5 ^- B
  20.          
    & A+ _; [" e  Z/ L4 k3 D2 }
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体) J4 y6 L7 ^  f0 H7 `( v" `8 c5 _
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    / ]  K+ `+ T+ ~* L
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型( K: m0 |, [% W' g
  24.          myaddr.sin_port                 = htons(8888); //选择端口号7 }3 h# U3 Y$ M+ |' G# e7 G
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址! h. t' v# g; G# l/ B! v) N/ @

  26. / m" I# v9 y% C! k& ^% G! s0 g
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
      W3 E9 K7 F/ ?0 ~0 C6 j' x
  28.          {  |/ O+ a+ o( @1 k. Y- J% h4 D
  29.                  perror("bind");
    6 h5 o/ f1 {5 {5 V3 ?' O9 T
  30.                  return -1;  J/ F# d* {, n6 z. Y6 G
  31.          }
    / @3 R8 S7 p! o2 p' U$ F
  32.          printf("bind..........
    ' A/ h+ }6 V$ ]0 r
  33. ");
    8 H4 P( ~5 o% v' m- ^

  34. 3 f. W& K+ T& n( q
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    ( k! H' i3 n' i0 Q5 [  e* f, C
  36.          {
    4 H/ n2 C6 y* a! {8 c1 `
  37.                  perror("listen");
    - b3 [/ G+ P( g
  38.                  return -1;# h4 B, s4 |' e5 e$ N4 J
  39.          }' o/ K5 M" R+ a* L. d
  40.          printf("listen............3 X1 W( {2 _5 v( J( ?" E
  41. ");
    3 d; J( I! E$ v
  42.          , ^: Y/ M/ e, N% H( Q8 ]; j4 n
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ' z# F+ q9 f/ B4 X. Q
  44.         if (connfd < 0)
    ! X  `; \( n9 ^6 o/ i* Y. l9 w8 u. @: D
  45.         {0 W9 p  N2 L5 X2 x5 o0 D
  46.                 perror("accept");
    : e% L* C' }- \/ c$ E& W) |
  47.                 return -1;
    1 g$ \0 K2 e  i4 v8 f1 z: f
  48.         }
    3 @! I" O. ?2 `8 v9 E
  49.         printf("accept..............
    # l* O: b/ O! \6 M8 N
  50. ");
    4 ]1 x- e  u1 l/ Z0 ^% U' w
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    ' H( `/ C# q8 s& ~
  52.         int ret;9 R* s! N% T( b2 i- A
  53.         while (1)
    0 Y3 d1 v0 z) p+ O0 o
  54.         {
    / @! d) j! w+ C( y! P
  55.                 memset(buf, 0, sizeof(buf));6 u" }, S: a' ^7 U& R$ ^
  56.                 ret = read(connfd, buf, sizeof(buf));; R( Q0 x5 M: P& [
  57.                 if (0 > ret)) Z' _: l& D- i2 g0 |+ `( @/ m
  58.                 {& J. e% y% M3 |* S  F
  59.                         perror("read");
    ( E" |4 t' R* @8 w
  60.                         break;- Z* f' e$ P4 d' |2 w  h- W8 x
  61.                 }//执行while循环读取数据,当
    9 Q9 L6 c' l  D. T" r0 a1 c
  62.                 else if (0 == ret)
    , f, T& A' }) ?( m' Q7 J
  63.                 {2 H. U0 ~. I/ r- a( f1 _
  64.                         printf("write close!
    1 y. Y8 h1 D* P) X) s5 b4 m
  65. ");0 Z1 i- T% G2 S+ u; N7 x) ~
  66.                         break;
    2 x5 G' r7 Z* ]& K7 f
  67.                 }
    ' H3 D9 ], N* ?, j  [' J; c( Z3 Y
  68.                 printf("recv: ");9 |$ C  v# N. ?$ n5 M$ u, f
  69.                 fputs(buf, stdout);//打印接收到的数据9 I( J: R# W2 z7 ?5 }* a
  70.         }
    4 ^$ d) \0 O- v$ B2 j
  71.         close(sockfd);//关闭套接字% o6 B% }9 ]+ H) z: B" V0 g; N; r
  72.         close(connfd);//断开连接
    # O( t4 X6 e) J5 A& w. E
  73.         return 0;
    % p& W5 h9 T6 z7 k4 U) J
  74. }
复制代码

" D, `# i* ]; n& W: c3 N5 K1 {
7 v- H8 ]* Q# L
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释). d' u# z" t  `1 x7 g: A7 R
  2. #include <stdio.h>0 q6 d9 {- o, J6 A
  3. #include <string.h>. [  Y: D: }& V. x! e! _! ?2 v, k* H
  4. #include <stdlib.h>
    % e# u0 X! n2 p$ ^' {6 L6 L; |0 {
  5. #include <strings.h>
      {) B' l5 N, Z# [) M& `
  6. #include <sys/types.h>6 Y& H$ H( y6 ^. J" N9 z  g  K( g
  7. #include <sys/socket.h>+ K0 i8 s. p9 r) X; e/ U, l
  8. #include <netinet/in.h>
    1 a3 a1 p1 t! r/ e2 x+ Z
  9. #include <arpa/inet.h>! w  ^! J, a% b: r3 N5 c/ z
  10. int main()
    4 K% g9 n% [1 A2 n
  11. {
    : Q7 x6 I. l$ ^7 P! B: V
  12. int sockfd;) ]8 p& I/ p: S; J+ V3 F/ o
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    6 o- M. C9 |: F5 K3 E. ?
  14.         {
    4 _! F: t6 o6 D3 R6 |4 l: I
  15.                 perror("socket");
    7 s/ b' r3 z6 [% x/ l" r
  16.                 return -1;
    ( v4 m& }9 n7 g: |! y3 d  w
  17.         }# m. i+ e+ j! }1 _
  18.         printf("socket...........7 c2 z4 ]/ e1 S% i. C8 \
  19. ");
    $ w; F! r+ I9 `, e
  20.         " U& L. V8 @8 I5 Q
  21.         struct sockaddr_in srv_addr;
    # e" l; ~2 R' L# B9 E8 w& v# w2 ^: Y
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    : r2 s- U5 K5 e1 f5 g6 k
  23.         srv_addr.sin_family                 = AF_INET;
    $ g% B3 [/ }* R$ j  `: P* v
  24.         srv_addr.sin_port                         = htons(8888);
    . I' l/ Q  W2 S( W! V  q/ z) l
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    0 r) o- [1 d; g3 y
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ; \5 \$ O- Y+ q5 G, {
  27.         {" b6 K9 s- k7 g, S* n% f
  28.                 perror("connect");
    + d1 r% F% C1 k+ G3 F
  29.                 return -1; //exit //pthread_exit! Y: g% g* @* q! w2 ?4 N
  30.         }6 [$ V8 J; P. X3 t( y, M6 s3 M" o
  31.         printf("connect..............5 ?' [* s% o! D3 P3 i
  32. ");0 r; G* y9 U8 V' l
  33.         char buf[100];
    ! K: ?0 S$ f" z  p7 G( q* y
  34.         int ret;1 f! X" S8 F9 P/ L0 {/ K
  35.         while (1)9 G. N1 b. E7 v8 v: e: `$ F9 R3 s
  36.         {# p8 |5 }0 s" i9 h% \, }6 j& S8 J
  37.                 printf("send: ");+ {0 S4 ~) y: u- _- R
  38.                 fgets(buf, sizeof(buf), stdin);
    * H* c5 k4 A: K, E# X: o
  39.                 ret = write(sockfd, buf, sizeof(buf));& Q  N% a7 }( {7 u# |  M1 b5 A
  40.                 if (ret < 0)% m0 z& c. l- H- f
  41.                 {
    0 J: g+ B1 O, c+ r
  42.                         perror("write");- |" R' _* ]% X# D
  43.                         break;6 L% F: Z- i+ V. Y. g& f! u. e/ f
  44.                 }5 m* r. g5 }6 ^
  45.                 if (strncmp(buf, "quit", 4) == 0)
    + {1 e3 D. w6 G" q
  46.                         break;( @0 C  w6 T4 c3 |8 ?4 n5 d
  47.         }
    ! H# N+ X3 N3 O8 j/ M* ?
  48.         close(sockfd);* I, {+ ^, m3 a# C+ L" o
  49.         return 0;7 i+ H6 a, w+ N$ q
  50. }
复制代码
2 y! M9 m8 d! y+ D7 O

5 ~9 r; F- Q! ^: d; }
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 00:52 , Processed in 0.070313 second(s), 22 queries .

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