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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。- U! X' b3 K  P. B6 u/ M

6 u+ K+ B' \+ C3 T# B
! n  l8 o$ N) V' p+ y3 ?
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
* l$ c0 B* ?+ R, S  f/ w  {4 c' ]; N* L% y

* _# \/ y7 F; y6 u. vTCP协议3 \9 [4 K3 p+ f& G' A% k" f
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
, T# n3 X3 U1 h, V  j+ G$ X8 p# d& O* C* Y) H

) l- K" z+ ^5 i0 n关键词:三次握手,可靠,基于字节流。
1 a4 A0 ^  ]* }5 p' ]2 _0 S: H0 ~7 T$ }3 `

) T  |5 [! b, R& v5 n可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
" }0 S9 A1 M' f) j# V9 R0 U* K
8 Z4 v8 o7 T/ k: A/ h) s, ZTCP服务器端和客户端的运行流程( B! U3 v: F6 Q* l5 C7 _

# T2 c, Q4 ^5 J* e# F* U% _
' r. }( c5 h, r' h
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
# z0 M8 @' L$ J, N& G0 R  h% O
8 m' P# k; e# e$ W/ T% f

# h1 a6 `3 _, `- N1.创建socket
1 O7 W/ L- l2 |; @ socket是一个结构体,被创建在内核中
( v; t# X7 s7 o; u6 D sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议1 U! H* @* R0 ^8 L3 O+ J

, j+ c+ z& o1 u" n& V* D/ O3 X& j

' e* _/ x& n; T2.调用bind函数' Z! t* E0 D! T& n0 h- t  O
将socket和地址(包括ip、port)绑定。% v3 O8 H; M1 K! o7 g- t6 c# U+ D7 w
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序* t9 A  O: W- M5 I3 V3 b/ F
struct sockaddr_in myaddr; //地址结构体
2 @4 @6 V4 A6 |1 ?8 q4 v bind函数# A: ?8 d# f6 h- B  v/ P6 a) B5 n
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))# L2 s  I7 x2 X/ t! m, c/ N

0 O5 c5 l0 H# g6 R

3 v! k# C! s$ [- x1 a3.listen监听,将接收到的客户端连接放入队列
0 ~7 ^; m3 J' u0 C: ]6 w: j6 R listen(sockfd,8) //第二个参数是队列长度, b3 Y2 H- n- ]$ v: l4 O/ F4 Z) x

