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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
. q2 L: I' n! m
* r) X) R, b( J( p1 Z* d$ J
- j, ?2 |7 p6 X! k  A
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
8 u+ w/ |. }3 f8 O& K6 A# ^& @# a0 `8 |8 G3 v+ m
/ O/ F" {, Y% u3 D! A
TCP协议8 U; O2 N; z" R- N! m
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。0 F) l, v; |: l: {1 g+ `
, I. }1 s7 \; n$ H  L

# }0 o( i  q- q# k关键词:三次握手,可靠,基于字节流。8 \8 X! ~# }' T/ _6 r. n
6 E+ K* i* I2 U& @1 w6 I( k

+ w# o1 h0 w1 O. e2 z可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。
$ k; I7 Z; \9 w9 `! B* u% [' h5 T. ? " I6 f, D3 n+ T5 s* o  {
TCP服务器端和客户端的运行流程5 y. {# i, `7 o; G1 U

5 l5 L: X4 v, f5 p- k5 o! C# n3 U( D
: O4 M6 `& P- H; ]
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?* l6 F- \. Q( f! A8 m; O0 r

( w) }! r7 x3 a

$ S1 ~' Y6 n0 M1.创建socket+ ]9 N# {) R- O8 b; A, `
socket是一个结构体,被创建在内核中
& t2 |  O! M0 o: c' E' k; o sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
- ?+ P' t) m6 _2 {4 U7 f/ u1 S9 E: o" U/ k9 m8 V
$ M; E" k$ A5 X; B3 s. r  ~! I5 C
2.调用bind函数! h# ?) q* D2 B& _( O
将socket和地址(包括ip、port)绑定。
* ]  G, n9 w- x& {3 m 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序
# w+ d- q* |1 X; x. p struct sockaddr_in myaddr; //地址结构体
8 x, z7 o4 w9 B4 h2 y& X" E+ J3 c bind函数
$ K' t- w" l, K& H) z+ T. n bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
+ D$ A& m- L$ b  y2 O% Y4 Z( |. S& X& B- s
" b+ B  W3 _; h1 }" Z) F) N  u
3.listen监听,将接收到的客户端连接放入队列
8 ]' |- o5 |7 \! E$ L2 _ listen(sockfd,8) //第二个参数是队列长度
1 P6 {4 V' ^) ?  k
( l# B/ t! f. t* X, }$ ]6 u

- x4 l, y6 c! n+ T& J& [* l) l( Y4.调用accept函数,从队列获取请求,返回socket描 述符$ d% C5 ?; z( B0 d* x
  如果无请求,将会阻塞,直到获得连接
2 t) z9 Q% s  t9 z$ V( G1 v/ Y  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
* J8 ^3 h/ F9 D2 i5 b; _/ f+ ?5 Z% m' L2 F$ e+ b

/ |: V- G. T' d% p2 q! x' N5.调用read/write进行双向通信
2 ?( O0 P8 ^: g% }0 x
7 b( _' c) Z6 u: B# C/ W
' L! o+ J' }& L
6.关闭accept返回的socket
& n/ R  K2 d6 f  close(scokfd);$ H# W" Y  E1 ]6 X

; W" m* {' S& \  {! v& g
9 i) f2 i7 `. p; Q9 K  Q
, ?. U' w2 |- u% b
) H1 q" T/ X  X' }+ \8 H& {: {
下面放出完整代码
2 a1 o6 `/ J) G* X, d8 a0 \! U  T* k9 _( e! O3 U
  1. /*服务器*/% J% ~1 {" ]! Q" X) O
  2. #include <stdio.h>
    ( j/ l7 v8 l/ I9 y- D6 T
  3. #include <string.h>5 @9 j. ~2 M2 E3 d3 g- j1 o
  4. #include <stdlib.h>
    3 L2 H* u& M1 n" f
  5. #include <strings.h>/ E& S) T+ H# J- s6 C2 V
  6. #include <sys/types.h>
    ) |$ o  [2 b# E7 H) R
  7. #include <sys/socket.h>4 ^6 B1 L6 u; l
  8. #include <arpa/inet.h>
    * e8 U. H; G/ J+ ]* G) n
  9. #include <netinet/in.h>0 k2 Z8 R$ [0 B
  10. int main()
    # E  a+ z7 s0 \; M+ ^# o
  11. {
    4 S" D2 h% A" E( d6 Q
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    * m! d; f3 ^$ Q9 w3 {% k
  13.         if (sockfd < 0)
    ) m: @  K& k( @& g
  14.         {* C# m# p" E& p; m4 {4 x2 ~
  15.                 perror("socket");
    - ^  B1 {5 Q3 |. }* L
  16.                 return -1;
    3 @' C. j" a+ x, ], `
  17.         } //创建失败的错误处理: `7 P, z6 c9 V& W9 d/ D% s; d4 E
  18.          printf("socket..............
    / t5 s3 t  A: h$ E& h# h1 a
  19. "); //成功则打印“socket。。。。”
      X8 N; v, S0 o+ _" I8 ?, N
  20.          ( s) `) I  n6 _  I4 A* F0 ?
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体3 B9 E! ^& B6 w9 r1 x- p1 x7 Q3 @2 S
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    ( h/ A. _0 b# b7 y# I( P0 A5 t1 Y
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型5 q& P" Y4 a" j/ F
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    3 v& t0 z! v8 r+ E, ~
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址% b0 Y2 l. T; }  b

  26. 1 n, b3 G1 Y  l- o2 h$ ^( q/ U
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    # M. Z( q6 L; ?8 x6 s! {  k
  28.          {9 ~% D0 n9 c& n; r4 @9 r8 l0 ~$ w5 L
  29.                  perror("bind");
    ; m9 K- z$ N: ?0 A% s$ l) ~
  30.                  return -1;6 v' n" \. Q7 ~. w% U% k
  31.          }; Q, k. N- }; N
  32.          printf("bind..........
    " L  L1 k# D* w* p1 g, ]: `3 E
  33. ");  i9 ?& l6 E' N. y' {) d- ]
  34. 9 D( L7 e- [/ x1 K2 F: Y# T% d+ {
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听
    - C/ A4 B- v2 t$ k2 W+ h
  36.          {- |- i" w# j. H. ]! I' B
  37.                  perror("listen");$ e# v& P" w. t4 j; n* _9 m' A
  38.                  return -1;* h* E4 m1 d) t' t
  39.          }
    ( f/ [' A$ ^( b/ K
  40.          printf("listen............2 s; N  c4 p* Z* ^
  41. ");
    6 Q: `8 U* F* m
  42.          
    " {0 z% c6 W1 I5 m- ]
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求3 C* Z# ?/ @& p
  44.         if (connfd < 0)0 D5 L' t; @7 u8 F  _
  45.         {
    2 ?6 N- _. Z. {$ l. L- I
  46.                 perror("accept");& N# \. Q6 w& f
  47.                 return -1;- e& z+ b- }2 T: D
  48.         }" o. w# h- y4 ?2 z& q" m! F
  49.         printf("accept..............
    4 w. u0 n8 `! q/ k
  50. ");0 P& x: _0 m, g. ^# J
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    - j' l( A$ x- e& c1 S3 ?2 z$ G0 I3 _
  52.         int ret;6 W! [) W9 A6 W- i# o
  53.         while (1)
    % H+ D1 U/ I0 g6 b7 ]2 i% f
  54.         {
    % L3 ~; j' {) q" G: j- H
  55.                 memset(buf, 0, sizeof(buf));
    " r( u! \/ T/ _8 x+ c- w) ]4 b
  56.                 ret = read(connfd, buf, sizeof(buf));& m+ {9 R( j7 I5 C
  57.                 if (0 > ret)
    & f1 J1 b' R$ c4 ~. ]/ H% Z( \3 M9 c1 _
  58.                 {3 L, B2 k$ t% w% }) y6 N7 \
  59.                         perror("read");
    ( P- k" M! [1 U; D7 Z
  60.                         break;/ {2 _$ C+ w! O6 V; n
  61.                 }//执行while循环读取数据,当
    1 p  }( n6 B* g2 w
  62.                 else if (0 == ret)
    2 Q  }9 N% ]- ]+ y3 w# l5 [( B
  63.                 {! q, y- @* r# ]1 l% Q# {$ u" j6 ]
  64.                         printf("write close!% Z9 B* S3 g* Z8 N
  65. ");
    0 ?* D+ h4 B+ g, L6 L( I
  66.                         break;
    ) }5 d7 K, L+ b; K8 Y! z( y( |
  67.                 }3 W6 D) i5 d# W' j: c' |
  68.                 printf("recv: ");
    - E( N, X# P  r+ @7 n0 k
  69.                 fputs(buf, stdout);//打印接收到的数据) g9 [5 S( V$ f, K+ l/ L
  70.         }
    3 i! A2 v" }% _- f
  71.         close(sockfd);//关闭套接字
    , P  m' L  G6 b* w$ O
  72.         close(connfd);//断开连接: t6 I; L, g+ Z3 o1 J3 |0 D; @0 p: X
  73.         return 0;, D6 C; q. i( G+ F1 Y
  74. }
复制代码

! y9 T. H6 q: G8 B; l/ a. i5 C1 i) _" n. t  u! c( Z+ B  R# ^7 U& u/ V4 W
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)1 u) n$ @" y, _  A
  2. #include <stdio.h>: a7 }, F8 I. k* G6 _; [# m
  3. #include <string.h>  k! g. _. v9 u3 T
  4. #include <stdlib.h># W" O" D* m8 d
  5. #include <strings.h>* k3 @* Z) I) U% ^
  6. #include <sys/types.h>& C1 Z0 p1 S% r$ J# V
  7. #include <sys/socket.h>5 a- R8 q- W; ~4 g
  8. #include <netinet/in.h>
    7 P8 U7 V% L0 u: r' o0 m' y# w
  9. #include <arpa/inet.h>% Q& ~; ^7 k' Y$ h+ n
  10. int main()
    , a9 j. Y4 l' R7 M/ t* c1 g. L5 T
  11. {
    " F2 v, A; K% _/ ?! D; k2 A
  12. int sockfd;7 P2 a; b& _0 k! @7 F7 U5 p3 E
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))- t1 {3 c+ P# M8 O
  14.         {
    ( I: R  w1 {/ F! H+ p
  15.                 perror("socket");
    * n( K2 t1 z; ^# Y6 g/ R4 ?. j
  16.                 return -1;/ s1 ^0 n- A5 b& s# W/ Q% s1 \
  17.         }
    - v' o) K. E& Y
  18.         printf("socket..........." Z8 X/ e: _# |
  19. ");  x1 c/ y& K- d* t; D) Z
  20.         2 c7 y/ V8 _" e. O3 @
  21.         struct sockaddr_in srv_addr;* |0 q' ]' c) m3 ?- U" O. ]; |
  22.         memset(&srv_addr, 0, sizeof(srv_addr));
      ]1 S* L: Y+ i" a2 _$ a( [
  23.         srv_addr.sin_family                 = AF_INET;
    " w# d# q; W  E" I# k- e
  24.         srv_addr.sin_port                         = htons(8888);
    0 s4 ]; k! B8 E* R
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");# k% h1 f" W. x) z* a
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))0 a3 X: h# N4 y4 @" I" }
  27.         {. y$ B8 ~' F* h9 o% h) {
  28.                 perror("connect");
    . h- Y# {) {& @/ G
  29.                 return -1; //exit //pthread_exit
      f& [' E2 G! F" D
  30.         }
    , W; N2 L5 @9 k/ E/ k
  31.         printf("connect..............9 }; ~7 n) K4 Z) x9 M% ~' [9 ]
  32. ");
    ( I5 U: {; S$ B
  33.         char buf[100];) P# W4 ^! G) b0 o
  34.         int ret;
    ! v/ |( b; @& a) e9 [
  35.         while (1)
    " a+ s. b- `, M
  36.         {
    2 S3 S0 F: v) }+ Q: d, C/ y
  37.                 printf("send: ");
    ) T9 h& T8 T4 t- C5 f" p
  38.                 fgets(buf, sizeof(buf), stdin);9 N3 V! z4 x: b. q9 o. j3 W
  39.                 ret = write(sockfd, buf, sizeof(buf));6 {0 Z4 @: b4 j1 n2 N3 L$ }
  40.                 if (ret < 0)
    % J7 l( u5 x6 k& j6 F0 m/ C) ^
  41.                 {
    8 q" y- X# v9 }* k$ H
  42.                         perror("write");
    ( N. X" n6 f/ ^" v0 N$ `
  43.                         break;
    5 R5 u5 i4 O& y3 R0 j5 D/ `
  44.                 }5 [4 v& m: ]2 b, S3 D- R6 v. X! A* S
  45.                 if (strncmp(buf, "quit", 4) == 0)
    2 X3 l( |* C; B6 h) e; o+ |/ G
  46.                         break;; U4 I" U. M& s) q- ^" M; r
  47.         }
    7 n, X( l: p2 U: Y
  48.         close(sockfd);
    8 T! e/ A1 Q4 W; a1 n) t* y) U
  49.         return 0;
    & a) j4 o1 i' T0 e
  50. }
复制代码
) `, }8 R+ C- u
# M7 F7 G( p) X  Z! g- g9 f* Y
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-1-30 13:04 , Processed in 0.063091 second(s), 23 queries .

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