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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

4 z% w/ f- H% l$ b# k6 o+ n! l+ f

6 r& `- P* X% S5 lsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
$ f0 F" ]4 d8 m% C
* }( Z" _" i0 z4 \+ E3 C% y
" m9 n# y+ T; E, P6 i
TCP协议
! v) C8 I7 S1 DTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。  n( R4 s  ^8 J7 D( v- ]1 Y7 Q
. J$ ]( G  ^& t

6 r( V3 H5 M$ k- t% J关键词:三次握手,可靠,基于字节流。
* ^0 ?6 E2 U, t$ h$ z: C, P5 E. ?% U

) }6 n  d4 ~9 L  k* ]4 @" t可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。3 F3 Q0 k8 ]# M

; }8 I$ R2 J* mTCP服务器端和客户端的运行流程
3 u# |) S1 s, D7 ~  ~
5 \$ {3 N' _, _+ _9 c

1 {9 y# ^9 R3 L如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
/ [) ?5 m- I" u" |' M
! [: l: B! g$ m* o; l/ ?8 T

! x: m' \9 z/ C. k6 X* B# e1.创建socket
) [1 ]( ^8 _" ~! ?; G6 f socket是一个结构体,被创建在内核中# m7 }& J9 U, Q
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
1 }# Y4 S# |" m% e1 h7 K! B/ D1 p# `. _0 X, t( o" ]' U. C! n

, Z" M$ s  G; M; i& a2.调用bind函数
$ V$ m1 h4 ~0 `2 ?" ~3 a 将socket和地址(包括ip、port)绑定。
: p! Q4 K9 F; t' m& S) ^2 x 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
2 L. [8 p! K. ?6 e7 _ struct sockaddr_in myaddr; //地址结构体
8 V' A( b! Y$ q  G# L! d bind函数, `& `1 m- C. D. \  T& u
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
9 E' a$ i8 T4 ~9 q. G! F3 S4 c" k1 F, b$ N( s

3 O1 R. x$ }3 A7 z6 C6 l9 l3.listen监听,将接收到的客户端连接放入队列8 X: U1 ~, k1 `7 x! ?4 g% r! O0 f$ @; D
listen(sockfd,8) //第二个参数是队列长度
" b( J  Z, _: p- \( F; k. E: K1 P  z# ?: K8 y- k

& t3 t' @* `( D& @4 u7 `' r4.调用accept函数,从队列获取请求,返回socket描 述符# x1 u6 U+ K; U
  如果无请求,将会阻塞,直到获得连接
# j0 L; a. s/ [) P  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 p' h  ~) i0 Q6 e- M
3 O% ^+ ]$ i$ w# ~6 M4 F0 }
$ B% t' c+ P- \+ j, `
5.调用read/write进行双向通信
: N' [) A1 R# b5 v$ I# O' Z1 `  M# \/ q

7 I1 h4 P; g* ^& x' L1 z  G6.关闭accept返回的socket5 Y8 i+ J( |; A
  close(scokfd);
! e) h& J% t1 r% Y* [; g1 s5 @. g1 X1 G0 }" g
7 l; @7 L' s  ^+ f# K" v' A6 }
' \% j! t' G) f3 s# c# z9 ^

% D" p9 g( o7 E- ^: y下面放出完整代码
& ~; K2 L+ U7 e' p* V: q; l: p
% V+ F+ V& }, U, I" F
  1. /*服务器*/
    * I+ l& P3 V9 k1 w5 e' H
  2. #include <stdio.h>
    5 L8 c5 p! t* ]( F1 W- `: z
  3. #include <string.h>
    " t; Z+ e! M$ s1 j5 L% }  }& d' b* @
  4. #include <stdlib.h>
    5 X5 q! S" }& o* V& @
  5. #include <strings.h>
    + r5 m9 x6 U) J, a# J& K, j. ~
  6. #include <sys/types.h>+ \+ a6 Y6 G. }0 |! q, T) A+ T) a
  7. #include <sys/socket.h>5 ^6 G6 X! O) |  x- e
  8. #include <arpa/inet.h>
    8 q2 U' n. f( J
  9. #include <netinet/in.h>1 U4 B0 |& F) K* g! e3 v4 |; f
  10. int main()5 b$ ^$ b! ?  R  M; M0 P# `8 u
  11. {
    - I0 ~( Q; {8 k$ {6 x9 D
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字3 O1 T6 F, c4 T, P  P! a
  13.         if (sockfd < 0)+ [2 Q0 ^% D# b! R% T
  14.         {* U5 t6 q% @$ K0 o1 ]% e" n" S. @
  15.                 perror("socket");; v1 [! }& r+ V* c( J# T
  16.                 return -1;) P% |1 B+ d4 o2 v  s
  17.         } //创建失败的错误处理
    * J$ t/ s- y  _* l/ O
  18.          printf("socket..............
    0 l0 h- \/ o" y& X
  19. "); //成功则打印“socket。。。。”7 ?5 V9 V7 }. C2 \2 v- w- v. |
  20.          
    ( |7 J9 o  h5 {
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    3 ~- Z* c: ]9 S6 G& Z' W' y
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)" C: M4 X2 Q5 o
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型& A, w( h+ F# p7 z$ R( N
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    4 G) ?( u& B( F* S& J! R
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址* x5 u8 f8 {7 S- m1 ?
  26. 4 v8 m+ N5 Y: s: Z/ P, {8 ?
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字/ V/ @, w: ?: h( W7 h
  28.          {
    : T& @2 R3 b$ v5 K4 x- p( [+ K
  29.                  perror("bind");( i5 h7 K, M% V2 _
  30.                  return -1;
    : B& B( r7 K& \5 D: L- M& h+ e
  31.          }; p$ a' n# O$ ^: j
  32.          printf("bind..........
    5 F; ~! x3 Y( j2 R; E! e) X' s3 g" y; f
  33. ");4 i' k) b- }% ^, ~: K' P% s" Q6 _
  34. 3 l4 J0 d# O% @0 |9 C9 k9 T
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    % m, Y6 N) F  F7 U& c$ f
  36.          {" _) F4 [  _. E! H- z
  37.                  perror("listen");
    $ x1 p2 j. k& Q2 f. Y
  38.                  return -1;
    $ J' {& H5 t) b& L5 P$ }3 a" v
  39.          }: X+ |5 o2 I7 E% R6 I! o
  40.          printf("listen............
    % \( N) t2 i. k$ K+ J' T
  41. ");
    - O1 _( C2 \- t% V- {5 J
  42.          $ O$ Z( a, u( o9 m# @: \2 X
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求7 D7 {2 }; a5 e
  44.         if (connfd < 0)
    + a  a- f3 u  z4 X% a* P
  45.         {* n% }' Z" d' [, b" p1 C: g  V
  46.                 perror("accept");
    9 [9 W6 d9 T& f7 g, U& s
  47.                 return -1;; s7 x9 T8 M' E
  48.         }
    9 |- z" N0 j$ K# }2 J
  49.         printf("accept..............; X4 n; I" j; @
  50. ");
    9 B6 m! b+ j4 L" N& l5 i
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    9 D9 q7 F, e, G  M9 |
  52.         int ret;6 P' s' O: v5 |, P8 m
  53.         while (1)
    ; `( r6 d5 `, Y) r4 O, h6 C
  54.         {9 q3 X' [4 U2 v/ h! T! u
  55.                 memset(buf, 0, sizeof(buf));. j) K3 w! C- i+ Z# J& V" }. W' ~6 r' o. ^
  56.                 ret = read(connfd, buf, sizeof(buf));
    # e% G" F# v5 ^. U3 ?- q' D
  57.                 if (0 > ret)
    + x* O: e0 h! T1 ~
  58.                 {
    6 Q; C# i5 a' B1 r
  59.                         perror("read");% b- F8 n5 [$ z, s9 U2 K
  60.                         break;" J9 ~2 O0 J2 y! u; I
  61.                 }//执行while循环读取数据,当
    3 s9 ?3 w& j7 O. ?5 v
  62.                 else if (0 == ret)
    4 O9 N/ w, G$ x9 @9 j. v/ ^
  63.                 {
    ; t, F7 k, A, U
  64.                         printf("write close!
    1 G; X7 f+ q5 b# `, ~
  65. ");
    % n! A  H4 K( v+ k  G
  66.                         break;
    : d% I2 x7 a& h$ b5 O
  67.                 }  P, e2 C8 G' W6 J
  68.                 printf("recv: ");
    # n: i  `4 N& f. ~% Y" f7 T1 D8 g
  69.                 fputs(buf, stdout);//打印接收到的数据
    ( H6 [) I1 e" V% i
  70.         }& e& i$ n# i& n; K0 Z
  71.         close(sockfd);//关闭套接字
    + w6 E- E4 k" W8 Z9 R- K& x+ ?4 J
  72.         close(connfd);//断开连接
    # h( q, E% x+ `- {1 b  z
  73.         return 0;4 W" l% L$ l& g5 d, m% W2 v# Z' c) Z
  74. }
复制代码
" s) {+ L# T- ~! h9 @2 X; M* b
* X* G1 v# r* A5 e9 V% c
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)$ H7 a* b4 r6 c7 u
  2. #include <stdio.h>- E) A0 `8 Q& X9 f( l/ W9 W
  3. #include <string.h>' [# v. i. T4 w% A# n$ J
  4. #include <stdlib.h>7 Q% H* r+ o2 E" G: c: d- `: }
  5. #include <strings.h>7 q/ V+ ^+ O: e5 {/ g
  6. #include <sys/types.h>! `: H# v2 I: D5 A( C$ `' i1 z' q$ o# x
  7. #include <sys/socket.h>
    + `+ C$ N, {. d9 h7 |) ^# m
  8. #include <netinet/in.h>6 ]+ e7 }6 Q  Z4 W0 f* z0 m2 p- O1 Z7 c
  9. #include <arpa/inet.h>
    + |8 L$ N; @0 Y2 W1 Z) R
  10. int main()( [8 I7 v  g) p  u' n; I
  11. {+ j; c; Z  N/ Z& }8 E
  12. int sockfd;, Y$ L! @/ b5 H9 l; u$ ]; o. L
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))' h5 U- ~& J( N' f8 o, k( ]1 @
  14.         {( H8 I& r' V' Y) [5 G( i
  15.                 perror("socket");
    ) [: A; U3 b% o& I
  16.                 return -1;
    ; |6 }; v. [: E
  17.         }
    0 f+ d% H& \. o3 _- c
  18.         printf("socket...........
      g. p1 y8 [0 V$ J) ~8 Z9 O' p# R: i
  19. ");1 m$ N5 \8 j3 [6 X5 Z
  20.         ; \# O& x+ w+ j5 l+ y
  21.         struct sockaddr_in srv_addr;& b0 N5 l' f# h
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    $ C% P( I5 ]* d9 o
  23.         srv_addr.sin_family                 = AF_INET;) G/ D  h  U3 n4 k5 `
  24.         srv_addr.sin_port                         = htons(8888);  s) e2 S4 P$ T4 ^) u/ e, R
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    % j  |6 B% K' q  n6 V9 F) k7 s
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ) Q/ {7 J( K. V2 p, r/ q% S
  27.         {; O6 ?* e- @7 C, V9 M' y; H$ Y& f
  28.                 perror("connect");) K4 N7 Q: @) M; f
  29.                 return -1; //exit //pthread_exit6 L& |/ L  X9 e/ y
  30.         }" b! y0 x+ e' d% f: ~
  31.         printf("connect..............' f/ n1 \8 G5 m* Y, t
  32. ");+ T; r* D. Q7 c& z6 i. Y4 u* a
  33.         char buf[100];$ k4 Y1 s1 }3 f( E6 `* X, l3 u
  34.         int ret;' W3 |6 O" k2 L5 ^
  35.         while (1)
    1 `+ C0 Y& L) X$ N) l) y
  36.         {% v6 Y! D6 G* w  [- n
  37.                 printf("send: ");
    0 o$ y* J+ B9 Z* j& L% i
  38.                 fgets(buf, sizeof(buf), stdin);
    ) Y$ Y& @6 G7 f0 d0 C5 A
  39.                 ret = write(sockfd, buf, sizeof(buf));' _/ Q2 Q( p6 H: ~& D$ x' h$ Y
  40.                 if (ret < 0)
    4 S/ [6 D7 u6 s& F- v
  41.                 {
    3 C0 Q0 m+ y0 e7 w2 q8 U! g
  42.                         perror("write");
    ! `! _" }$ H/ u4 g
  43.                         break;! O$ [/ Z3 d( N5 T4 G9 A% P, K% b
  44.                 }
    6 X8 u- Q9 s  l- ^
  45.                 if (strncmp(buf, "quit", 4) == 0)
    ! ~: {0 x; }3 X' u
  46.                         break;  x# R1 ?& \! i8 H
  47.         }
    7 F  s: N! Q+ J
  48.         close(sockfd);
    3 R$ C- G# o( z! I8 b
  49.         return 0;5 b1 M9 o* e/ U+ h
  50. }
复制代码

6 U% B, }% _. h9 n, @& E' k' Y
" u8 P1 b9 D! j7 N6 }( u
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 11:14 , Processed in 0.173022 second(s), 25 queries .

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