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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

. M" X8 ~- n2 O1 L  [

1 [9 k2 ?6 r; s( ?  f# q# P+ F) Hsocket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
  |/ _( j& g5 }% y
" N9 ^8 N% f$ }% u6 O. I1 h0 B0 |

) X7 Z. v- Y1 r; M7 r" NTCP协议  q! [! u. C; ]1 p9 \
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
9 n% _5 w! a5 f
6 _; N) D9 u/ m) w* t$ o; i
: ^$ D& ~3 S+ @
关键词:三次握手,可靠,基于字节流。
6 @4 j' \0 E2 p% V0 c/ f" m2 {: T% v" Y, y& |

( i* h5 S& }  k可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。& v8 j: w/ \9 [4 J0 y/ H
8 |% @) \( i' b8 y
TCP服务器端和客户端的运行流程
8 i. j! m% ?$ M4 O- g/ P, P9 ^( W4 L% N; E  G* G
/ N- p% o+ E8 O7 p4 K$ e
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
7 f' H& t' Y; B: x' j1 y1 e2 L
0 @% C2 l% E  A: N
; e4 X9 G0 ~. l
1.创建socket
8 X5 n6 H; C+ a" U5 d- \6 f socket是一个结构体,被创建在内核中, R4 n9 U4 S6 o/ Y9 I( K/ o" S
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议! a4 t$ e$ \0 _# y4 R. a
% R& g" R. W: p- t, ^+ B$ q: X4 r
2 t+ @4 q: m( n4 j; }
2.调用bind函数4 _6 S. M; g6 t7 X8 H0 O2 ?6 [
将socket和地址(包括ip、port)绑定。
  I- \: `4 y) Z7 V( _6 ]4 e$ T 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
1 P4 K' ^% {0 f. h/ a* \( m struct sockaddr_in myaddr; //地址结构体
9 l0 M/ f) O4 Z, R- w, r5 p bind函数
, e. q7 R( e8 Y7 q1 n/ o bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
8 i0 i; S! s1 ?" ]+ S, D# N# o% q

7 b+ A& H6 {/ F3.listen监听,将接收到的客户端连接放入队列" d  f& @1 T: c/ {' A' X) d
listen(sockfd,8) //第二个参数是队列长度: ?% i* M1 F: p# ~) {0 C) J

5 K6 `7 C6 g( f. Z

: j8 A8 l0 H6 Z4.调用accept函数,从队列获取请求,返回socket描 述符  B7 _  _) m, K& W3 {/ H! e
  如果无请求,将会阻塞,直到获得连接% ]4 ?+ Q* B9 B3 `  B+ \
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数/ d) ^* K7 e5 C* t: q
) V; M. \0 f% i5 q2 O3 k3 T

! P1 I3 h3 M: ~5 ^$ V5.调用read/write进行双向通信
; Y9 ^$ g) N- B( i( ?1 S, V
0 L2 D2 q  s: t! w
2 m- F  `( l+ u7 q& Z( A0 ]  N# ]
6.关闭accept返回的socket
8 X; y! Z5 B9 S: h( P# f  close(scokfd);
# A3 [' ?7 ^' s, ~4 N* Q
1 o; A+ W' c) t0 R4 C7 O9 P8 G

6 `- h4 e5 E  Z; _
: B$ @$ X: A9 a: C: _1 A) u& J  k
% R; v4 a0 i( U2 o- n% R
下面放出完整代码
1 d* D8 n8 W4 v7 O+ s
" l4 J6 n# I9 B# @# Y/ W. M
  1. /*服务器*/5 ?3 Y7 l( b" J7 b  d& [: |; F/ O
  2. #include <stdio.h>5 o5 K5 U/ m* ~; K
  3. #include <string.h>
    # H  Q$ T# b1 C% a! w2 e
  4. #include <stdlib.h>/ `  F& p6 ?- O% |- z
  5. #include <strings.h>) _) N' A! o( U- u
  6. #include <sys/types.h>( L7 w' ], u, I0 Y
  7. #include <sys/socket.h>( p0 h( \3 ]3 k* g
  8. #include <arpa/inet.h>
    % K5 Q% F+ e/ c. Q- y7 l# \/ B
  9. #include <netinet/in.h>
    5 M  B! u. {- U6 x! j6 M4 Z
  10. int main()
    : c. q0 H3 y* p# g7 [  d- x
  11. {
    . U" c, g% R* E* D" [
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    * ]) v' N! f/ H8 K
  13.         if (sockfd < 0)
    1 v; s: d2 {. w- |
  14.         {
    . }$ Z& q# `* `* }( I2 [
  15.                 perror("socket");. ~, y8 ]& Y. ~, p) Z$ l9 ^7 w; h
  16.                 return -1;* ]4 `- T& u+ k! Z
  17.         } //创建失败的错误处理  @. I& G" V5 }3 K4 O% l
  18.          printf("socket..............
    0 m$ G: i, F- O% I: e. I
  19. "); //成功则打印“socket。。。。”" i' c2 z( A4 l) Z: N5 D
  20.          
    - i1 G/ s5 b: g7 @9 p. p8 x
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    ( s; z2 @; o6 [, X
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)7 K" |: g. |+ C6 c- E
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    ' P1 r5 m; \0 L- ^; P1 x9 B
  24.          myaddr.sin_port                 = htons(8888); //选择端口号9 `, m! p) K& d1 ~3 y5 l% M! u5 ~
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    1 L% V3 {( P8 w. A) i
  26. - b) N, Z: |4 _: a: b0 I& O  p9 T3 X
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    4 W6 e6 T, k/ {( Y) ]9 l5 c8 D2 d; d5 z
  28.          {3 U4 @) }" }) d- b
  29.                  perror("bind");
    : S7 }8 i% k6 ]. U( t
  30.                  return -1;9 Z4 }0 U5 t1 _! `' Z5 a
  31.          }1 G& d  a6 W  O( a
  32.          printf("bind........../ ?4 J9 M8 O# \5 f6 e
  33. ");
    " q4 Y9 a. p7 P8 y

  34. + q! W: |* K' a# M
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听" H2 f6 J+ _% {& f
  36.          {" i2 [6 q8 u' Y2 T, R8 Q: Q- Z3 m
  37.                  perror("listen");+ e' d5 C4 o( f4 W. P0 x% a0 ^# X
  38.                  return -1;4 Y0 t5 `% \- M3 d
  39.          }+ t- O$ a% ~; ?# o& k: R2 G( M
  40.          printf("listen............! E4 @6 p* g9 C6 \
  41. ");
    4 s: V) D2 z* K8 D& `% T& Q- a
  42.          
    / [  f% X) g9 _8 V" U9 {
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求5 i& \" n# f; ], |9 u& r  X
  44.         if (connfd < 0)
    * z9 u& s$ X& S4 F  m
  45.         {
    8 c& ~+ z/ e) V6 J
  46.                 perror("accept");
    / n/ \2 h6 V" s/ @: Y0 I. H
  47.                 return -1;
    : H8 ~6 g# k* P4 v4 S/ j
  48.         }
    ( p6 d  L$ X( g2 R; X, C% V
  49.         printf("accept..............7 }5 |2 d* m& k) q/ u
  50. ");, q1 U* X; C9 t) ~$ f$ S
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    $ u/ j* s8 \8 ~5 e8 g$ L; q
  52.         int ret;% `# D8 J$ l6 l8 q8 e7 `- z
  53.         while (1)
    - \2 t) ~* h. v6 }6 j  R
  54.         {8 H/ J' Z: l/ X( M, v1 f
  55.                 memset(buf, 0, sizeof(buf));
      [8 Y+ q+ M% r1 }  f' s
  56.                 ret = read(connfd, buf, sizeof(buf));/ K, v/ v1 L9 f' ]$ x+ }, a/ X
  57.                 if (0 > ret)
    ( w% L  V3 i  A+ Q7 d: Y6 e. d0 j
  58.                 {$ d* C2 H( T0 ?
  59.                         perror("read");! }/ h: B6 M1 v+ v5 ?. q+ o" }
  60.                         break;" V$ ^3 `5 W) e2 {7 g( v5 V5 N
  61.                 }//执行while循环读取数据,当
    % J* f0 N% M- I7 U+ N2 p$ W1 q2 Z8 v
  62.                 else if (0 == ret)
    , F- h. D8 S" z7 G
  63.                 {% c9 V+ V) |' ?" p5 h
  64.                         printf("write close!
    ( H3 g1 l  f4 E8 D7 x* e+ C
  65. ");& b, x. C% W+ I, T5 l$ @! ]) A9 R
  66.                         break;
    - c+ o7 P  \' D5 m$ v8 m
  67.                 }% q4 X5 j2 t- m; w( o( a! Y# @& J
  68.                 printf("recv: ");' A: j# i+ T' h4 q% j! _; v2 v
  69.                 fputs(buf, stdout);//打印接收到的数据
    6 \# y- q7 h8 [' S
  70.         }+ f: j& J* j/ o1 K  _7 O
  71.         close(sockfd);//关闭套接字" K/ b: I6 Z4 V4 x2 g, O1 z& a( X
  72.         close(connfd);//断开连接0 j0 c- r2 m0 q1 ^; d+ b( @; b) L
  73.         return 0;
    0 e; a0 f# ]" W  U- [8 ~8 M6 @+ w
  74. }
复制代码
" _, x) y; z* V' O- ^
/ Z9 A4 c, e. Y1 g3 @/ a
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)6 S1 A! K% h9 A* g
  2. #include <stdio.h>
    9 d+ ?# N& x3 x
  3. #include <string.h>3 `- Y1 J& H9 I* \& c
  4. #include <stdlib.h>
    7 S. a% P' h" U& H: G6 T
  5. #include <strings.h>
    % h% ?; g2 e4 J: m) Z% m
  6. #include <sys/types.h>' [% u, r. v. G/ Z% Z
  7. #include <sys/socket.h>
    5 n7 S  W- j! O
  8. #include <netinet/in.h>
    5 ~" L' l6 g( W; H5 ^- v1 ]/ y
  9. #include <arpa/inet.h>( M$ \. Q! g) \3 W3 N
  10. int main()5 e  r# m" N$ H7 H9 U
  11. {
    ( M8 h6 M0 k& i3 V+ G- a/ |% R, H+ r
  12. int sockfd;
    & w+ ~- g2 o4 P5 l
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    + g8 E3 [$ Q* d6 C
  14.         {
    . A% V0 c( ]1 [" p2 j6 D! V0 P! u
  15.                 perror("socket");
    # Z! E; T' q- `: h5 h5 k
  16.                 return -1;
    1 \" u! C+ j. P1 a) e
  17.         }
    * m$ D, ^; W; [/ h+ c/ |/ h. U" F8 v
  18.         printf("socket...........3 C: R/ m. X' ~# a
  19. ");
    " f3 ~& f- _; U7 @/ @; A3 e) L
  20.         
    " {5 O9 u. E( g3 E7 G0 z
  21.         struct sockaddr_in srv_addr;
    4 F: _+ R, P/ Z/ y# S5 t& A
  22.         memset(&srv_addr, 0, sizeof(srv_addr));$ g/ J) t* `  F% d
  23.         srv_addr.sin_family                 = AF_INET;- p0 b6 o+ c7 }2 d3 l
  24.         srv_addr.sin_port                         = htons(8888);0 z* Y4 V' y7 T8 c6 K1 ~
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");4 o  W2 ~2 g  i& J9 p1 e0 Q* e! Z
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    / e! E* P# a+ F, ^# t, p& m
  27.         {+ U$ `! ?4 m$ J4 V1 f, z, b$ |2 c! g
  28.                 perror("connect");, T% J+ N. u2 U2 X1 m1 I1 I" h% `+ a
  29.                 return -1; //exit //pthread_exit
      [& |9 X- P1 s. N3 F
  30.         }+ H, j4 _8 J) L  t
  31.         printf("connect..............; ^* L! d( ?3 t1 V" K4 x
  32. ");
    ' w; ]2 z, @7 @( @' Y. `
  33.         char buf[100];
    $ ?3 V3 V8 K# m
  34.         int ret;- n2 d8 U5 i1 E( q( m
  35.         while (1)
    ; Y2 x- B% X! U8 y: ~; R8 _
  36.         {
    7 k' X% q) i7 }2 f2 F8 k4 K
  37.                 printf("send: ");( ~+ p$ O" u2 k, J' d
  38.                 fgets(buf, sizeof(buf), stdin);1 i+ k$ T, B0 L( X7 I4 R6 q' w$ a  E
  39.                 ret = write(sockfd, buf, sizeof(buf));
    , A( H* Q4 @4 U
  40.                 if (ret < 0)9 }7 y  |! o" q+ c* @
  41.                 {
    3 X4 r" x" \% Z7 @+ u: a
  42.                         perror("write");2 Y+ w+ d0 Z5 m8 q2 c/ R
  43.                         break;
      U1 B& G9 U1 k1 k5 C+ v# T
  44.                 }
    % g; ^7 z* ~) T+ l, V$ M/ @: Y8 n
  45.                 if (strncmp(buf, "quit", 4) == 0)
    3 E% T/ u- Q1 c2 y& l2 _2 b2 ]
  46.                         break;5 `' I) |/ C2 M+ |
  47.         }; X6 W3 H2 |2 u* L7 L6 L
  48.         close(sockfd);$ _; `$ \( s# Z% {. |
  49.         return 0;5 i9 j; p5 o# F3 @( {# Y
  50. }
复制代码

2 n6 n* C. N$ j0 w( ~
. K& R2 m$ e/ x" [) [: I& D/ E4 P
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 22:39 , Processed in 0.112606 second(s), 22 queries .

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