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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。, |/ b. v; @1 p  U; E& X
$ I6 p7 Z' z3 V7 R& b! G+ A
5 b- U) C1 D. K: b( A! D
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
6 Y. k6 ]0 q) x7 \8 N9 q7 f" v. j( u- p* L2 v1 f* t

4 n: D7 x& A+ Z6 ~% w/ TTCP协议! ^, F# i7 i5 n& l7 n4 i: i8 w
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。$ Y) n9 z+ W9 C
! U3 M( M% X' v- t" ~# X! E4 |) }
( L5 M" d! ^  {
关键词:三次握手,可靠,基于字节流。
! X* |$ l) H. o. y' q7 H" N6 |
9 t/ T( t( p0 `

, N6 V9 j# R3 i) x! m可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
% @' }( ]: V. \
; c! x2 ~' c  x, o, O9 VTCP服务器端和客户端的运行流程
+ y  g6 ]- E* i
0 D1 T4 ]+ Y, a
" d  _& N" m) N6 p
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
7 F2 @7 u: b0 c; ?. ]
8 M( r! I! m4 I8 W  M. a
* m5 W. C8 `: K1 _4 k/ T
1.创建socket
9 u8 ^' N2 ^+ n: V" B7 C" Y socket是一个结构体,被创建在内核中3 d0 e, f) v  X' d% `
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
3 M2 z( J1 o/ X
+ b* q" N0 d+ s. B

. i! [4 B( c8 G2.调用bind函数
; D5 ^5 K6 W2 }: Q% M+ g 将socket和地址(包括ip、port)绑定。
) B4 i+ |) s/ J7 u) j# h9 ^ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
; h: M( u% e( F9 ~, L" K3 ~ struct sockaddr_in myaddr; //地址结构体& |: I0 a7 J4 V! Z% x, m  Q9 e$ D+ n
bind函数% ~6 h) f9 N( M/ Y6 i: N9 {" c
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
0 ~0 O" I, @, r2 M  E# t: i- t2 \$ F. m- A  }9 I* x# R+ E

7 k$ L" ~) x+ T8 D" ?* v$ ^3.listen监听,将接收到的客户端连接放入队列4 a6 L* {- Z3 y3 B
listen(sockfd,8) //第二个参数是队列长度% q6 _, f6 i) _' z5 ^
) m7 P& Y( u- s+ n1 |! T3 _4 F

5 Z8 S0 C( {5 b9 `7 F4.调用accept函数,从队列获取请求,返回socket描 述符$ [2 n7 e; T* O
  如果无请求,将会阻塞,直到获得连接
* L; E3 Q8 p' M  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数" }6 D# p  \; d6 J* ]+ I: r
& Z0 ?& i) l' R
; W, `) o8 w6 p& P6 n/ S5 @+ `
5.调用read/write进行双向通信
3 H5 n! u# R) f- F6 J' Z1 q5 [* b4 R8 s9 e. Q0 U
; B4 D4 R) E" N. q* N4 f4 M& g
6.关闭accept返回的socket$ n* s; o! u0 |3 i  F# @8 s' l
  close(scokfd);
9 [# Q' I! G4 W; e0 k: K5 }: m
) M$ C1 P$ m5 e' {

# M1 Z& Z. i  D4 D* k( x' ?* M' q" A# v
& ~9 x; ]6 ^) \+ C+ {% Y  v# d! R* b
下面放出完整代码
9 G' J, [8 d+ B5 \% j; @5 y; I: ]4 `) `: x2 _5 e
  1. /*服务器*/
    * S* H( \/ n- ~, c. a
  2. #include <stdio.h>
    ) n* F/ g5 O$ Z7 w! L2 L$ k
  3. #include <string.h>$ r- e2 v; L2 v; y5 K
  4. #include <stdlib.h>
    # N$ m7 f: e) Q- f& m
  5. #include <strings.h>
    ! b& _' _, D1 P# F' \. c$ s  \
  6. #include <sys/types.h>
    8 E3 d( W0 ?  T& ^- L# e
  7. #include <sys/socket.h>/ r) d, U+ i/ |* J$ U8 u% Z
  8. #include <arpa/inet.h>- t/ z. L; R- R; B* L. i. Q" z
  9. #include <netinet/in.h>
    # w+ d( Y! j& F1 A1 \' R
  10. int main()5 p6 h& f. q. B  n+ j% t
  11. {
    5 z1 ^4 v2 u- A( f, v
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字  S& t% T4 Q. ~' j- ]1 f
  13.         if (sockfd < 0)
    3 g3 n  v1 t' H% g# S+ I
  14.         {. A3 m& L; R; \9 @3 s" z
  15.                 perror("socket");
    0 ]5 o3 v& D! z' `  g. C
  16.                 return -1;
    / ?7 e! g, R( O' R! y+ M/ j8 j
  17.         } //创建失败的错误处理: ^; e# [1 f4 V. I3 [7 ^
  18.          printf("socket..............
    & k" b+ m3 T6 ?" [/ a* G
  19. "); //成功则打印“socket。。。。”: ]. {/ O  F9 ]) n+ u/ Y/ N
  20.          * l8 V5 B/ A9 X
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    2 ?& H* U5 A: q$ e% f: d$ G
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    # Z/ l6 K' W  L2 k. t: C
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型# F6 ~* s4 r/ S
  24.          myaddr.sin_port                 = htons(8888); //选择端口号5 {4 B: I3 {* Z! L  G7 q* {2 Z3 \+ c
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址( X$ @, N6 n- R5 |9 p

  26. & Z5 e$ c/ ^, f3 |1 r' m
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    " M9 p  T4 g# n
  28.          {
    0 o+ x1 H7 {% M4 |2 b) g
  29.                  perror("bind");8 Y3 C  _9 T# J6 X7 M  h
  30.                  return -1;1 C  n2 p2 Z# l$ @$ A; p! }& C
  31.          }: p6 i+ r0 D0 R9 B6 w) t
  32.          printf("bind..........9 i! H& i) j1 R( X# o( L6 G
  33. ");
    + L9 E+ c; v: y

  34. % _5 [  x& h$ M& k0 J* q. n+ j& {! O
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    3 ~7 f9 _# |1 O) g
  36.          {/ n/ N3 W0 I- F% x
  37.                  perror("listen");8 \' w* _! G4 z+ a1 R
  38.                  return -1;
    , \* V+ f' `, g
  39.          }  j3 u, c' y5 i4 M$ o0 Y( ^  {
  40.          printf("listen............
    $ T1 W9 |; O) B" \# m1 y
  41. ");, Q+ S; W; k0 v. H6 l# r7 M1 `
  42.          
    . J- M( [' C5 e1 X
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    4 o+ j8 d! J: J" R3 u* G
  44.         if (connfd < 0)
    3 ]/ V# b$ Z* L8 t1 u4 H, d7 M
  45.         {
    ) l0 |4 E1 P) c/ B# }  R, }
  46.                 perror("accept");# f) a+ z. m# i" Q+ l0 b& K" a0 l
  47.                 return -1;
    # U  g% ^1 s5 D# `1 j2 G
  48.         }- m6 S9 i$ n3 W) \
  49.         printf("accept..............+ g0 A7 _' Q/ W5 O: J
  50. ");- j7 M# [7 z& m6 I; z
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    9 {+ y( {% o$ v1 I- W: n
  52.         int ret;' K0 n% X3 ]8 m- |1 {  ]! n
  53.         while (1)* P) v3 P- k2 p. l6 I  W  L
  54.         {
    ' n6 d; M. j8 W& c# o) h
  55.                 memset(buf, 0, sizeof(buf));" T( w# e) g9 Q$ R
  56.                 ret = read(connfd, buf, sizeof(buf));
    ' T; Y' U) t0 m1 t  r  y
  57.                 if (0 > ret)
    ; D/ D/ K+ h2 n! s. j/ I: l
  58.                 {0 H' l3 E, ?& N0 B2 Q  j' ~
  59.                         perror("read");) m. G' O9 b: D1 s# L" q8 {
  60.                         break;
    * m/ }' n3 [" s  p( l: R* N
  61.                 }//执行while循环读取数据,当! V4 b8 E7 h* x/ Z
  62.                 else if (0 == ret): Z3 g0 l2 f6 G; h5 n7 }7 A
  63.                 {7 l( W, q' ~- `
  64.                         printf("write close!5 {: K% l6 @* d1 g
  65. ");/ V8 p; L7 M1 _
  66.                         break;! a$ ]! E0 `( U& p. N1 P6 m
  67.                 }
    ( F2 }* n( |6 p
  68.                 printf("recv: ");
    1 n  S$ L) u" f% U+ _, [& b3 z7 h
  69.                 fputs(buf, stdout);//打印接收到的数据
    6 @* D. y/ J! R4 {4 I9 x; C
  70.         }2 k7 I  o+ ^7 f! I( v' X. w
  71.         close(sockfd);//关闭套接字' Y* s4 t: \/ U( i+ i
  72.         close(connfd);//断开连接# S- Y( x/ \" `4 |5 e7 ]/ k6 c) Y+ p
  73.         return 0;
    # w8 l$ ^  U" d
  74. }
复制代码

3 p0 K- |' Q1 n; X) ~- H
: k" Y8 u- X9 {/ d
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释), I; t) x2 T4 C: N
  2. #include <stdio.h>- S9 F1 X5 M: T# E# Q. p, P6 i
  3. #include <string.h>; ~3 W5 y2 T& b' |3 a' E7 |8 I
  4. #include <stdlib.h>. q7 L) o8 {& B8 Y# R
  5. #include <strings.h>
    - i, d  D6 q" K% m9 [
  6. #include <sys/types.h>! n! z' |& Y5 d+ n- E
  7. #include <sys/socket.h>; V  b/ r( N+ y) R, B! |1 t" o
  8. #include <netinet/in.h>  i; ^5 [; N6 C9 B) ~3 T
  9. #include <arpa/inet.h>( q6 c: M( ^+ v: G8 J
  10. int main()
    : o* D' _& N) }# d
  11. {  ^% P* r3 d, o7 f+ S- C# J: M
  12. int sockfd;
    0 s* G* y& D% S; [4 x
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    , Z9 T; o. Q* B* [" q) C
  14.         {
    8 z; [, o2 b/ j& X3 r( {8 N6 [" g
  15.                 perror("socket");
    " H4 _+ R! I; P4 f5 C
  16.                 return -1;4 l$ C& Z) V0 p- g
  17.         }
    + U' F  @8 A4 K$ a4 L  G% h& h4 W: _
  18.         printf("socket...........( ?4 T+ y0 h4 [' N5 s! m
  19. ");* n2 S$ [2 W7 F, K
  20.         8 [9 Q8 K% S, }/ v& b
  21.         struct sockaddr_in srv_addr;  l7 b- m- P6 U% P
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    $ J6 B6 ^0 r- B. k/ G7 k& l3 K
  23.         srv_addr.sin_family                 = AF_INET;, L7 Z5 |# V9 ~% v/ P8 q
  24.         srv_addr.sin_port                         = htons(8888);
    4 m( C2 v- E$ {
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    4 Q5 E  [: B8 m& I$ a. k
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    / U3 {4 G6 u+ D+ W- a2 K/ U7 @. c
  27.         {
    2 b- D* Q! q6 V8 Z+ x3 m. F
  28.                 perror("connect");
    2 t$ t8 D' |0 F3 O
  29.                 return -1; //exit //pthread_exit
    - O' x: x' z8 }, ~3 h
  30.         }
    ' C' M  V' p; w& Q. g! Z9 V
  31.         printf("connect..............' _* v8 k4 F. t7 V) H4 O9 i
  32. ");2 |) d  ?9 S1 Y4 z* B
  33.         char buf[100];! w: T; w/ d7 n( L
  34.         int ret;
    9 G2 o1 g  f4 P
  35.         while (1)* R  _- Y* i+ ^6 T7 h/ T9 }
  36.         {
    8 Q# Y# p  }# C+ G* y( a
  37.                 printf("send: ");
    7 u2 F# h" U: s# |
  38.                 fgets(buf, sizeof(buf), stdin);
    4 Y0 @2 W, Q4 G! l' [1 h; B  q
  39.                 ret = write(sockfd, buf, sizeof(buf));: z4 |( J; T' `$ i
  40.                 if (ret < 0)- L- r" S  H. _7 G
  41.                 {
    ) J" q3 f% J. }4 B) D
  42.                         perror("write");
    , Z$ X/ k7 _& M0 f
  43.                         break;; t4 }; Q: }7 p4 w7 |/ L6 Y
  44.                 }
    ; m* W! A( A- p7 \5 I
  45.                 if (strncmp(buf, "quit", 4) == 0)
    9 j. A; F) D5 C$ y, D1 w# j
  46.                         break;
      F& ?7 y8 q/ I& ~9 V/ }: c
  47.         }
    - F0 |. D& I9 S0 G3 k
  48.         close(sockfd);
    4 F) J6 g0 D/ z9 s5 ^9 C. Q8 {2 o5 l
  49.         return 0;7 k& r( a) c5 y6 S
  50. }
复制代码
0 }1 }: u* ^$ u0 Q" n- V
5 f: ~, L- q% r7 @' X
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 03:16 , Processed in 0.059088 second(s), 23 queries .

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