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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。$ h8 A4 O: s3 F3 ^
# k1 D. a0 b9 X
1 U! F+ G' ^, o
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。" x- [1 {, J7 U# Q4 j
9 U& u$ `* Q3 z5 g" B2 @5 ]+ u4 t

+ B- q# H3 G) p! I0 E7 xTCP协议9 o. Y9 F3 u6 v
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
9 N- V/ ?  K. t0 w, b; r- M0 K! w& a

/ X# n# y" V$ M1 F7 ~1 s  \- B3 c关键词:三次握手,可靠,基于字节流。
. j3 F5 h2 M$ K, e' q# w- U$ u- X$ B

; o/ P( |$ y+ o% D& _可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
" }6 l" Y* W# {0 w3 L  C
8 k- J4 t3 S0 H& N9 m+ bTCP服务器端和客户端的运行流程
" `9 Y: x2 y# E! y/ B9 k3 c
+ Z: c: o7 m1 K& r. T: R+ A
$ P( ^) Q4 m4 ]
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?0 M) W% F- s) N

$ Q+ u) G8 t6 n+ o! f1 F
" m# n) \3 K3 f! [- h
1.创建socket
4 d6 R; U4 H2 i8 N7 j  w socket是一个结构体,被创建在内核中
$ Y$ V( c+ |, L7 _0 s/ i  y% T. |( M sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议' |" p- i7 j& M- ?4 m* e
- j: i. c7 v# y, Q
* |6 A# I* Q, {. Q: f4 L
2.调用bind函数
  X) {+ {/ K0 |& U; Z 将socket和地址(包括ip、port)绑定。
. I. L( y1 i: B9 \ 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序/ C0 ~5 Q, b1 s5 v" z
struct sockaddr_in myaddr; //地址结构体$ ]+ {( {; q. C! W) N) M2 v4 p
bind函数; P- N4 m0 z  \2 P" @" B8 v
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))7 ^/ F6 b* a+ y4 I/ q# r
1 |! Y! M# y# @$ Y, h( p

+ g! D* g8 }' t3.listen监听,将接收到的客户端连接放入队列# s" o, X5 N$ [5 ^. r+ L
listen(sockfd,8) //第二个参数是队列长度
" {+ Q# L; N& v4 Q" N1 l: D" T5 C8 h+ ^9 ^9 |' l
( m% o7 J+ I* N4 z  s: z6 r
4.调用accept函数,从队列获取请求,返回socket描 述符
; c! u7 e  |* \# x  如果无请求,将会阻塞,直到获得连接- M) g3 e! E; H7 G
  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
8 F! J3 o5 y; t( ]. o: c. f7 f% }. F. j/ Z

. n3 l) l; X  J* y5.调用read/write进行双向通信
7 Y3 d, ]4 [7 L( r. h" Z- z, G6 [! H0 T3 p, S" l
8 X) i" F2 ~! s( T- d1 y
6.关闭accept返回的socket
9 M3 U  q6 o; ]  close(scokfd);2 ?* D" i( d4 `. i
& k: m4 Z/ k- S9 L6 W. Z

