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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
! t+ {* O$ a5 ^6 G" c, L; G2 Z+ o% h" W2 l9 I* ]; R! U# w3 X1 ~

8 P2 Q: ^3 C( z. ]$ O0 _5 ^socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
3 d+ @) b5 Q1 l8 Q7 u2 f# ?8 ^( D. N4 C9 \) i* e# Y$ T
; [7 y: E' o6 s& K3 K7 m( i- X! K
TCP协议
6 ?3 K1 ~: J0 s+ G$ uTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
& m7 l0 f, F: V& S- k3 W
, m# c0 r7 `" M" J1 W# F8 ~2 C1 Y1 ]
! t* g8 g9 Z, l! s- K: \
关键词:三次握手,可靠,基于字节流。% c; |- Y" U) j# U5 K4 ^

- j: J! m) @: {$ ]6 w7 E. [
7 D8 W% A4 O8 a% S# [
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。+ F! u8 R; U: L6 |# [2 |$ i

- S- H( }* n) p. l0 P& A0 S0 i, qTCP服务器端和客户端的运行流程
  {- X% \( N6 p/ S( i+ S% u& B, K/ z; `' C* k
% p* p& u3 S  P$ h/ ^# A
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
8 y6 w! u# Q3 y' t, e
7 h% L4 u; D. A, V/ l3 W

" C- Q8 ]- q4 [9 A! v: i1.创建socket; ^% g4 d) A# q1 a
socket是一个结构体,被创建在内核中+ s9 b: s, }' V& i. H+ C! Q2 c
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议$ A3 g0 Z0 D# @4 V2 P+ c/ ^4 h

+ t( l0 e) M$ B, H* ~0 z: E
  {3 X; g- e2 T9 x, T; A
2.调用bind函数: |! {6 N. Y- z3 C: A# D, f0 b% E
将socket和地址(包括ip、port)绑定。
. M  f. D6 @) b/ n! U! O* p 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序# a+ u' P# H( y
struct sockaddr_in myaddr; //地址结构体$ ?6 K6 g7 b! V& d0 c7 R# A
bind函数
: D+ z! n# n$ J! W. w7 L3 u bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)): g$ s: e2 R, I

, C$ W8 f! e- m  x! [* A

# B6 O& B2 T2 z( J4 T% p& H& ^3.listen监听,将接收到的客户端连接放入队列3 f0 \: J# N8 H! N9 N8 w
listen(sockfd,8) //第二个参数是队列长度
% N) \, y0 f2 Z9 j0 Q- K/ B
5 ~6 Q, _- V% ]$ T: P# E# X

; |8 r: b/ \( b% C8 A& o4.调用accept函数,从队列获取请求,返回socket描 述符0 m! R' R, Q6 W; H
  如果无请求,将会阻塞,直到获得连接- }4 `' x. k6 a9 q4 B* X  Q# L  I' \
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
) C$ C5 J  k3 f9 ?' K7 Z+ i# r$ a- ^+ W" _. E# Q2 k. O/ s
, k9 d: w. h  [, `, \! T" h2 V. P: @. p. }
5.调用read/write进行双向通信6 d5 ~6 h7 {: ~1 y+ @; P$ U* f
- D( [- J0 J* Z1 ~

+ W( G# E7 a3 k# l6.关闭accept返回的socket; s+ @7 O) h8 a  b; x
  close(scokfd);- `9 W% j2 i( m0 Q, o7 c

4 a& v+ x6 S8 q, N7 I
0 a6 w' r* E1 i" r( t
  |. H' w( l1 D

3 o" Y1 H5 |! \- T下面放出完整代码+ f4 O2 n1 {; U5 J

6 f! S8 I/ m2 X( g% z
  1. /*服务器*/. I2 V! [9 @& A0 N' n5 v( h& _
  2. #include <stdio.h>8 l5 i2 u6 q+ b/ K3 l/ J
  3. #include <string.h>
    ' v" |$ M; c3 b) D. d) l/ a! I
  4. #include <stdlib.h>
    5 _8 e9 [  t$ w
  5. #include <strings.h>
    + L, h( B6 g0 @/ |" E$ L0 \. N
  6. #include <sys/types.h>
    5 `$ o: ^4 n* j3 Q
  7. #include <sys/socket.h>7 s+ n1 d* p5 d; g1 V# u
  8. #include <arpa/inet.h>
    0 k. D& f: ~& w5 b4 t/ ]
  9. #include <netinet/in.h>
    6 i5 z6 p+ S- i+ v8 [# S& P' I
  10. int main()
    0 |. i# L4 B( ~& g$ ^) ], |5 Q3 M
  11. {
    ( V, N$ F1 s: G$ g4 c
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字" ?$ d% K. u) m7 U' g
  13.         if (sockfd < 0)
    : Y9 T9 q# m/ A6 D1 Z
  14.         {& H# @# E+ B$ [/ Z5 b/ E
  15.                 perror("socket");
    * f; F# s( z) l
  16.                 return -1;5 ^+ Z3 c; \  ?" I5 N+ u6 ]
  17.         } //创建失败的错误处理( k9 H# E  N4 Q/ R7 O
  18.          printf("socket..............) B$ S- L- _7 A# x! ]) |
  19. "); //成功则打印“socket。。。。”
    2 N# L( I; B! Y8 I
  20.          
    5 X  t( `' j9 E  F0 f9 E8 t6 z& o
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体4 a$ F/ |- S; y/ m; B) ~2 G8 d
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    1 l3 r+ ?/ ^  m) v7 T0 I0 b
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型" D8 q8 M, f8 j+ B$ s  ?8 @
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    , a! S9 d1 m4 P( l5 P( \$ N
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    - \% N* k% w4 |7 T5 |

  26. $ V# G  a1 E  w+ t( `
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字  C& e: g$ {  V* v) @  n8 o2 R
  28.          {. Q6 C1 P& ^+ z/ ^7 P8 K8 t; N" w
  29.                  perror("bind");
    , T8 R! m0 {  p9 L: R6 n+ R+ ]
  30.                  return -1;
    ' P; f6 c# _" O9 ?' Z
  31.          }
    ! g& p. D6 K# C% x9 @5 Q: j
  32.          printf("bind..........
    7 O. I+ G" b. O
  33. ");
    5 E8 i$ @8 b. ?% v; N* l0 X

  34. " Z+ u' v' A" K- x4 Z
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听4 m: b9 ], N( j
  36.          {, }1 f: X9 K8 |
  37.                  perror("listen");0 T3 T5 `/ t: F# D: _  P/ r1 B& K  h
  38.                  return -1;
    ' K4 e+ W8 m0 Y% O
  39.          }; N5 G! _+ Q5 z9 \
  40.          printf("listen............
    / t# W0 E& q4 ^7 B+ T; [
  41. ");
    7 J4 Z" ]9 ^) U( y# H
  42.          ) p+ l) ^/ L7 ]/ V# F
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求, s! @0 `( s3 G3 t) e+ O5 R  W6 L3 w
  44.         if (connfd < 0)0 ~  }% c  N7 d3 S2 }8 }5 z5 X
  45.         {
    4 w, R1 K6 Q; ~) E1 S
  46.                 perror("accept");
    3 k' F5 V4 q9 E' P7 ^
  47.                 return -1;
    # L0 P  L5 m$ K" V& X. a& x
  48.         }
    # H, O) C* [, h' H7 f
  49.         printf("accept..............+ |! j; B% L# Z5 F* m5 l
  50. ");
    ! h: g9 V% ]8 {4 P1 l* p6 [
  51.         char buf[100];//定义一个数组用来存储接收到的数据, g8 k/ ~* o" N. i* ^
  52.         int ret;! |; d4 v2 M/ p& r4 I% ~( e
  53.         while (1)
    2 a2 P0 D5 z5 J: T: C
  54.         {% J, K/ d1 ?7 F+ l- K2 S: U
  55.                 memset(buf, 0, sizeof(buf));' m' o' F. n+ J) I( E
  56.                 ret = read(connfd, buf, sizeof(buf));6 V+ o* D# E: X1 N/ h
  57.                 if (0 > ret)
    , x' ?! [+ e. `! K
  58.                 {: [0 b9 j- E, _: I# U
  59.                         perror("read");2 N. Y1 e; [3 \! s
  60.                         break;6 ~( o0 a1 \1 N, p! S) @
  61.                 }//执行while循环读取数据,当
    " w; B- I. W( p; ]9 p8 N( h
  62.                 else if (0 == ret)
    % j/ l/ f8 G! z( }! s2 l! F
  63.                 {6 [9 i6 ?. T5 ]* Z2 W
  64.                         printf("write close!
    $ ]* d2 q4 ?! I  }! _3 J
  65. ");
    6 ?8 I; \! E9 }- w( t  b. Z
  66.                         break;7 n7 ~' i, {# R+ [1 E
  67.                 }
    " |# u4 U2 \6 I4 j+ a3 X7 i
  68.                 printf("recv: ");) M% ~3 Z$ `( l9 M2 O5 c
  69.                 fputs(buf, stdout);//打印接收到的数据
    ! L& O/ w( [7 G+ ^# }
  70.         }. c" H' Q9 P8 Q8 H: n3 O
  71.         close(sockfd);//关闭套接字4 s2 [! N; u" R# R$ r
  72.         close(connfd);//断开连接4 i& [5 K/ i# [9 a4 U3 M# y
  73.         return 0;
    8 a( Z) B: D& I8 L4 ~
  74. }
复制代码

$ B" }5 m3 i5 v. F9 X
4 C5 b9 c( _% Q4 l( B) A$ U
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    6 @8 h) F* a' D% |9 D( r# x  K; t
  2. #include <stdio.h>' p9 \$ R6 h+ c/ S% y
  3. #include <string.h>
    6 y' L! b$ a6 `# k- t* b
  4. #include <stdlib.h>: S& `; W/ M4 t$ v( A6 z. ~
  5. #include <strings.h>
    $ G5 g5 B, f* I+ i# u9 e
  6. #include <sys/types.h>
    ( k* G; n7 ]1 R3 p) ]" k+ z; R
  7. #include <sys/socket.h>
    / O9 @- [! g5 B& b0 \
  8. #include <netinet/in.h>
    3 A+ E" H9 M' r2 S6 J& U
  9. #include <arpa/inet.h># ^+ {6 W( }% ~+ N" d
  10. int main()
    ' M" ?* l( u0 g5 @7 {
  11. {
    $ g  a2 g8 I, r. h
  12. int sockfd;
    + o0 s9 i: K) }( Y4 k
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    ) m8 k' c( H5 o: q8 l
  14.         {0 A$ w% ?. w  k6 s7 _! [6 \
  15.                 perror("socket");$ s& a  F( k6 c0 X* ?
  16.                 return -1;. x" G1 g" v) N$ k2 R8 r# D
  17.         }
    . S& F; q8 e- @; }! E; Z) i' F# e
  18.         printf("socket...........
    / m% Z* Z7 O1 D( ]" t; K, Z
  19. ");2 l0 M# f2 S9 q) i/ ?
  20.         
    7 ]% s! ?' @7 b4 a2 Y$ k3 W
  21.         struct sockaddr_in srv_addr;5 \: B: B4 a% n. A6 I/ `( L% p
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    & s4 j9 {( s5 Q% m6 v
  23.         srv_addr.sin_family                 = AF_INET;; B- B; J( V2 X( G
  24.         srv_addr.sin_port                         = htons(8888);- s9 ~) v0 J2 Z% u, q
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ( p6 {' p: _! l: p
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))9 Y; P2 |# c* g: k7 N7 g
  27.         {
    , s) O2 {1 i% \2 K! L- n# s8 b
  28.                 perror("connect");
    1 q' t* W9 L: Q" W3 `( C
  29.                 return -1; //exit //pthread_exit
    / ~' J. M* n- y  S0 X
  30.         }  r) X8 Z+ ^; H+ v
  31.         printf("connect..............
    3 N. h+ k8 t" J5 q. }+ b+ Y+ k
  32. ");1 J( E$ W% q; s4 U. ?" N
  33.         char buf[100];- k2 i" O; i  G5 L- u+ ?0 u. `
  34.         int ret;
    ) a3 B  R/ z, Z% R& a2 e
  35.         while (1)
    0 r. [) Y4 U  W  k7 ]
  36.         {9 S+ e" l. L1 t' W3 M+ Z
  37.                 printf("send: ");
    - p/ t1 ]7 V: x
  38.                 fgets(buf, sizeof(buf), stdin);6 @' d' d. v' V- r0 o
  39.                 ret = write(sockfd, buf, sizeof(buf));6 K3 r# ?1 g# D! r, U
  40.                 if (ret < 0)
    - e6 R0 z. r9 t. j  O- A
  41.                 {- e6 N+ S, V- A9 a2 F
  42.                         perror("write");4 u- k) c1 R; ^9 w' v2 O
  43.                         break;
    5 }* u3 n/ [2 ?! @8 B8 h2 _: @
  44.                 }! q1 C' s; y) z, @. H! t2 ^3 H2 p
  45.                 if (strncmp(buf, "quit", 4) == 0)' F  T3 N6 x1 v. N* R+ F  v
  46.                         break;) F5 B! r' D* S2 f  j* `! W
  47.         }
    5 ?5 w# B. W7 }% Z% g3 ~1 U
  48.         close(sockfd);& }& b' G$ ~1 s: F' e3 B0 ], m
  49.         return 0;
    ) I0 V" n! @: l* G! n. n' S
  50. }
复制代码

8 W- _% ~: K1 @3 h
! b8 q6 Q, s2 x2 o: C( [
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 01:25 , Processed in 0.122938 second(s), 23 queries .

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