" D6 f& M: j2 j/ {$ Q+ L
4 }. g6 c+ x4 y1 a9 w( t
4.调用accept函数,从队列获取请求,返回socket描 述符
0 {; W" N" w2 P  如果无请求,将会阻塞,直到获得连接; h, [- W7 g' x
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数3 \% e. U  f( [5 {9 l7 H! ]7 f

* M: e* U- C6 U# F& g8 J
2 m* ]! W$ @7 t9 q# |) g) r4 V5 O
5.调用read/write进行双向通信8 U/ b" {" O0 e& W. F0 I

' D4 O- z# q+ r0 _
, U! P. ^* `9 H/ t) [5 Y! [
6.关闭accept返回的socket; [4 d4 w9 ?0 `6 h# k5 x% K- N! ~. |
  close(scokfd);
; b; W9 K7 [/ J! v; z" f, ]. b# p/ J/ }4 M& H, F
2 t1 O. o& F: C1 j' E
  P* A( U' u' R4 u

$ B  T# k, D& d# g: ]/ g4 a1 E: m% X! e" Y下面放出完整代码$ Q" N# r$ I. X7 t0 o# @4 B$ c6 B

4 p9 b" Q1 j3 d# s2 T$ s) t
  1. /*服务器*/5 a$ _5 H. F9 g* ?% D5 o4 j
  2. #include <stdio.h>0 q& D& |! `2 E. y1 D0 G
  3. #include <string.h>
    7 M+ Z8 {, j2 G8 m
  4. #include <stdlib.h>! L; D# `8 b  [) R. l# }8 `, S
  5. #include <strings.h>4 _) _8 e8 \9 N- Q( K5 i
  6. #include <sys/types.h>
    ; s/ c5 C7 d6 Q. q* ~1 E& A7 z
  7. #include <sys/socket.h>
    " G) l  F. r, \" G5 D5 X8 ~
  8. #include <arpa/inet.h>( j* j0 Y+ p" D* ]
  9. #include <netinet/in.h>
    . E$ A: }9 P3 c% e
  10. int main()
    ) Q1 D, r5 w! C6 f1 B
  11. {7 d; x2 o/ I) h' V( }
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字6 e# G: F+ z; t1 y
  13.         if (sockfd < 0)
    ! P( _+ ?" M- F( O- Z. v
  14.         {
    , b0 e+ k  t" Y* h
  15.                 perror("socket");  H$ E* w$ a2 A- W
  16.                 return -1;7 u5 C* L( ?  ?+ t$ `; ]) p
  17.         } //创建失败的错误处理! u6 s7 O( k3 _8 ]6 p
  18.          printf("socket..............
    6 D$ ~1 {: j1 j- _2 z0 H2 }3 [0 r
  19. "); //成功则打印“socket。。。。”$ {3 a. d! ]2 y- X5 [% I$ \9 H
  20.          
    0 B  A* D6 g" c9 r8 r
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    3 m: f- n; b  _1 t7 J# E
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见). \6 v: Y; r3 v* q  y8 ?
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    6 X: L: g2 q8 u4 b0 e1 E
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
      ^! ]) y0 P0 @4 e4 w& |8 u! c+ h
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    ! W% L' \2 a( e# ^

  26. ' U+ ~! C3 g" y9 v( L/ b
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字0 n7 K0 Y! N( f5 F" w4 O" ~
  28.          {
    + F. r9 F& `& K
  29.                  perror("bind");
    & T$ N: j' \% Z2 q+ |
  30.                  return -1;
    ) r3 U0 u7 h6 A
  31.          }6 [% V+ @0 K' [8 O' A# |' K# e
  32.          printf("bind..........3 G3 @" z8 R" X- `6 z1 R0 `0 y4 f
  33. ");
    2 }/ c7 l4 e9 {$ }3 A
  34. 2 I/ y* y3 I, `8 x# r3 G) b
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听/ C1 W3 P# |) W: p% `. A
  36.          {
    7 m, n7 L) Y9 s
  37.                  perror("listen");* Z& m/ x3 L. o* R) G9 s
  38.                  return -1;9 W2 g% o5 d$ l! g9 p# h/ O8 c
  39.          }
    , c9 T. T8 e/ k0 F3 R4 F
  40.          printf("listen............
    & H4 `( h, `% Y& C
  41. ");
    . W; ~: m( T( \5 M: @, G1 B
  42.          
    , o; i* A0 K3 R" e, n: |
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    * b6 j" y3 J( T/ a1 p2 o3 Z
  44.         if (connfd < 0)( S1 u5 b6 \- q6 y7 j" _) F: R0 A, H0 x
  45.         {5 P, Y1 h0 h% b0 h0 Q
  46.                 perror("accept");0 @  m2 A8 E1 ]) V, V: ~
  47.                 return -1;7 l% m  ]7 a, C3 k/ d( c& V
  48.         }$ @" O7 a: |- o' f
  49.         printf("accept..............- c* R; }4 I6 T' z
  50. ");
    ! [+ d" ~! N7 w& C- r* }9 [! n
  51.         char buf[100];//定义一个数组用来存储接收到的数据# B0 E; ^. `! e/ i  j7 _! T. P
  52.         int ret;
    7 h" B* a; o, l' u5 d7 G, l6 C
  53.         while (1), v3 E9 n3 j9 c$ \4 k' T
  54.         {
    7 C* ?! f% @$ Q0 Q% j# m' L4 M
  55.                 memset(buf, 0, sizeof(buf));7 ^; m* k, j2 a+ B
  56.                 ret = read(connfd, buf, sizeof(buf));
    2 V. }$ T" k# ?$ [  p3 p
  57.                 if (0 > ret)
    9 x7 l  q  h) N0 N
  58.                 {# j; H# H! Z) x4 Z6 U
  59.                         perror("read");
    + Q7 v( T7 n7 p! ]: J# Y9 J
  60.                         break;
    - R: T( \- s$ |
  61.                 }//执行while循环读取数据,当- ~9 y8 r' {. T; f, Q
  62.                 else if (0 == ret)
    ' f6 M" h" c; _% R
  63.                 {7 ~2 I, @4 u6 G
  64.                         printf("write close!  u  k* G9 g: M# J7 \# u6 s
  65. ");
    * h6 a6 U" |  b+ \8 c7 |
  66.                         break;
    , B0 q2 v: z6 ]! R7 U* H
  67.                 }
    # ]6 a) x3 S( V2 w
  68.                 printf("recv: ");
    + e$ s! V' r6 C4 j. C5 L
  69.                 fputs(buf, stdout);//打印接收到的数据
    . `8 T# a* B+ b% s" a1 c/ V
  70.         }. T2 `6 r- ]1 u, v# i
  71.         close(sockfd);//关闭套接字9 D8 W8 u2 i* I8 R4 Y
  72.         close(connfd);//断开连接" w4 q2 i6 \7 }% L: J. v
  73.         return 0;
    & c( F7 G% Q+ o; _9 F
  74. }
复制代码
, f$ o: g% Y% \8 E* }

* H: H% h: Z) `/ ]
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)0 Y8 }' A7 h9 |0 b+ ^5 p/ i
  2. #include <stdio.h>
    . x; P* b3 m: |  O0 V
  3. #include <string.h>5 `% i' A  U7 h8 G
  4. #include <stdlib.h>% ^" @7 H; I4 @6 H$ c" U1 |# T
  5. #include <strings.h>
    ! M+ ~; Q5 Y) R& J4 b5 n2 a
  6. #include <sys/types.h>
    2 m7 D$ I! H4 }
  7. #include <sys/socket.h>! N# l1 _* S, i* W$ Z
  8. #include <netinet/in.h>
    6 u8 K: K% P2 |+ d
  9. #include <arpa/inet.h>
    . o2 P" G+ b4 s( K' q) o
  10. int main()0 i+ p' p% G) N% V
  11. {, f% x; Y2 B% K* T2 u& f8 p
  12. int sockfd;
    1 T: K. U1 r, N1 w2 R6 ]7 h
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))( w0 ?5 Z4 ^5 D' e# Z+ P" m/ l/ L
  14.         {
    & N: P- W: x* g- S* Q# W
  15.                 perror("socket");( y; x5 W' r7 l2 x" x7 m' L; N" m
  16.                 return -1;+ H- O5 `  ?4 c3 ]& a6 n( H8 A8 C7 W
  17.         }
    9 G+ o3 s2 I0 ~9 ?/ i* v
  18.         printf("socket...........9 Q2 B* Y7 b# u' }" d; [0 G0 N6 j5 N
  19. ");1 ]# H7 V. N# q+ f) w" a/ o3 k
  20.         
    * q9 x, o0 N  w- D1 q& ^3 v
  21.         struct sockaddr_in srv_addr;" n, |1 \2 o8 s" H0 r3 F. m
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    9 e( C) ?& T* p( p4 a2 ?
  23.         srv_addr.sin_family                 = AF_INET;
    6 [$ T' D" y& |8 k# }
  24.         srv_addr.sin_port                         = htons(8888);  l2 O2 t5 ~; u' m6 m8 x4 Z. H
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    4 X! w6 a! t5 `, F
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    & H. ]" K7 Z6 |! B: ^# ~
  27.         {: b- p3 `" q' h
  28.                 perror("connect");5 w4 s9 O0 l. O" v
  29.                 return -1; //exit //pthread_exit
    ( i0 c# g# \7 o! q6 `' `. G
  30.         }
    ! L7 s5 y9 a( E0 }
  31.         printf("connect..............
      A7 R2 m/ ^* J# F1 r5 W
  32. ");
    , i9 C( A) w& o9 m, x3 ^$ m6 Q
  33.         char buf[100];: b* T* q- e3 i: f# }
  34.         int ret;8 l& z( p! t. w1 w7 k) X
  35.         while (1), L' u% d7 B# D1 j
  36.         {
    ) u  d8 o/ \. G
  37.                 printf("send: ");
    % ^/ o2 H: `3 R4 i8 O8 H
  38.                 fgets(buf, sizeof(buf), stdin);
    5 S* l6 P& L, }+ ~! A
  39.                 ret = write(sockfd, buf, sizeof(buf));6 w: t9 L8 _( I# y" n) Y
  40.                 if (ret < 0)3 M( ?% s8 j7 ^& `9 M' w
  41.                 {' L0 U# i* X5 x4 s1 k2 y% y
  42.                         perror("write");
    ! p. i" C/ d7 B2 v2 P
  43.                         break;( q# W' L8 p5 {0 i* T
  44.                 }
    ' {9 d5 W0 F7 a4 ?4 ]
  45.                 if (strncmp(buf, "quit", 4) == 0)
      @6 n; i# c3 Y
  46.                         break;, k! E2 x. d1 X
  47.         }: P/ \8 t: b, I0 N! n* W& m" v
  48.         close(sockfd);
    $ E  }; b* i1 B+ a
  49.         return 0;+ W, B4 T& V5 t+ u7 ]4 G6 _; ?; Z
  50. }
复制代码

- m. [+ }; ?1 Q/ T- V" l
% ]  l1 `& A. m
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 03:09 , Processed in 0.127822 second(s), 23 queries .

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