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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

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

% C+ j; \( l0 F1 F& ^5 l

# H0 p6 o/ ]/ V! d; S/ c' p- {socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。2 W4 U1 _" ?& o

) v3 U  K5 w0 e* m$ @/ U
! ~3 h( i9 g' w8 A7 R3 p
TCP协议, [! K. _2 W2 L$ F. U) ]
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
, H4 X# g1 z5 Z  L
! M- Q: E! U4 B* s1 a( E

- U7 f' y: }% Q! F' T关键词:三次握手,可靠,基于字节流。! {3 |7 ^5 D2 @' L' w. _2 h1 }
6 |) h1 A: f' h% O

/ t1 S' p6 z1 s+ b( g可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。. s- I( E- b+ a" P: g" }" R1 f- y
' q2 G9 X9 a/ {! e
TCP服务器端和客户端的运行流程6 P; q+ }! n: [7 {) x) S* G/ u( |: m

' A# }( z4 o/ E% X

. `* ~$ _+ K' E如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?0 Y6 M* x0 u: y5 T: A0 r& V  C' i/ G+ e

. O! U, z2 Q$ ]1 g  H% I& i

+ m& N% P0 J1 [1.创建socket9 z. {) f+ e$ a
socket是一个结构体,被创建在内核中
7 V1 I2 K1 U4 j# {8 a sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
5 W+ [" q2 j% f, n; q4 `
, _' j4 Q4 d4 k% ^/ L0 x6 \
" M* g' l  K, M5 K
2.调用bind函数! p; `% b& k2 g: F0 j
将socket和地址(包括ip、port)绑定。
6 j: o7 L9 [; ^5 c 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序0 y5 \( G* w1 i9 |& d, x" [
struct sockaddr_in myaddr; //地址结构体
$ Z/ y8 Z  ?8 _1 _* K2 W  M bind函数. o' l) d2 S! e1 o1 D
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
( J& c$ [$ |0 U2 `; v/ F- n& V$ ]2 p8 @5 r

/ S0 l2 O  a4 v* W6 y9 ?5 c& Z6 k% C3.listen监听,将接收到的客户端连接放入队列
+ B% m5 k; @9 z) |! j listen(sockfd,8) //第二个参数是队列长度
" ^$ n4 q& N5 e: Q; c" n' U8 ]% \& ]5 z/ z$ w3 S' K" v$ D
6 _6 N+ M5 h" l) h6 a( E' p! X
4.调用accept函数,从队列获取请求,返回socket描 述符
: X0 I$ ^3 [/ M& n. M  如果无请求,将会阻塞,直到获得连接
1 Q; h( A2 |  E" v. }/ j5 k4 f/ E  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数2 V* s, n* n7 A: O5 D
2 C% A. c" p5 `! \. L
5 r) ^% e/ q1 Y+ b. v& S! p
5.调用read/write进行双向通信) ~8 e3 B2 k& Z% t4 Z8 W
  }4 E0 N8 I' w

