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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
) A9 N: i; O3 B" {% E! p1 v% @' B7 ?( F- q

2 ]% D+ p6 _) r  v; v9 W$ Qsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
& t* X7 S& B" |' y& g1 j3 `, w; T. L7 [! ^
% h1 c4 I6 p% z0 K
TCP协议
6 z+ [* l  c: WTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
( o& X4 i1 L, k% L. u" P" a
- C0 B2 g8 @# k0 P

' ^- I5 ]* R% p2 E4 @- O3 D关键词:三次握手,可靠,基于字节流。/ r, ?6 H6 O! Q9 p4 c: C: D0 u+ Q& m

8 E7 m" u! D" p% W4 k# x0 h4 S# a- k

% \% ^! J5 Y& a% ~: N可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
$ Z4 O3 Y  Y6 r 0 {% T8 @# U, c) P: @4 G
TCP服务器端和客户端的运行流程
" V$ T9 |; {+ K: {. N/ ?! R0 m7 {' X1 R7 M* n9 l  m
1 z- P& \8 q1 C
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
" A$ l! B. c1 _; E) ~; E. e) ^; t) d" b- |  S+ k$ f

5 A1 Z4 l, Q( M: q1.创建socket: E$ T2 ?# X9 ~! t  I0 Q5 y
socket是一个结构体,被创建在内核中, U  J: u5 e( ^; o+ d
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议/ y+ a) x6 W" t. [8 c& {) S
4 \4 a& Z2 s4 a

# H) W2 p) S/ m1 h# C2.调用bind函数* B& `* N/ X: N& ^% f. c$ x
将socket和地址(包括ip、port)绑定。" s9 A2 ~: t+ y/ ~& s) X
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
2 \( q) I4 K) w  W struct sockaddr_in myaddr; //地址结构体8 D& N! H  B. R1 H1 _+ ?6 m
bind函数
+ C! c/ e! A5 O) ]* C2 F& n! w bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
/ ~1 P( r, @6 Z3 n5 h5 Q7 R
$ q4 h$ r& K+ @8 q+ {  N) m& }
  a0 t& n! R% U( Z$ q7 F; _# L
3.listen监听,将接收到的客户端连接放入队列
8 [0 u7 w- d) M5 R listen(sockfd,8) //第二个参数是队列长度
" e& Y/ w  V. s* x
/ I; G* m& X; D7 Z: D" j; @

6 C! ~: z1 i+ W* @; n. f7 r2 O4.调用accept函数,从队列获取请求,返回socket描 述符  l/ X# X7 @6 ]" d. d
  如果无请求,将会阻塞,直到获得连接3 Z5 G) S" c, ^/ ~  }+ q
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数! w: o, D. A0 C# t7 w2 {- e

- V+ a" K, h" V7 q; t' K
8 e! X5 P" T; E8 }7 E( Q/ F
5.调用read/write进行双向通信$ k5 F! a" L" x1 ~4 C
$ n4 y- h% `3 ~& P- g1 d
) |9 Q+ Z, H8 l5 `, k
6.关闭accept返回的socket  ?! g( `1 z8 ?/ b
  close(scokfd);5 ?+ A+ [+ v( E+ U  C" A
5 u. O: Q5 M: [9 d1 a

; u- b/ E3 u3 C* Z: t& J5 C% N4 W8 x* l' c
3 S: f, k, B" y
下面放出完整代码
# x3 ]1 w  k, F7 P* Q& G  C- _" b% L1 V. S
  1. /*服务器*/+ q" z6 d8 v' A! ]1 ~$ |
  2. #include <stdio.h>* z( ]7 D, r9 v8 I. Z0 D
  3. #include <string.h>) ~( \. p9 T; Y
  4. #include <stdlib.h>
    ; H; e3 ]( T2 `9 I$ `
  5. #include <strings.h>
    + i& K( v8 i6 a1 q8 O' t" x6 h
  6. #include <sys/types.h>7 O+ B; P8 I( g% W
  7. #include <sys/socket.h>
    $ R* `' g1 \; k
  8. #include <arpa/inet.h>% ?( F6 Z  l! G' U. l
  9. #include <netinet/in.h>
    2 _* Y! P( l0 I( O* }. L0 S
  10. int main()
    7 `: Y& N# S& d& d* K5 Z
  11. {
    ' O/ A, T5 b" c, e# J8 ~
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    # \2 K* H; |9 ?: N$ s  U
  13.         if (sockfd < 0)  j1 Q; D  z7 F; \1 `; a# j9 u
  14.         {6 Y$ S2 K& m: B) [' T' x/ _2 f
  15.                 perror("socket");
    * Z5 z& S/ e/ d$ L  y/ A
  16.                 return -1;
    # s2 ?& q5 E) n4 _  u; v
  17.         } //创建失败的错误处理
    7 Z+ s1 W7 U4 G
  18.          printf("socket..............
    1 R! d' V) x& H$ g9 ?' _* v3 @: s1 b' U
  19. "); //成功则打印“socket。。。。”7 z; K9 u5 ?1 C7 X1 J; N, W, u# i8 g
  20.          
    5 Z7 f; Z% F  {6 w! P
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ; g- O7 E7 @4 _5 M
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ; s4 r5 a3 t6 p1 y5 v5 t; V
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    1 Y1 R! p) p  U8 h9 W7 _
  24.          myaddr.sin_port                 = htons(8888); //选择端口号7 q# W' j7 S6 I+ l
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ! F( u/ j6 W: @" i

  26. ( |0 d% n+ b* r. B6 V
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字% y7 J4 U4 e7 C9 p: v7 t
  28.          {5 K( M$ y/ u9 h1 h5 ~
  29.                  perror("bind");9 |* z3 e4 t- \) @9 J+ y% I+ O9 x
  30.                  return -1;. y% b6 ]" ?4 J$ l$ q; v0 J! A6 i# G
  31.          }2 V5 b# M0 d/ {# n  e4 s% X6 R+ a7 r
  32.          printf("bind..........
    8 ]4 e# \6 M, \1 ?/ i/ Y
  33. ");# g5 `7 l" k1 j$ t8 g' Q
  34. 0 k& ]  E$ ~( t: b$ L# J3 f2 F8 n
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听) ], \6 t. m: k
  36.          {
    + m" Q5 f+ Z" t" T3 r1 `; z
  37.                  perror("listen");# d4 X0 M5 j  I) I' i8 Q
  38.                  return -1;
    0 y* N' g: H) i, ?8 A0 i* L6 a
  39.          }+ Z! q4 y1 q3 T
  40.          printf("listen............
    9 |8 E  t, K4 r# A. {+ ?
  41. ");3 Y/ I9 U7 A9 g" g
  42.          + I1 b& j5 ?" S% q0 y& Y) z: }
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求$ d0 @- V' L$ B* r0 U
  44.         if (connfd < 0)% O6 C: _3 L2 S! A; ~: D# i  Q# ?
  45.         {
    ; ^, ~  G. ?9 F/ s, [& V
  46.                 perror("accept");8 }8 y" Y3 V. u( M8 S2 V- A
  47.                 return -1;0 F% M' K* z( W5 [
  48.         }# ]; w1 |: n- ~7 @1 v  A
  49.         printf("accept..............) ^' [' l3 Y4 c) Z. ^
  50. ");5 n4 m, z& v$ m$ M/ H1 l+ b( v( \2 T
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    * |3 c  l# {  `: s
  52.         int ret;) e$ T! r! l- q1 c' A5 h
  53.         while (1)
    1 c. `: o7 x9 l7 _
  54.         {9 d$ [1 t  \% g3 X! W% r
  55.                 memset(buf, 0, sizeof(buf));) o* B1 \2 t9 a- `
  56.                 ret = read(connfd, buf, sizeof(buf));
    $ ]% Q- F6 \# T# }0 E
  57.                 if (0 > ret); h/ Y( @3 n$ J) e' f4 e) m" S
  58.                 {
    : P- c$ J7 t+ o2 E5 f
  59.                         perror("read");2 s7 n/ D1 R  w) L- E% R
  60.                         break;
    2 e4 w! b+ l0 l$ K* W6 u* A
  61.                 }//执行while循环读取数据,当
    7 m' {' j: h0 l8 |* O& {# U
  62.                 else if (0 == ret)
    ; V( F0 l4 d5 c8 F% W& Q
  63.                 {
    - F+ L4 O- x  g) x
  64.                         printf("write close!' W+ n! j$ _9 G" q# Q0 H
  65. ");
    " P7 q, k* J' o( d
  66.                         break;
    & {- ~" I9 ]( t% _1 ^0 ]2 r
  67.                 }  e* e% \0 F7 E7 \# u. x/ t
  68.                 printf("recv: ");, P% d2 ?6 p4 T' R# O0 j( C7 m" T
  69.                 fputs(buf, stdout);//打印接收到的数据- k  {. A8 {6 a& K1 G/ F7 H1 {
  70.         }
    5 N0 H0 K) X$ L" a
  71.         close(sockfd);//关闭套接字
      L" b! y/ ^9 k; ]9 B( t" G
  72.         close(connfd);//断开连接
    ) k4 g+ k7 [4 w3 J
  73.         return 0;
    * T) u& w" V, h1 }; N6 W; ~  X$ \1 F
  74. }
复制代码

% |/ ^" T2 V7 X. R2 d5 n3 Y3 f! Q% Y- _3 \" v) W- y% c* k+ h
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    5 G  M! K7 A% b7 N( m* w
  2. #include <stdio.h>9 C4 |7 I: O4 D: R. e! p
  3. #include <string.h>9 d# X9 D  t7 _  G" ~, ]6 V
  4. #include <stdlib.h>" a4 U" h& ?4 b% D
  5. #include <strings.h>
    ' k& a% C# }: D+ B
  6. #include <sys/types.h>- W5 `' k) g0 V# ?: G5 @
  7. #include <sys/socket.h>% \; g8 I6 g& I) L! ^! Q( k' D2 j: e1 ^
  8. #include <netinet/in.h>
    9 p  B& h: v/ c2 A+ F" y+ n
  9. #include <arpa/inet.h>+ P/ M4 m! g$ }7 {4 F) u
  10. int main()8 N* ?, C+ C$ Q; D- k0 Y6 g4 {' J
  11. {- e& l% R& k9 ~7 r: L5 e
  12. int sockfd;
    ( l; _$ z6 t. m$ A9 `
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
      {9 o. T1 a8 A. e/ N, J7 X
  14.         {/ y5 _" N8 N1 f4 X7 b
  15.                 perror("socket");5 i* m# p( o/ m1 R. k
  16.                 return -1;
    # l3 B; D& a3 N* ~2 P+ Q, B
  17.         }. Y3 L4 J4 w) z9 e& A' z" G+ e$ D
  18.         printf("socket...........
    # n) }, D8 @2 G
  19. ");
    3 T% A4 T; ]8 J, t2 o
  20.         
    ; A4 a+ ?; H" H* r
  21.         struct sockaddr_in srv_addr;. U  H2 B# b/ d( M; V8 Y5 F) {
  22.         memset(&srv_addr, 0, sizeof(srv_addr));8 I( o' x6 M$ f  q3 d
  23.         srv_addr.sin_family                 = AF_INET;
    # J: O$ K+ k- L! Y, i  v
  24.         srv_addr.sin_port                         = htons(8888);4 J) }) Z' e: z' Q9 O/ x% s/ u
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    8 e% z4 c/ x' J
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))/ B/ O, Q) h7 G4 `, y
  27.         {& R! ?5 h$ ~4 l! `1 `% G' j
  28.                 perror("connect");% k" }+ q% Q! Q* O; [! c
  29.                 return -1; //exit //pthread_exit- r5 i6 s" Q2 m* _# r
  30.         }* U, f, f- q+ N" M' k8 D6 L
  31.         printf("connect..............
      Y5 J5 [+ L- O: @0 h, S- G
  32. ");
    2 P0 b- R5 E0 m3 h
  33.         char buf[100];
    $ T9 c3 B7 L, f5 U$ S8 i" M' S
  34.         int ret;
    7 i6 B/ ]  y0 U# I/ T0 V
  35.         while (1)7 `; _) n, E1 O6 I$ e' e
  36.         {! a3 A& \2 d( m/ m
  37.                 printf("send: ");
    8 Z( @3 J" e* Y# i+ m" B, U" B' K
  38.                 fgets(buf, sizeof(buf), stdin);# |1 m4 v- Q- R# r7 V; v: }( w& Y
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ! t8 Q3 {" y# T
  40.                 if (ret < 0)
    4 T% M+ H: O5 H/ [) }0 \6 Q0 _
  41.                 {
    8 |; R+ E* D+ }3 t: _6 ~
  42.                         perror("write");" J+ d9 @* {2 v* `( ~9 ^
  43.                         break;* s5 a( t) H! s2 p7 A
  44.                 }0 K6 `' c5 |% ~5 ]
  45.                 if (strncmp(buf, "quit", 4) == 0)' O0 M& k# X* a0 e/ A
  46.                         break;
    + R6 W3 Z/ \0 F1 Y
  47.         }2 K8 f+ g4 a5 \* R
  48.         close(sockfd);' E. I8 [' W7 c- T8 H2 T. ~
  49.         return 0;1 ^/ s9 `- q" a+ Z+ c
  50. }
复制代码

: A# B( a! l& p7 Y" n5 b; H" V
! b. }5 O- k. |6 _0 S" a% _/ @- O
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-6 03:25 , Processed in 0.164293 second(s), 24 queries .

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