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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
5 b5 n& f7 o2 T/ s, k- o7 I
% d% P/ V, I! K6 y  i+ k) h
" d0 h: B% ?5 l
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
6 K# J; N8 N# Z. N
7 ?; e; ]2 j. ]4 L& N
0 I+ [  ~+ x- c: V1 k
TCP协议
; n$ O+ v0 \- C( oTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
. x! m3 V0 f0 R* _- d% Z% |9 j- M& l, h* w9 R

* c- @/ u! C" {关键词:三次握手,可靠,基于字节流。  X& W+ k" g- j! G- z

4 X0 u$ `) q" [, g
5 C0 K$ f4 E. @9 o* N
可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
; d, A3 \  |" r8 n. ` ! ~, Z  S7 G0 v- F8 Q
TCP服务器端和客户端的运行流程
1 ~+ w) F& g1 k3 S2 [5 Y7 l% W2 x' O% H, u- |4 B( Q4 v
1 \! M% ~9 e$ q0 X  v
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
- r. u' [( t+ r$ r9 y7 ?" b$ b& d1 y, u7 W3 H
( K, H& A! B# y3 e+ e" x* O
1.创建socket
5 ~4 a) ^+ j4 b+ v) p socket是一个结构体,被创建在内核中
9 x( P6 k) Z0 N# ] sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议7 D$ d0 f% B% T% m3 Q8 t2 R! M
8 K2 c: e3 E8 L  e
4 n) L5 E3 u3 `. B, T* M8 ]! T& A, ^
2.调用bind函数. D1 ?: ]$ B4 ]
将socket和地址(包括ip、port)绑定。5 L) V' f! ]" b
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
  J$ D- a2 j2 D! s$ Z2 W2 W8 E struct sockaddr_in myaddr; //地址结构体8 ~: D0 \' x$ D: o5 \7 _- b
bind函数
6 S6 V2 h$ n$ q& N$ X bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr)), ?8 A: k: t8 I, e$ B

/ Y5 h5 I$ y3 m, X6 J

: W* T8 U9 A7 {% A3.listen监听,将接收到的客户端连接放入队列3 Q2 C/ {. d7 \2 J7 {6 v% X
listen(sockfd,8) //第二个参数是队列长度
% X! W' ^  p( Q. g* \& ~; i* f# |# v; s

3 o6 R3 }5 R+ \# u4.调用accept函数,从队列获取请求,返回socket描 述符8 \6 d( J1 T4 m# `
  如果无请求,将会阻塞,直到获得连接4 c$ Z( u% }1 p1 a7 F
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数) n: w( p# [$ Y2 p/ F1 c, |, ^0 e
  K& H8 i/ F+ X8 p& Q

, |0 E& l. A1 J8 {- m. m; V# W5.调用read/write进行双向通信
3 p# g6 P6 V  j1 f" [" ?2 v# o- o3 c3 c- H( o4 E2 M- P& B. B& Z
6 W$ S" X* T. O
6.关闭accept返回的socket
! K$ c# s0 L. O  L& ^  close(scokfd);
9 ?% P. D' y* V: a
& Z% B- p( `% i( @

' c4 O- W+ r3 J' ^5 p  V9 ]5 p6 L3 u/ _+ W  C- n. f

& [- W% _9 ?6 l4 l4 m! N3 v下面放出完整代码$ n  ]. o2 z1 }3 C1 i

9 ]* |" ^  e# g) O% ~- Z2 F* n
  1. /*服务器*/' Y6 L, H0 Q: Z9 J
  2. #include <stdio.h>
    / o  {. l9 p9 l2 X6 h+ W6 C  D& a
  3. #include <string.h>
    % T8 }4 M! [5 D4 }
  4. #include <stdlib.h>  F  R9 W. m2 l/ L" n/ N$ L  G% j
  5. #include <strings.h>/ o1 l" t/ \4 r0 Z1 \
  6. #include <sys/types.h>6 F& a( W4 `" ^
  7. #include <sys/socket.h>
    ) \6 ]" R! W* a* l+ m( j% K
  8. #include <arpa/inet.h>( N( L  ?7 J; R; }
  9. #include <netinet/in.h>
    5 o" ?/ @6 q! q+ q! U  W. P* v7 T
  10. int main()0 O. E& O7 d( ^2 f
  11. {9 F4 v' n2 A$ C3 i. A% A
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字& w1 @3 @# J4 N: w/ A. g
  13.         if (sockfd < 0)
    & `5 Y! O- q* \' x" a
  14.         {
    8 X" p+ z2 ]" G& _# d4 S! N
  15.                 perror("socket");+ k3 g& `3 `* ^8 l7 p
  16.                 return -1;
    ! D' _1 R# V6 K  L5 D: o
  17.         } //创建失败的错误处理
    1 M9 S0 |, M, e
  18.          printf("socket............../ ~- i; V7 B0 v/ u" }
  19. "); //成功则打印“socket。。。。”
      \/ O" i/ B$ i
  20.          & f3 u7 a; p# z, X, `0 D
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体( D/ [1 n( i4 i% K
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见): M6 T; J; [/ b0 D0 W; @3 r9 c
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型5 }2 w" U" l8 M! W9 W
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ( w+ ?0 T( N% ]' W- Y
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    % y/ U# L) c9 p+ J

  26. . k. v0 w- h- F* h( M3 i
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    " D/ e. e3 D6 T0 E9 G8 M9 {7 g7 m
  28.          {
    , [6 B9 o% ]: _! v
  29.                  perror("bind");
    % z* ?% z9 l9 x3 \# |% X: _5 p, T& T
  30.                  return -1;3 ~  T  M1 _" C( W4 _0 ~# Z6 `
  31.          }, m& j2 ~+ E8 E4 D. L' n! Z
  32.          printf("bind........../ b4 x+ Y1 |* p6 R) a7 q2 `' `
  33. ");- S  d* E7 C! _, |& u+ E& ^" }
  34. : }0 E' ^2 x: U/ `6 w. W* @8 q. m
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- b. M. C) s4 Z
  36.          {
    # N# x: @/ C) f* a
  37.                  perror("listen");
    ) }  E7 v$ L- {- M' c  J
  38.                  return -1;' a  i& @9 q( d( d$ {& @
  39.          }
    , w0 l; z+ ^. g& b) U/ m  T- Q" A
  40.          printf("listen............
    ' f; u& M' b7 d0 t8 W4 L
  41. ");
    , y% F; F. O7 k5 J3 w5 ~* ~
  42.          7 ?7 b1 H9 H! _4 V( {4 k# o
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    ( c5 |0 \2 Q4 W3 j0 R0 H: @
  44.         if (connfd < 0)9 Z2 |# y# c5 ?' `
  45.         {% X" j" `0 |  j2 `! x$ {3 f
  46.                 perror("accept");
    + n2 P4 o  L4 q5 F, I8 h
  47.                 return -1;7 o3 V% m( D3 K% a4 M% o
  48.         }
    " t' l' W+ M9 ~. I2 X4 r
  49.         printf("accept..............
    4 `9 Y1 _8 X4 j0 `+ ]# s' {
  50. ");4 c* c% d4 G1 M6 m% C" K, i6 W
  51.         char buf[100];//定义一个数组用来存储接收到的数据& l3 Y# F' c9 o4 m* V
  52.         int ret;
    2 `/ F# Y) P$ R9 a) v' t2 x* Q
  53.         while (1)
    : ?1 B7 H* v  |2 h, J3 X9 q
  54.         {, j. F* U( f8 A! j; A8 t: ^& M
  55.                 memset(buf, 0, sizeof(buf));
    8 c) R% p  A$ j) e
  56.                 ret = read(connfd, buf, sizeof(buf));
    7 N0 J$ c6 f& |9 s' u( k3 S$ o- M! K
  57.                 if (0 > ret)! I+ P7 C, y$ n# e4 Q' T
  58.                 {
    8 u  K4 |' Y; j) G5 Y
  59.                         perror("read");
    & e9 ]3 g4 ]8 Z% s+ a! |, D
  60.                         break;  W/ V4 u) p, ]0 X8 e
  61.                 }//执行while循环读取数据,当2 ~3 \4 @3 C+ s* t5 {8 f; o) \# a
  62.                 else if (0 == ret)* w5 a1 z- }+ |6 B6 R5 H; T4 W
  63.                 {" J4 t7 P6 D  R9 f8 a
  64.                         printf("write close!
    $ e2 X$ C. l1 [  H
  65. ");( n; f. w7 U2 c) j+ u1 \
  66.                         break;9 _7 N: F  F$ o
  67.                 }% u0 F' C9 X% ~  S
  68.                 printf("recv: ");
    4 i6 y% R. u! \# @
  69.                 fputs(buf, stdout);//打印接收到的数据
    3 W! G# D8 ?5 h- q
  70.         }
    6 h# O+ ?/ u1 S* J+ g1 z! A7 u
  71.         close(sockfd);//关闭套接字% h2 m9 v7 L% u& _$ e6 j
  72.         close(connfd);//断开连接
    % l% r( Q8 k9 Z" \
  73.         return 0;: t8 m& b; U  J4 `3 k$ n& N0 p
  74. }
复制代码
' ]- S9 u: `$ G

* M4 I6 ?$ H1 b7 i5 R- }2 l/ U
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释). _; I8 e& }0 ?" L1 C
  2. #include <stdio.h>3 A; {9 B) `2 V+ w
  3. #include <string.h>! h0 ~5 W) e8 l3 _
  4. #include <stdlib.h>2 X5 W6 r! j9 U0 E2 i5 @
  5. #include <strings.h>& ?5 H3 k/ `9 `6 ~4 L1 A
  6. #include <sys/types.h>
    3 h( ^& t4 F+ R1 v
  7. #include <sys/socket.h>
    3 I4 P  u2 a5 E
  8. #include <netinet/in.h>
    " r7 D. ]" k, c7 y' I, c5 a
  9. #include <arpa/inet.h>
    3 U5 E+ }2 s: g5 V) \
  10. int main()& k6 @# e& n  B
  11. {: A  h+ ?9 X1 P8 ?% G/ `$ j" w# C# u
  12. int sockfd;
    & \' ?: {( n( X8 |: u
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    6 {  W9 O4 z5 ]) b
  14.         {3 f' x0 l; i: x$ D8 m7 }
  15.                 perror("socket");  q7 H2 F; G9 V) R
  16.                 return -1;9 \! d, d0 A) U; q" L7 {
  17.         }; Z* J$ V8 [/ }( X0 j$ ]
  18.         printf("socket...........! k- }/ Q, x+ q% i" Z
  19. ");' Y1 Q! b+ r5 R( M* X( b& P
  20.         # r) @2 t: O' S+ e
  21.         struct sockaddr_in srv_addr;
    # H" A$ ^( X. Z. n1 n! O
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
    - P& M0 y; l' K" Q$ `7 c; ?
  23.         srv_addr.sin_family                 = AF_INET;
    1 v3 L5 D# W& S3 Y
  24.         srv_addr.sin_port                         = htons(8888);2 H' w$ m( Y* D- |
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");  F: E7 D  [8 _5 K9 F9 o& T
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))  [  V+ M7 n9 \! X3 R" z+ i
  27.         {2 y$ Q1 g; E  j
  28.                 perror("connect");
    , }9 |( j( |# y& y' t+ z
  29.                 return -1; //exit //pthread_exit9 |# J2 V- y, {
  30.         }
    1 [% D# x7 h) T$ t# Y- Y* B
  31.         printf("connect..............) _* L/ q5 m+ c$ d
  32. ");
    : T, b2 w  d/ S& j7 v: a( ^, }- M
  33.         char buf[100];
    + k, d. L' Q7 M& h& x  S! [
  34.         int ret;
    6 Y- @8 _" w9 {7 q" j, a# K6 w! U
  35.         while (1)3 o! }& H( [& J2 t
  36.         {" O- d9 Y: H" Y. ]$ h  |
  37.                 printf("send: ");
    * N9 ?( ?* ]8 R) t/ }
  38.                 fgets(buf, sizeof(buf), stdin);
    0 q9 v; c' O6 S& |( t( D8 l
  39.                 ret = write(sockfd, buf, sizeof(buf));1 g9 z4 H: ]0 j) r  w
  40.                 if (ret < 0)( l) D, `8 i. z5 G8 t# R
  41.                 {
    * A0 W# q0 j: h4 v, C9 X+ D( k2 ]0 o
  42.                         perror("write");6 s2 R$ E7 C, }( d" I
  43.                         break;% X) Q' f) z$ V% G' Q* w( H% ^" |
  44.                 }: n4 ~5 n1 \2 ~
  45.                 if (strncmp(buf, "quit", 4) == 0)! |4 P' O7 T2 l4 p5 r$ O
  46.                         break;! H" v8 |1 e: u9 m7 K
  47.         }
    : V1 y: u8 e8 a# L5 |
  48.         close(sockfd);
    ; a; k9 A9 g- a
  49.         return 0;
      _6 V5 Q' k5 V- \
  50. }
复制代码
; s/ T, x7 R0 Y3 [

6 i) L- X' q; H# |8 D+ {9 G
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 06:26 , Processed in 0.115128 second(s), 22 queries .

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