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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
$ d5 a; t/ ?; d5 n! F9 x7 E9 K) U  b# L/ e: ~6 H6 m! K

$ q$ O  b* W% f$ Z) l2 S2 p5 hsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。% ^( q; Q) M: G: C. i5 x
# M8 s. c; ~: z! R
1 I" k2 L2 x2 [+ E- _7 |1 V
TCP协议
' Y3 J# D7 D* h6 eTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
; h8 G: v: P% u7 v3 S
# k: ^) \6 D  j+ d

1 ~# ?6 C4 i$ c2 R关键词:三次握手,可靠,基于字节流。
8 m6 D9 q/ q- C8 j# j" q
# Q& E2 W, f1 ]# S

5 x- E7 t5 e- R/ O) F6 U可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。; p) B' C3 q, G
- Z/ B5 K8 [0 R% {+ x
TCP服务器端和客户端的运行流程
2 a9 m% ~3 h# s/ p4 v( k4 S# H( C& m2 z3 ~. y* ^
' N$ M. E/ @4 Z+ a5 x* K/ F
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?2 A4 i8 g+ F0 h. X" z+ t
  u3 W; F  g2 y
4 }* I7 Z" f5 ]8 o
1.创建socket
, s( f0 p% y. m0 N socket是一个结构体,被创建在内核中
5 s6 p  t. \* S! A6 T4 G sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议* R. i$ ?% E7 U; D+ g

3 ~0 J' v, V  P- W  G( B+ ~2 V+ \

$ h. B4 X3 I- T9 x& P5 W+ E  o- S% N2.调用bind函数
9 {- n! W& W+ P. N# T' ? 将socket和地址(包括ip、port)绑定。8 m: p5 y8 {3 d  q
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序; O( Z$ _& N- w/ G5 c
struct sockaddr_in myaddr; //地址结构体0 I% W7 x+ ]. h0 _6 D% y
bind函数% i5 w$ q$ x6 K5 d* ^' p
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% r% @. e: |& L5 C
. I  b. n% ?# {) x/ x

  B9 P* s5 D) @/ h2 w2 D3 z3.listen监听,将接收到的客户端连接放入队列
- M; p; j: s5 l$ Y; x0 M  W listen(sockfd,8) //第二个参数是队列长度
% a8 v  W* J( R' o. z, M2 V: f, C, F! _8 [9 B7 k

9 \) z/ G3 \! B7 ]- K) P  }5 w4.调用accept函数,从队列获取请求,返回socket描 述符. r5 T$ m; N3 z+ l4 s
  如果无请求,将会阻塞,直到获得连接4 s9 J2 x2 {( ]: l  O9 [
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
" `0 u0 ~% h2 `0 d  h: m6 \
. w+ F- E6 O9 K/ k8 v# ^0 b
& C& x% b! m5 }, a
5.调用read/write进行双向通信
6 [$ k- f# I- u, q1 @, P* _5 b" h, H6 g0 c
; |2 {& t8 I  {' D. I/ R
6.关闭accept返回的socket: r# S+ Z0 w' ]
  close(scokfd);7 c" Z4 X4 G& L4 W- L& J, f
) o( O0 G  A& y/ i/ w# A
- Q  w8 M; Z0 `# @( h

; V. r& f* X* a5 n
2 ^9 q% |9 D$ [. V) b
下面放出完整代码
3 k" q6 y5 a, @, a3 S0 I# f6 P
, ]$ N9 |3 ]& s4 F' C
  1. /*服务器*/) i# }4 c/ a0 E. x4 K/ i0 P/ s8 M- @
  2. #include <stdio.h>1 C$ Y; t3 S% {/ s9 u4 |2 M* C
  3. #include <string.h>
    # c2 t9 I/ r7 F: G1 Q
  4. #include <stdlib.h>5 n. P5 g+ v, g7 d8 z6 x+ ]! z- @
  5. #include <strings.h>: i1 s. I; S  {) U
  6. #include <sys/types.h>0 C: b( `6 G3 ^2 j! J$ n: j  d
  7. #include <sys/socket.h>0 c! v8 j2 ^& F6 I9 S4 ?5 P
  8. #include <arpa/inet.h>0 f8 K0 D) k4 ^7 O( z
  9. #include <netinet/in.h>$ [: v9 C7 `% z( `# B: A7 x
  10. int main()4 r: ?0 r' X3 J
  11. {
    - i* }  S* j1 r
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字) Z' d2 ~0 F* r9 k
  13.         if (sockfd < 0)
    & s; d  j# g+ t2 {
  14.         {
    . h$ b0 f* T3 l- ]$ X7 z
  15.                 perror("socket");
    3 j$ D) Y7 p: i- d
  16.                 return -1;
    1 v& U. x7 x5 ]
  17.         } //创建失败的错误处理
    ; k- S- Z1 K, ^, c9 C
  18.          printf("socket..............
    " z: [$ M0 n$ y. q3 [0 J
  19. "); //成功则打印“socket。。。。”; `& c0 t# D+ e; O8 h
  20.          7 E; r- U. c3 Z; E; G- o+ Q
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体7 I/ ^* X" x9 P% ?  A" G, O
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    , m- D7 y* q9 R5 o
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    . A, t" g( ?4 _' _+ y- M5 {6 J
  24.          myaddr.sin_port                 = htons(8888); //选择端口号4 ]! g2 a' e$ p3 ]* R+ n
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    4 H5 `! G. d' g3 L1 I- J
  26. % h+ v, z; a4 j+ r7 S3 A
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字8 ~0 @+ f7 y% j& K3 u! ]
  28.          {0 F+ [' w1 F( h: D2 w* _2 c- w8 V* p
  29.                  perror("bind");5 K6 W: z) \5 _; S1 ?- U" a
  30.                  return -1;& \% k. y4 M- K- |% R' I# D
  31.          }
    $ U, N: F- W& ]
  32.          printf("bind..........
    ( k' A3 I( s3 K7 V5 J
  33. ");5 e. Z/ i0 K' O

  34. . C) `  W4 `0 D; L) m9 s
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听* S2 Q: v; s; ]; P4 q/ I
  36.          {
    1 o& P/ o8 \4 l( Z
  37.                  perror("listen");
    6 H( o) k; n# ~2 A* t
  38.                  return -1;
    9 b# i3 D: N* R) a
  39.          }
    ! s2 {6 o" m0 C! w1 l* H! E
  40.          printf("listen............
    5 A# T" z% d. l
  41. ");
    - q7 T9 Q/ h" k! r. q
  42.          
    9 U! b  m) `  `
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求# n' M5 o5 X  p- j/ S' Y  D" M
  44.         if (connfd < 0)
    7 i  z/ n  ?5 C/ y
  45.         {
    : i: X  N; ^8 e/ u
  46.                 perror("accept");+ O6 c1 A* _) i: j0 F7 E; D
  47.                 return -1;- t$ ]" p/ B9 W7 [0 u$ s
  48.         }
    3 H4 w' z5 X& B  o# `4 w' d
  49.         printf("accept..............3 L$ J6 R  Z  R5 D- {4 b
  50. ");
    : o5 E* [9 L# C, d& l) M; x% u
  51.         char buf[100];//定义一个数组用来存储接收到的数据3 D: j, m9 d9 ?; g6 F# k
  52.         int ret;
    . r4 {& V* n3 L; G  t6 E
  53.         while (1)
    - ~7 R7 }) c) W+ o. D* [
  54.         {
    ' q  Z3 @6 ]! _3 Y
  55.                 memset(buf, 0, sizeof(buf));* z! v& u2 \0 E, B! D  {4 I: e
  56.                 ret = read(connfd, buf, sizeof(buf));2 l! ?5 r3 p7 S( k
  57.                 if (0 > ret)! M; _# B/ A" i1 A
  58.                 {
    9 J8 ]& t1 m( ?  {4 H
  59.                         perror("read");
    & f+ j, ?3 r7 X* j3 M$ f
  60.                         break;8 r" i* E5 r- A  H
  61.                 }//执行while循环读取数据,当
    ; Z2 w- q7 n2 k$ u
  62.                 else if (0 == ret)
    & I( c7 F2 q/ c& {) |% V, ~
  63.                 {
    8 Q) P. M/ a, y) h
  64.                         printf("write close!2 l6 l1 H4 |3 h: ]
  65. ");
    7 B0 V7 {1 S6 ]% D$ E4 O
  66.                         break;& m+ j; n( x, K& c) ~% L4 W1 u
  67.                 }
    ! a' V2 O- r( N
  68.                 printf("recv: ");2 r# l. ]3 b; }4 S% x$ v5 m% W
  69.                 fputs(buf, stdout);//打印接收到的数据( d, ]' @) D9 s6 j6 s" a1 V
  70.         }  y$ Z. }9 Q% e( F
  71.         close(sockfd);//关闭套接字
    4 b/ |. g1 Z) n, C9 s
  72.         close(connfd);//断开连接( b( l2 c9 f; J9 @$ j0 e+ I+ i) p
  73.         return 0;6 O1 w9 w$ m* ?
  74. }
复制代码

. T, q6 R& P5 A8 R- X4 S& @6 |) z. s% Q" b' D
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)0 v$ l. V2 p* b
  2. #include <stdio.h>
      }+ t4 C; W, I& w0 k  [
  3. #include <string.h>1 \+ B. u0 h. T/ B  N4 ~3 ^
  4. #include <stdlib.h>5 a! T1 ]: l/ f9 P# K2 b1 q  d, S( O
  5. #include <strings.h>
    2 n/ O) C" S3 |$ D
  6. #include <sys/types.h>
    : t# Y+ k5 M# n* L
  7. #include <sys/socket.h># m- h8 ^3 u5 @
  8. #include <netinet/in.h>$ T5 U% P0 `0 b
  9. #include <arpa/inet.h>
    . j# k% M9 K+ G, E2 X
  10. int main()
    + ?% M: @6 j* l0 e
  11. {$ G- B7 a, g; N6 [" Z; k- M
  12. int sockfd;* v3 t- N5 Q2 Q% c
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))$ O. X1 D  m! a
  14.         {# N8 M' H$ F+ |3 D
  15.                 perror("socket");
    9 v( M: x& A* }3 i% q" T
  16.                 return -1;
    + r, g0 X1 C. X
  17.         }2 M2 u% d: J0 u
  18.         printf("socket...........
    0 M" T  ]1 c+ ~% [% j% s! L
  19. ");. K8 o, M; }4 B, r
  20.         / y4 |- Y: H0 k: x# Z
  21.         struct sockaddr_in srv_addr;
    9 |& S. |# m0 w) i7 ]
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    ; |  h! H/ h; O$ O5 s
  23.         srv_addr.sin_family                 = AF_INET;4 q+ @: B2 L/ |4 J/ o
  24.         srv_addr.sin_port                         = htons(8888);+ v" V" a$ T" |
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
      u+ D2 E" B5 S: H4 t; I/ z
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))+ A+ h0 i0 L! R3 q
  27.         {
    2 h- _3 e3 N- A$ D* f+ a8 }
  28.                 perror("connect");; h$ J; G$ E5 Z" s
  29.                 return -1; //exit //pthread_exit5 `& j" ~+ x1 B* k7 j
  30.         }) \' u  k. b( Z) \# I' W* `
  31.         printf("connect..............
    : M4 Z3 T3 o( y
  32. ");/ I8 M& T# E  u2 ]7 e2 R
  33.         char buf[100];2 `% o# @$ K0 Y- k  c
  34.         int ret;
    ( R% Y$ O% t0 z! @7 N
  35.         while (1)
    ( {) {9 Z+ E/ B7 i8 y% r  G
  36.         {
    7 o# \4 Q) h& B
  37.                 printf("send: ");& Y6 X) F' x3 Z8 z- ^, ?; q7 r5 m7 O
  38.                 fgets(buf, sizeof(buf), stdin);; `& N* l  {- u# O1 }
  39.                 ret = write(sockfd, buf, sizeof(buf));8 z! f6 w- g$ @4 M
  40.                 if (ret < 0)
    6 A3 g: d2 C" T0 s
  41.                 {
    ' R) D; B/ a1 Z8 K: h0 \0 |
  42.                         perror("write");
    : ~% t, n7 o: o- }( P; Y
  43.                         break;
    1 ]# ]8 G1 O8 q7 k4 _% N, |! Q2 q
  44.                 }
    : o. C5 J+ @( t' s8 k2 @/ D! `9 P! V/ O
  45.                 if (strncmp(buf, "quit", 4) == 0)
    6 h/ B9 W; S4 _9 [
  46.                         break;( E5 F) e4 w' \. R3 _+ E
  47.         }
    - C+ h9 o" w/ s
  48.         close(sockfd);
    3 a* k1 h% f5 F! {$ q9 L$ s
  49.         return 0;0 e8 y) M* l( b. X% x0 i
  50. }
复制代码
9 f; i8 z0 e% h: Q& f& [) D/ R

( O/ Q$ P+ a/ X! S9 w( \
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 00:40 , Processed in 0.121817 second(s), 25 queries .

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