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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。/ ^5 @" E& L) C, J$ F1 v$ y9 x

1 O1 p2 E/ u0 S$ J& ~

' e( n! T7 S# O, q# csocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
+ C: D  z9 @1 f2 G+ j; v2 K
8 Y/ q- J' w% V

8 Q( N) ~8 @& m) oTCP协议3 |. ^. w2 D$ k5 |3 a
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
3 s1 l' U* A) ~# d: j1 R7 j* S& M7 ?/ F" X5 ~2 B! W! E
0 R( R3 U3 Z* A* a$ v
关键词:三次握手,可靠,基于字节流。
+ S& t; S0 V4 V: X: X( `7 T. O5 r. K
" f! a& y0 i1 h4 j
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。4 }( C* y" f% P" T" w

# M2 R$ N0 [8 j( W2 v6 A5 e6 {TCP服务器端和客户端的运行流程
6 [( L& P3 c) G' a: e
% q* y0 K! \4 X7 ]- L

+ u* x5 f. O+ C: o5 G) o( y. }如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
& W. e8 ^- z: B) o6 g+ Z, V+ s+ z; n, }. }9 J+ i
. f' Y$ N  A5 f" N, P+ ^
1.创建socket
# R7 P4 q4 T* j: i7 x socket是一个结构体,被创建在内核中
  ^8 W' r) m% d3 V; U1 q; q sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议3 `$ ~; s1 s( o

( h" \8 G/ m; R( r8 y) B% A& `2 d
6 ~( }- T( J0 D
2.调用bind函数
8 L5 y/ K6 ~1 G% Z0 u$ ^. C: m 将socket和地址(包括ip、port)绑定。
$ |+ {; X/ [) i9 e$ u6 u 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序6 `4 m& K3 E5 F4 z
struct sockaddr_in myaddr; //地址结构体
$ D% P: \! j" o6 w' f bind函数
) R4 x3 k" i1 l0 T bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))% q5 _$ i  E+ Y
; B8 X; X: [- ~3 n* ]

! M, Z, b7 }4 K4 s) y& e2 v3.listen监听,将接收到的客户端连接放入队列
$ ]/ B% h8 x9 W* |! n3 o listen(sockfd,8) //第二个参数是队列长度, u: `( k" ]2 ?6 ]1 b0 C: i

% x. }  K$ D+ q4 T

% O4 x: A% p  Y9 y+ b6 s+ i4.调用accept函数,从队列获取请求,返回socket描 述符- d, _( n6 w, y$ v; d% e
  如果无请求,将会阻塞,直到获得连接
- `. J7 ~& U# _1 f. I( U1 {  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
) w7 I) E3 V9 o7 J
+ n0 ?* x7 u& l7 ]: t# i( `4 N
& ^* I- f6 ?2 @
5.调用read/write进行双向通信
! }' L+ x" I( B! c: u: J- z: I7 a1 p% A6 n
- x/ t7 T( ~5 d# ~+ a
6.关闭accept返回的socket( p) o9 o2 g) f4 l' X3 o
  close(scokfd);
1 x* G) c7 j2 p4 [; g6 M; @6 G: a. K( g) y0 n

) a# j: S+ k9 F7 l9 s, t4 B
5 x0 l! K, p+ U' ^/ i1 x. ~3 c& l

1 E! X& U! q* p: Q; |下面放出完整代码
# e0 N* {! F! N% E- Y" A- w
& w% B6 o3 u8 w. [6 M0 Z  j) f! W
  1. /*服务器*/
    3 Z( b" n- m% p3 a$ M' M$ J* ~
  2. #include <stdio.h>& }$ P( P' F# \- H& X
  3. #include <string.h>
    ' r1 x* m3 N4 d8 f
  4. #include <stdlib.h>. A% M: [* x3 w# Y4 p
  5. #include <strings.h>! H0 _7 F: w5 v" i# q2 h/ \; x
  6. #include <sys/types.h>
    5 [8 d) P8 X& d. b* \7 \, Q
  7. #include <sys/socket.h>/ b  b5 F" x1 e4 n; K4 }% |% k
  8. #include <arpa/inet.h>1 ]! R0 H! M" B3 _5 M
  9. #include <netinet/in.h>
    5 {1 P- {5 {/ z0 F  P
  10. int main()' a- ~9 q0 G; n- M5 J, Q& D
  11. {
    3 V, e; t3 l$ N) T7 t0 P* N- K% s5 [
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字1 `$ z" q& f! [  C+ |1 ^. z
  13.         if (sockfd < 0), @% \: O. o9 q' M5 R
  14.         {
    ( N4 D0 A4 \' E9 ?
  15.                 perror("socket");
    ! |# t: d3 e* Y/ t6 O6 g) O
  16.                 return -1;! v, {# z: [" u; R1 S( D( t+ o
  17.         } //创建失败的错误处理3 g% p6 V# \5 ]  ]4 U
  18.          printf("socket..............
      q4 P# C, j* v: h4 F9 O8 |% H# ?
  19. "); //成功则打印“socket。。。。”0 f% j% P% q. C+ h, K0 ~2 H
  20.          
    - f4 S8 n8 `2 @+ P! M7 S
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体# J" w9 ^, {3 z# `; T; {
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)$ P, Q' L+ F2 I! m* ?6 Y( J+ b. p( ~
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型- r2 H5 V% p3 s1 a+ |& ^
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ; n# {7 i6 F) i8 ?7 G, F
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址3 o6 _: o( \  d4 u: S

  26. % D* q; c0 h  X2 A3 J) g
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字4 g3 q+ i/ C5 z1 ?8 u
  28.          {
    ( ^+ a1 `/ Z1 y& V1 {
  29.                  perror("bind");* M# k' \6 U  e& t! F( u
  30.                  return -1;
    / a: h( b5 |1 Z1 u/ }9 s
  31.          }
    : W& U0 p& l) d+ R
  32.          printf("bind..........
    4 D( E. P% F1 D  {: {6 `6 O0 \
  33. ");
    5 L" N2 X& P1 b4 {* N" S

  34. # M* }7 d3 f0 q9 ^+ I
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听/ s; }. o& o8 |8 V" x
  36.          {0 }) P2 j5 z7 F4 h4 i  {! N' `
  37.                  perror("listen");  E" l# e2 I' d: y) L( w
  38.                  return -1;
    7 i3 W8 c  {% p2 I; d: p0 E
  39.          }
    5 d* |" T! c$ o
  40.          printf("listen............
    1 V$ J$ v' j6 Y$ W9 g4 a
  41. ");" O  K5 @. L7 C2 a4 _8 D' p
  42.          " z3 d8 y5 B* v9 }) |" S) a
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求' m/ D7 Z8 K* F/ V  k5 y* I6 n' m
  44.         if (connfd < 0), c9 V# @) W+ G( H/ N! P2 N
  45.         {' p6 J$ @, M  [9 w* F
  46.                 perror("accept");7 v) L- b  x5 R- J/ h6 r
  47.                 return -1;
    4 U+ M4 r; W$ l8 W  U4 W$ B' |
  48.         }0 s# I9 u9 t  i  a- O1 T2 v. I
  49.         printf("accept............../ ]- W, `% l* n3 A! j2 Y
  50. ");
    & X7 ]; b: T" x) ^1 r
  51.         char buf[100];//定义一个数组用来存储接收到的数据! \7 Z( T7 {" A2 M" t* m
  52.         int ret;7 x& C  l, d) z# O+ Y1 @5 G5 k
  53.         while (1)
    & K: F* [7 a" l' f8 ^
  54.         {
      b' s* d! D. Z
  55.                 memset(buf, 0, sizeof(buf));4 V1 Q) V( ~/ g+ E2 G& k
  56.                 ret = read(connfd, buf, sizeof(buf));
    5 y9 N7 L+ S  {% Z
  57.                 if (0 > ret)  C$ ~; F, ~# `$ k% O
  58.                 {
    0 a! p3 |, H6 W7 i# g0 L
  59.                         perror("read");$ ^: h4 _& c2 ?/ s
  60.                         break;( z/ O' E, P: x1 u6 }: J
  61.                 }//执行while循环读取数据,当8 v# u6 g2 E( p# w: M
  62.                 else if (0 == ret)
    ! P8 @% @+ G4 @6 E2 r) {
  63.                 {8 a, E+ \- X0 A3 b2 I1 L9 O2 P
  64.                         printf("write close!
    1 V- Y- g% _4 F/ {" g: |% @
  65. ");( _3 }9 ?* E) F& E
  66.                         break;8 E2 D3 }8 b) D% u2 [
  67.                 }* J2 j! b' A8 T- H
  68.                 printf("recv: ");
    0 x) \, r  j% ?( O: L) p( W, }
  69.                 fputs(buf, stdout);//打印接收到的数据
    ! H5 E- G/ K# l
  70.         }& m. _5 p4 T, Q  p- S
  71.         close(sockfd);//关闭套接字
    * d( D/ @# W0 s
  72.         close(connfd);//断开连接( Q) g( M9 N5 C* B9 l
  73.         return 0;8 l7 M9 Y( a) }9 q
  74. }
复制代码
8 s4 ~+ e. t! |

& n7 V5 y* N" u/ P
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)
    2 n" s) B- W" s/ q2 J
  2. #include <stdio.h>
    % f, ?  d3 b" X/ }
  3. #include <string.h># z1 `6 s7 D3 h# c% w% w
  4. #include <stdlib.h>6 r% j3 s: R! m' _
  5. #include <strings.h>( M" ~7 m7 g, H. i% M. f
  6. #include <sys/types.h>
    , l: j4 q( \2 k( y
  7. #include <sys/socket.h>9 f1 {9 F$ L6 h% D3 a% `
  8. #include <netinet/in.h>2 Y3 _/ x0 m1 g2 s1 y
  9. #include <arpa/inet.h>
    & G: c4 o! s& ]9 [8 c: y
  10. int main(): ?  o# x8 W% K9 S7 Q. w7 T+ y
  11. {2 r: C8 {! o$ s: v+ c
  12. int sockfd;
    6 t  b: q) ]1 y1 r% Z' B9 W& _4 S. D  D
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))( F0 n3 J4 Y1 _# p7 i
  14.         {! U5 H' l! V; Q  N
  15.                 perror("socket");
    / B* J# @% D9 \% S. u$ D
  16.                 return -1;  D6 N# {+ E# v: G# W6 p! W
  17.         }
    9 X: g* ^7 c% u9 R/ Q
  18.         printf("socket...........
    % X: l: ?2 i6 b$ B! {2 M4 o
  19. ");
    8 A; c$ d4 E8 F6 H% `& K/ `
  20.         ) M$ z* U! o4 h, ?
  21.         struct sockaddr_in srv_addr;
    " G* G8 m4 a$ g" ~  i2 l  x
  22.         memset(&srv_addr, 0, sizeof(srv_addr));& I  `2 Z  p! o. j3 A# E4 i
  23.         srv_addr.sin_family                 = AF_INET;6 l; s! ?0 D8 \
  24.         srv_addr.sin_port                         = htons(8888);
    2 K9 O" Z8 r  P  h/ ~
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");; v4 g- H! K' ?' ^# ^  F
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    , [' x6 ~! h# x" J
  27.         {
    9 @" F! C6 {! H4 V" z( a- k( C
  28.                 perror("connect");
    4 x3 Q0 ]+ h; J- X4 l- I
  29.                 return -1; //exit //pthread_exit
    / ]$ k" X+ D1 S" b0 q5 u
  30.         }
    # ?+ ~% |" I6 Q3 y/ n, n
  31.         printf("connect............... C4 w' K/ R7 o/ r, X  R! d! Q, B
  32. ");8 B% w1 p: j+ ~5 Y2 Q& T2 J
  33.         char buf[100];
    ( c+ M8 m- L3 I6 L% t- u
  34.         int ret;2 @! K8 s! v/ J: J  r; u
  35.         while (1); C8 {  |- F4 }9 M
  36.         {- e# `; u0 t9 K! i
  37.                 printf("send: ");
    5 N9 j$ B, ]' }2 c3 L0 v& y
  38.                 fgets(buf, sizeof(buf), stdin);1 t' h9 ^7 d9 ~# `0 ?3 J
  39.                 ret = write(sockfd, buf, sizeof(buf));6 O! y3 b. a# a" k/ r! H7 ^
  40.                 if (ret < 0). s  O  E+ s; G4 X" i$ p+ w1 |
  41.                 {
    8 h1 @3 p5 w# `4 V7 @4 [: `5 `& ~
  42.                         perror("write");; O& K, o/ K7 R9 @
  43.                         break;; M* E" _8 t2 N  G# ^+ z: L
  44.                 }: t( U) p( W9 {2 u% ^+ v
  45.                 if (strncmp(buf, "quit", 4) == 0)
    7 g+ n) E4 w2 U
  46.                         break;! o; a* Y! n( s( W4 p0 B
  47.         }  H1 ?  ]5 R, D* X7 g0 d7 c5 c0 d& X
  48.         close(sockfd);
    . p9 W$ X6 \% h' D
  49.         return 0;. O" U: S2 ?6 }/ o/ u2 b
  50. }
复制代码

; h! u2 ?- {4 J1 c9 z/ s! G  _: g$ g
& h# u2 w9 P5 w! ?
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 23:40 , Processed in 0.110393 second(s), 22 queries .

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