6 ?' P* X" ~7 P! A
* L5 q2 J8 g- D  C( x8 R8 j5 R  Y
6 D' U9 s- ?  I" z  Q& T! M
下面放出完整代码+ c+ o% s5 m9 [, n# F* m

/ h; u; Q) q" {5 _: B
  1. /*服务器*/! `- I: e3 E7 j  L( f
  2. #include <stdio.h>
    ( ?% }! P; F+ E) L5 W+ v6 o/ Z4 Y
  3. #include <string.h>* @& F& L* P1 o+ O% D
  4. #include <stdlib.h>
    5 p5 o3 r* }' m# i7 z! o7 c" z' ^
  5. #include <strings.h>
    2 h. O# e8 K" ~, R
  6. #include <sys/types.h>
    ; u- d. D: L( g
  7. #include <sys/socket.h>( C: p9 O! V, S5 b& s
  8. #include <arpa/inet.h>8 E0 B3 Y; E0 y5 F! d' S; y
  9. #include <netinet/in.h>
    . V: M* l. V; A( Z+ d5 K
  10. int main()
    / y7 {) Y- N- D6 c* n" U0 Z+ j
  11. {
    0 r. e7 s4 r' W* h5 T+ y5 [
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    # W! ^# k( e0 {: h. n4 m
  13.         if (sockfd < 0)
    1 ^9 P, X8 ?9 I7 b9 o; L2 o
  14.         {8 I$ H9 \. F) g' K% M4 ^
  15.                 perror("socket");
    # ^' d2 t" ]& E6 t9 v- _
  16.                 return -1;
    6 X2 s% N+ H6 f  Y. |8 n: z
  17.         } //创建失败的错误处理
      A5 W3 O- w9 h7 w4 v$ q- ^
  18.          printf("socket..............
    . S/ R4 Q+ X7 Z6 J2 w, a: L; `
  19. "); //成功则打印“socket。。。。”( v4 c/ @. U: M; B7 D0 R! @+ `
  20.          
    - q. ]1 v. Q2 Z5 _
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    $ B$ p4 n1 a8 ]2 i3 a
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见): y7 N/ R6 C; J5 o* L3 m% F( a
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型
    0 w2 O" @5 Q5 y/ [
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    ' k1 W/ Q8 Z. ^0 f  Q: [  H
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址4 y  ^3 r3 a2 }: L; {/ F( T: ~! x

  26. 5 h; z+ J0 B  [$ F' g; ]8 H2 a
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字1 h: B, J$ ^6 W9 [
  28.          {/ J5 L7 S- L" H& S0 M% r, ~
  29.                  perror("bind");
    ; _  {6 W6 x& \  `" ]
  30.                  return -1;
    7 ?% l1 s. W5 Y+ F! N) ~" x
  31.          }) Q+ \6 u/ ^/ h3 v; O/ b
  32.          printf("bind..........4 b$ f6 w* M# j% h
  33. ");* p! E% q7 b" }. D5 N* V1 L

  34. ! N0 U5 t( Q% f# h7 N$ ~) ?
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    - L, o& C5 l% Z9 _; q7 N! H; O' X
  36.          {$ K4 X" M, _& G" O. |
  37.                  perror("listen");
    $ C: w+ p; \2 x9 ?# e
  38.                  return -1;4 Y) ^, {  j& l' k( d" w
  39.          }
    + }  t- k+ c- ]
  40.          printf("listen............$ r+ N( b! ~0 @
  41. ");# ^: |$ k) G% o9 g) r
  42.          
    % s- g* D5 v' E, Z7 Z$ S/ N& r
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    4 v- s& S9 T! w. r& p: v8 t2 U
  44.         if (connfd < 0)
    : `! r8 g* W8 v. z7 _* X- ^% t
  45.         {" L9 n4 J5 x4 h/ p3 T6 F
  46.                 perror("accept");
    4 w# @7 K# `0 F+ ~
  47.                 return -1;6 l" |3 h; M/ |3 t% h
  48.         }
    ( k- s( W3 g. R. j
  49.         printf("accept..............
    1 J+ o% S6 X: Z# G
  50. ");
    % ?! y9 \# r. ]* y
  51.         char buf[100];//定义一个数组用来存储接收到的数据2 }  U8 n, j8 }! {/ i& J2 S$ C5 W
  52.         int ret;/ x$ ?9 W4 T. r+ J& d# S
  53.         while (1)2 H: _4 G% \8 V6 v( x
  54.         {
    3 Z' ^! ?0 y* f" D
  55.                 memset(buf, 0, sizeof(buf));( @  ^: t2 w* t5 T3 \: X& T
  56.                 ret = read(connfd, buf, sizeof(buf));
    ' h" @# r2 @  v- e
  57.                 if (0 > ret)1 w* ^1 `, n8 G7 d3 k! [! `( L
  58.                 {3 n8 k0 i) j' ~/ \* {5 X
  59.                         perror("read");
    % L7 P# E1 N" X$ v
  60.                         break;
    , t7 t+ @/ O8 L6 \' O9 C' _. n
  61.                 }//执行while循环读取数据,当
    9 A5 D! v4 ]) j8 ?: \
  62.                 else if (0 == ret)
    # S7 |* I+ N! x( `& J3 _7 f
  63.                 {: M; J: e) @8 p% c
  64.                         printf("write close!2 s% N) R, t; _' g- j
  65. ");' \4 N3 t! ?, y7 [% Q
  66.                         break;* j+ O3 F% G; u7 a7 c, p
  67.                 }
    # t+ y0 a9 d, I
  68.                 printf("recv: ");! y; @8 n! @! s" V0 v7 N. \
  69.                 fputs(buf, stdout);//打印接收到的数据" n3 j5 D! K/ c3 M0 L
  70.         }
    - n' Y$ S8 W, W8 I) R
  71.         close(sockfd);//关闭套接字
    0 r/ h" W6 {+ ~( T" D1 g  p6 H# N
  72.         close(connfd);//断开连接
    . I* h" Y) G3 q  \
  73.         return 0;
    : n; g' U9 C5 q9 F
  74. }
复制代码
* O! N+ M, e7 s  y: H( h

* r) a* K( `* A2 q1 M0 ?9 L
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)) m$ Q' T  @5 ~
  2. #include <stdio.h>
    9 A3 G9 k+ d4 h' _+ x* j  r8 H
  3. #include <string.h>
    3 y" [! G: O. Y( S
  4. #include <stdlib.h>
    7 ?4 {: f$ k/ |" k$ z
  5. #include <strings.h>4 {* Z+ Y) q  J1 E7 \  S
  6. #include <sys/types.h>
    " K" F( Y! U0 j* L1 U
  7. #include <sys/socket.h>/ p7 r) y2 z6 t* m0 M0 [( h
  8. #include <netinet/in.h>3 W# [5 Q, g0 G+ w# X& T
  9. #include <arpa/inet.h>  z- s4 F  I4 I8 f7 W/ d1 A  p
  10. int main()
    ; Z0 `  u2 o8 U/ {
  11. {9 f/ V2 Z3 \9 G. t
  12. int sockfd;: D7 H* l5 L* |* K0 A
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))! y1 q% T$ e! e. c2 N. X$ i* S% a( k
  14.         {
    7 e/ A! h  P# D" }
  15.                 perror("socket");
    + c5 }& L4 s9 N$ E, o6 {
  16.                 return -1;: g4 e1 e! L8 [4 s+ r
  17.         }7 }" j& k  m8 ~" e5 y7 Z8 |
  18.         printf("socket...........0 N, k2 a5 m1 k$ u7 m9 i* U! b+ H
  19. ");. _0 H3 E* r# Q+ R) s; [7 \
  20.         
    % s- s1 I& b7 g, q( N0 H, h
  21.         struct sockaddr_in srv_addr;7 k- c! g( G1 j2 c; c. j3 G0 Z1 N4 O- i
  22.         memset(&srv_addr, 0, sizeof(srv_addr));% X/ f' g$ h- M" t
  23.         srv_addr.sin_family                 = AF_INET;) C# S; v% A: m/ x
  24.         srv_addr.sin_port                         = htons(8888);
    9 K/ k" c: m# ~0 z; q+ E6 F
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
      V* E+ l1 }# D$ W$ A+ P) u
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))8 U9 U! u, g" }3 o2 R
  27.         {+ ~% [* z9 F6 m0 K2 s" y0 ?3 Y
  28.                 perror("connect");
    + o9 P5 V/ S/ J: G- p0 i6 q8 P+ d
  29.                 return -1; //exit //pthread_exit
    7 n# R# x1 I  A6 {! P
  30.         }% a+ m% ?+ j# U
  31.         printf("connect..............0 M" g7 h* [; q2 s  [& B! c
  32. ");
    ; w8 }& H  Y. O+ ]7 E8 J/ {. }; \
  33.         char buf[100];
    ! @9 \7 j- I. r  e+ N6 W
  34.         int ret;3 M* I* p: B  T
  35.         while (1), _' g8 }! r6 t/ L
  36.         {3 \7 I& B! b- z. K# w+ A
  37.                 printf("send: ");
    4 R( A5 A/ A6 m3 X2 ]
  38.                 fgets(buf, sizeof(buf), stdin);" x, K. P; s% h! B, H
  39.                 ret = write(sockfd, buf, sizeof(buf));
    5 C/ a) `; Z: ~0 w: H  n* I
  40.                 if (ret < 0)7 t2 i, s: K6 y- g! a+ [* @$ i
  41.                 {
    / b2 F1 L/ e& g6 u
  42.                         perror("write");4 Z: C0 x- x4 d2 x. Z2 ?" H3 [- L
  43.                         break;( f' t/ m4 O' i6 E- h5 [
  44.                 }" w: c& @2 S, h8 A1 S
  45.                 if (strncmp(buf, "quit", 4) == 0): b1 |, A6 N, h9 Q# m! @
  46.                         break;1 P) Q6 [6 C7 U! R1 {8 u
  47.         }# L- {! ~: B/ |0 q6 b( c
  48.         close(sockfd);, ~& K2 a1 a: C3 k+ O3 ^
  49.         return 0;
    ) O* \! ^  k0 ]8 l( n  ~
  50. }
复制代码
) Q- j, {: \" ~7 N, Q  @

* i8 x% t+ Y# k& N1 j' H2 C) w5 ~
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-9-21 03:18 , Processed in 0.079980 second(s), 23 queries .

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