' S: @0 R& }; i+ ~% A# I4 t& v6.关闭accept返回的socket- d. b+ X  {: S
  close(scokfd);; `+ A7 ~( x; X' ~6 q% }

& M, N5 ^1 o. N, K6 q
# Z( W# T" y% C& D6 o3 ], t
1 G, W$ G7 r- x2 k7 T" s# B

/ ]# g  Y0 Q( Y3 f6 A8 V下面放出完整代码
3 d7 @9 ?+ S7 N1 m- h8 g; Z9 D6 |+ [% F$ |+ O) A
  1. /*服务器*// d$ C, Z3 h6 a( ?
  2. #include <stdio.h>
    # J2 o; |7 j( B' t
  3. #include <string.h>
    3 V  W# C$ P  L$ m0 E" C
  4. #include <stdlib.h>' b: z1 L+ q; U' N2 S7 \. z
  5. #include <strings.h>5 W" D0 q' F  v2 a4 P# V
  6. #include <sys/types.h>
    $ @. {" L6 J. E1 B5 c( a, h+ O' W
  7. #include <sys/socket.h>
    + e! {+ b& {  s* G
  8. #include <arpa/inet.h>& u9 Y' Z! k; R+ H" T% l9 ~+ W
  9. #include <netinet/in.h>
    1 J. ~5 B/ ]4 x% q
  10. int main()2 z; I- _0 ~/ F. O6 T
  11. {
    ! O$ A/ W, R. F2 C% e
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字+ F. f# T7 c5 t) n6 @8 ]0 K
  13.         if (sockfd < 0)+ n/ M! i9 N2 P; m# C% ?
  14.         {
    ; b( G0 ]; m: q$ |
  15.                 perror("socket");
    $ B) c& C- e' M; g
  16.                 return -1;6 C/ ]7 M/ k3 Z$ e  H
  17.         } //创建失败的错误处理
    ; O' j. }+ T* D+ A! u( o
  18.          printf("socket..............
    : U$ U& [" R  E/ S3 a; |
  19. "); //成功则打印“socket。。。。”
    4 Y9 |4 Y  a2 N9 @3 a# S
  20.          " Z1 X- j; m$ d5 W6 z7 h, P
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    , J. H" n  N- P' G8 N
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    3 r9 T9 W2 {# i+ R
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型; ?- Y& P. `/ I3 l4 A) L) b
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    - r, S" m0 w, R3 [) Y! v
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址
    2 j2 I# W1 F" a/ X0 a, I1 W

  26. 8 O1 X6 p; S3 E8 {9 B/ k4 C% f
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字4 O2 E8 Y/ y4 s$ p
  28.          {
    3 y8 Z& _! k" y' O9 e
  29.                  perror("bind");
    0 M  F# W; W. B8 n( E( L
  30.                  return -1;. d; n. R) Z. i& W$ c
  31.          }# S" z9 m" F5 }& X5 O
  32.          printf("bind..........  y! A& h) m% ?+ M# c3 b
  33. ");
    2 l$ M* i8 y# d6 v+ f

  34. 4 a& n- j, h, Z& n8 l) F- Z' N- l/ e
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听8 |8 O9 A. N, ~) [* T
  36.          {" E# K( G1 c+ u; h1 U( S+ e
  37.                  perror("listen");4 O6 k+ g- \3 k% B' \) u6 v1 u
  38.                  return -1;" e2 c  s& E/ J, ]2 }" M
  39.          }
    0 ?. m4 e5 ^+ z7 @2 s
  40.          printf("listen............
    ' N2 X" s  l3 I8 K+ ^
  41. ");
    % o, J" }; X7 Q: V% e( ?# O( O2 U
  42.          & H5 k9 g" I8 f
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求
    1 Y0 ]- b( e  f3 K
  44.         if (connfd < 0)
    4 B  ^* T7 `3 u& R
  45.         {2 m* a  Q$ Y6 B6 {
  46.                 perror("accept");( |9 Q7 P# T9 u4 t- Y
  47.                 return -1;. N! v5 G: u9 v# r( R( `5 u- n& l; \
  48.         }
    $ ^/ z5 O) f( k0 i8 y" S
  49.         printf("accept..............
    7 I* g2 w5 Y+ f; Y/ A/ _
  50. ");
    " w% u( m' O$ h
  51.         char buf[100];//定义一个数组用来存储接收到的数据) ?: M+ ^2 S4 ]; R& {
  52.         int ret;
    , ]# ~& F; U4 b
  53.         while (1)
    / n, B. @$ f/ A. T. ~0 p
  54.         {
    0 n1 _4 U3 E- z+ \
  55.                 memset(buf, 0, sizeof(buf));/ v7 X8 M0 b$ Y; N# q  D8 h5 E& @7 B
  56.                 ret = read(connfd, buf, sizeof(buf));! V9 Q2 H) f; o4 O, B
  57.                 if (0 > ret)
    " E$ p8 J+ k" u. @2 S
  58.                 {
    / u( E. }' h0 B
  59.                         perror("read");
    5 E0 J( a# m$ d5 f
  60.                         break;
    0 J) ]/ u+ v9 U
  61.                 }//执行while循环读取数据,当
    ! v+ t, V& X- \! o1 ^4 X' w4 @
  62.                 else if (0 == ret)
    $ N8 q; f! A9 D  ]8 ?' I
  63.                 {, c* d2 u* |' u# M% D' F
  64.                         printf("write close!: Z+ [6 I$ ]& x" F
  65. ");9 O& p. Q, `% A6 q' Z1 V
  66.                         break;
    0 {, F2 ]! g2 I3 y8 L, z5 o; h
  67.                 }1 u+ m8 J; @0 J" @
  68.                 printf("recv: ");
    ; d: @  s( a, J# M0 t" o3 b1 ~
  69.                 fputs(buf, stdout);//打印接收到的数据9 ?/ {0 `9 c) C' p! @/ s
  70.         }# I4 O. c0 _0 V  p4 l% A) d0 [
  71.         close(sockfd);//关闭套接字
    ' y( X& h) [" V! w! F9 m0 Z9 p; D& E& M
  72.         close(connfd);//断开连接
    4 _2 ?7 f* `2 s+ F+ u7 g
  73.         return 0;1 e! p6 }. e* A: }3 C
  74. }
复制代码

# Z! q  E9 V- I* l
7 d" `/ S7 O) y- t
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释): O+ ~/ H/ z8 X3 Z0 ?% N4 S+ ~
  2. #include <stdio.h>
    3 K0 v0 Y! U, c: U
  3. #include <string.h>4 U0 j% O' i6 ?. m
  4. #include <stdlib.h>7 I2 L2 e/ B: m+ u
  5. #include <strings.h>  T% R/ P, }, O" k- }. Q; p& G
  6. #include <sys/types.h>
    2 W" z6 o, D0 G
  7. #include <sys/socket.h>% ]* ^2 f3 V" H! U- D3 M) z* x1 N
  8. #include <netinet/in.h>
    ; s8 x* L# N: j9 f) M2 C1 N* m. Q
  9. #include <arpa/inet.h>; Y# R1 g3 C+ S  v6 H
  10. int main()
    . ]0 m: N9 T7 }" S& O/ ?4 ~
  11. {
    ) e! S4 @- p" I
  12. int sockfd;$ q0 b& r0 z3 n; X3 `5 v
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    * @1 [2 C( G- @5 I3 ]& Z/ L
  14.         {
    + }: d% k8 X4 r/ D
  15.                 perror("socket");7 c: H, k$ u9 g  }, `. [
  16.                 return -1;4 |5 c( [  E8 `+ j$ n
  17.         }
    " s! M: e- `( N$ W/ f9 v
  18.         printf("socket...........) a5 ~" R2 d# u/ `9 B8 Q
  19. ");# Z2 D1 Q/ ]* x$ h: U8 g' ]
  20.         & q' r' T2 k- F" w' `: b
  21.         struct sockaddr_in srv_addr;
    : }) i/ M# t" }3 P$ h3 p# h0 W
  22.         memset(&srv_addr, 0, sizeof(srv_addr));2 G6 A2 T; [$ l. G! M: Y: x
  23.         srv_addr.sin_family                 = AF_INET;, J6 |8 `* c- ]$ }8 n4 w
  24.         srv_addr.sin_port                         = htons(8888);
    9 g7 P! E, x& S
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");
    ! m: z6 M' m1 {9 ?5 w' l" k* o
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    ! L: E+ D, _0 m/ M- Y
  27.         {
    ( ^$ E. v* ?) ^- z% V3 o# I! p
  28.                 perror("connect");
    . @3 K4 c/ b! E" D$ d* e
  29.                 return -1; //exit //pthread_exit
    1 k) d4 [) B' m; j" i& l) V
  30.         }
    6 [- \) g; `& _: C) n4 y
  31.         printf("connect..............
    ( H2 }0 T& |8 }& k( A) s1 A3 W
  32. ");; O3 B3 \. M/ ~+ G2 A2 B
  33.         char buf[100];
    1 n* l& D! T+ M' v4 A* [/ G9 V
  34.         int ret;
    : a. B7 N2 ^/ J3 }! U8 G- t
  35.         while (1)
    ! u( n2 t0 k) W
  36.         {3 o6 }# Q: \/ s5 S
  37.                 printf("send: ");, Q7 t' G$ {$ w6 h
  38.                 fgets(buf, sizeof(buf), stdin);
    : E. P% `4 M0 p8 h9 J- B. y
  39.                 ret = write(sockfd, buf, sizeof(buf));
    : f3 s, [& }0 V3 Q9 x
  40.                 if (ret < 0)
    3 `; G- ^) g# _2 ^9 R! E7 N: B
  41.                 {
    & A# t1 F% j, h$ l$ w. P- _0 q
  42.                         perror("write");8 V  j/ p8 ?5 {* ?
  43.                         break;
    ' W* D8 J+ }( i: K' `
  44.                 }; r. ]* }! S6 Q: p
  45.                 if (strncmp(buf, "quit", 4) == 0)
    : D  a  e/ c& Z0 T9 A9 H. a' @' Z
  46.                         break;
    - P0 E# K3 J. B9 F
  47.         }
    * @  c/ }" w1 _% |( ~. X
  48.         close(sockfd);
    % T4 k: F! z8 l% ]; f# G2 j
  49.         return 0;' `% z* ~& l& v1 D
  50. }
复制代码

$ m% I  Z6 E/ [7 `" p7 b  e7 p8 O9 R. s: S0 t  _# X5 Z
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-19 21:04 , Processed in 0.062359 second(s), 22 queries .

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