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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。( N" L  E& g) g# T! v' m7 }3 k8 F
+ y$ X8 J. g. ^3 p+ t1 X2 Y) C
) P4 }" x2 F+ L
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
. n% y5 M9 o) u: p, Z: A/ \' }
7 G3 U" h* S% c) y- g3 H+ ^; n+ h
+ p; {# R" z0 Y
TCP协议; T, [/ U6 t' b4 `
TCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
4 J5 E; p) A. Y7 a! x5 p4 d
+ ?  t* h; t5 a. y
, _) P9 U; d9 v8 T* ~, v
关键词:三次握手,可靠,基于字节流。3 L* a; X3 {5 J! d  i9 |
/ P# R  l' ?2 m4 U  h$ }' v

. Y! |% b$ O) J  }可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。, `$ p  D+ T4 R" R7 ~
! d& _# y7 r. t) ^
TCP服务器端和客户端的运行流程) @) R9 Y+ E3 v

& U7 x) P, V( R9 h% w  e1 L+ `

* `- C/ |3 t" q+ u' o. w如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?! k' H/ w; O. A; T' o

: E! ~1 H* c* F$ l. D3 o

" r# i- o7 `9 M1.创建socket+ f: Z( f$ c" V  e- M1 A% \
socket是一个结构体,被创建在内核中7 R: p3 R6 R2 V* _( A( Q
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议& q7 B3 T9 Q$ _# W

! q3 H3 j% Q, E
( E0 R# w# v$ P6 T
2.调用bind函数) _% q- \% s; O! H$ X$ ?6 Y
将socket和地址(包括ip、port)绑定。
3 d4 V7 I% _& Q+ R9 ?3 W0 [& c 需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序* ?' L/ v8 \; g
struct sockaddr_in myaddr; //地址结构体
" e- W0 y; I% E  y6 x2 `, I' Y& ~- h bind函数* S$ V0 v' R9 ]  ]" a$ A2 X* g
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))* d8 }' i6 l* w$ r" n% h. B+ o

0 G# D" i3 [0 e  }( x
( c* E2 j, ^9 q
3.listen监听,将接收到的客户端连接放入队列/ y$ w, O% k" a$ n! w- V
listen(sockfd,8) //第二个参数是队列长度
5 `$ n, U- I/ x( Y/ y. ~5 }* x. q+ Y9 M" P; L$ ?/ Q5 O
8 r) x3 e# I7 j9 R* @$ c
4.调用accept函数,从队列获取请求,返回socket描 述符  G6 ]( W0 N( w$ \2 V3 P
  如果无请求,将会阻塞,直到获得连接
3 X; L- P: B% g4 w# N  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数
- n  z) {+ n6 x9 l3 H& I  t, W; E0 X, d. ~1 a( g: v
% Y4 ?) z" W# I6 |6 y  m- X6 d
5.调用read/write进行双向通信
# o9 Q9 x5 i7 x: K& ^
3 O- ?' b" K' H

2 z' P0 c# _& u* _, B) B' L. n6.关闭accept返回的socket* v2 R5 w6 s0 ^4 a: m* A
  close(scokfd);8 }" Z" j& \) J

  |9 ~3 O9 F- d. L2 m, n

) W) j7 k: y; Y- _  Q+ K. g* S$ _+ E: |: G1 w# U" n% m( {$ x; C% [' e

& x: |- l1 t8 a下面放出完整代码+ {+ p" t  {5 X6 M; @6 U0 [& @
) [& g! a, x( u. L3 m( b- d
  1. /*服务器*/
    6 h, q7 f+ H) @/ j- O
  2. #include <stdio.h>/ B. F1 i# Y; x/ W( @
  3. #include <string.h>1 r$ t1 [8 \& k* x8 d
  4. #include <stdlib.h>
    5 H& P% }+ E  `4 W3 b) v
  5. #include <strings.h>
    1 \/ l. n. g1 o6 }
  6. #include <sys/types.h>- G# ?, G5 c% ^/ A( @2 A% w
  7. #include <sys/socket.h>! z0 q, k! l* i/ G$ U2 Z! N; Z
  8. #include <arpa/inet.h>
    " C. K" B" h; |9 e9 A& w  U& T
  9. #include <netinet/in.h>
    " J$ ?5 a' e- W+ X4 |
  10. int main()
    % c# C7 O5 `- Y
  11. {: y3 Y- G4 d8 b6 B1 `$ X: d6 O( W
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    / F9 S! `  B) ~1 H% I
  13.         if (sockfd < 0)
    ; [! ~) V5 o# q5 Q. ]8 q, a- t8 s8 q9 d
  14.         {
    7 }: D3 Z3 I5 u
  15.                 perror("socket");- d" K" o3 C6 N9 j
  16.                 return -1;
    ) L  y; g! N! R( {$ L' n! F% m; \# _
  17.         } //创建失败的错误处理
    0 c: h, q4 h8 F
  18.          printf("socket..............+ l  C- [2 K. m4 n5 N
  19. "); //成功则打印“socket。。。。”
    2 T% ~. `* H5 N! |
  20.          ( C7 l4 [: U1 P: s  w
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体% M- j& N0 y; o# ?0 j9 M0 i0 [* S
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)
    0 e+ y  C5 p" o  ]# |$ }8 [
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型. @) x( C2 D# c( I' o
  24.          myaddr.sin_port                 = htons(8888); //选择端口号5 |3 H* ~) T2 o5 a* g2 {' w
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址$ ?" ^$ y, E4 W# L3 ^

  26. - n6 @! ]# }" M/ p0 w  e$ F
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字( f: }' E, _4 @) Y
  28.          {' ~% t: w0 U. _# }
  29.                  perror("bind");
    4 p* ?3 _7 u# _* H" {
  30.                  return -1;
    * B" h2 _+ ~: Z
  31.          }# Z8 K% C7 \5 i4 p! \$ T5 a0 ]
  32.          printf("bind.........., U( V' n+ X: ?% |# F0 r% [9 V
  33. ");( w, Y7 [4 w, ~; [: y3 B3 b; i
  34. 2 K" g3 E$ a" b1 p) u7 B) l0 e
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听- B; [/ I" W/ K$ K
  36.          {
    + A' C1 v) q2 V. v5 _  H6 i& T* `
  37.                  perror("listen");
    / p$ J7 M5 R/ [+ ]% L2 Q5 W
  38.                  return -1;! @* n; T  {/ q4 X2 k
  39.          }
    ( r: P3 u( |+ O0 S4 F0 [4 o/ Q$ I
  40.          printf("listen............
      h: @  v# R: r, L: ?! @
  41. ");5 A, r  O; J5 V8 C4 U; V
  42.          
    5 ~8 m. N" E6 ~5 z* b1 [
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求4 s, ]2 z& ?' B2 p: Y2 |6 }
  44.         if (connfd < 0)' S* T) S5 l- i: @  ?, H, h9 J
  45.         {
    ! W, n. B% O: D
  46.                 perror("accept");5 ~; _, e0 ^- E( K. r. Q1 k: M
  47.                 return -1;
    6 P  a" ?: L+ R4 s! r- [
  48.         }
    $ [7 U! T" K) R& t* t* u5 a
  49.         printf("accept..............
    ) R4 v' H. n# Y- J. l
  50. ");5 S6 m- `2 ^6 h5 L! F
  51.         char buf[100];//定义一个数组用来存储接收到的数据
    & c: q$ l8 o3 @# e+ k' [/ w' i( u
  52.         int ret;
    % j( r* H7 r7 S$ C
  53.         while (1)
    ' [9 w1 i2 O6 i/ y
  54.         {" {9 a* [6 E; V  h6 ]+ v8 K7 T
  55.                 memset(buf, 0, sizeof(buf));
    7 l8 \( `% S. [  M9 O4 M. m# R0 p
  56.                 ret = read(connfd, buf, sizeof(buf));2 o4 H' j. L1 o& n6 s; E( v
  57.                 if (0 > ret); K% f, Q% f& h0 M/ G% y6 _
  58.                 {
    / k* O8 V6 G+ I) @; }2 W' M
  59.                         perror("read");7 B& z* |' ?& O# l, G# F4 s, N5 L% t
  60.                         break;
    ; W& ]( u: m, r
  61.                 }//执行while循环读取数据,当
    / c# p1 n7 I. k: y6 \7 s
  62.                 else if (0 == ret)
    ) {, ]  X1 q6 Y. R# l; s! O
  63.                 {
    . S) U5 V: {4 J% q& K
  64.                         printf("write close!
    4 v9 P' S* l& S6 R( ^0 A
  65. ");3 u# |, X% R" r, M3 d
  66.                         break;
    - z) ^2 C  }' w* _  Y4 s9 s* C
  67.                 }# k8 ^* Q. K* W
  68.                 printf("recv: ");4 i! ^2 G/ v. s2 f: s8 u
  69.                 fputs(buf, stdout);//打印接收到的数据
    * W: y/ Y, k" P0 Z: @
  70.         }4 }8 t+ m& @2 q
  71.         close(sockfd);//关闭套接字
    $ ]0 K( B; J! q6 U( W5 c( T
  72.         close(connfd);//断开连接
    + R  q/ |3 _/ N- n
  73.         return 0;
    4 f- P5 l6 h. N' |
  74. }
复制代码

. g( x0 C3 h! X5 Q5 e' M( f+ m( ~3 r8 I! v6 v4 p
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)/ L  r* K" Y" u6 V
  2. #include <stdio.h>
    0 C1 h9 g! Z  X) I5 B- F/ ?1 Q, l
  3. #include <string.h>
    / s5 y" s2 ^: R8 U  V( b9 C1 G
  4. #include <stdlib.h>
    4 w  N& \" v& F/ i) g# B( u
  5. #include <strings.h>
    0 z# V0 |# g6 q7 C8 n* a8 Q
  6. #include <sys/types.h>& O9 t, |- g! }1 J- P& p% [
  7. #include <sys/socket.h>
    . i1 M- a; L# ?; N
  8. #include <netinet/in.h>/ J* \1 J. X0 s' P
  9. #include <arpa/inet.h>
    2 J- o2 a8 U- s' S& w0 ]
  10. int main()
    9 J. H- W: D5 K) Q( }: l0 ^- t) O
  11. {3 R( f1 ]( V+ H2 ^5 k
  12. int sockfd;! n( e9 z% y  Z+ h) H6 Q
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
    . t: G4 C7 J! ?9 r- t  z  n) w$ Q: Y( J2 A
  14.         {
    5 o$ k8 [1 B7 W1 [: C4 V6 G8 k
  15.                 perror("socket");
    # \1 v/ J& r2 u+ }! r+ ~
  16.                 return -1;
    ; V4 `( I6 v  m5 e; \7 k  f. ^
  17.         }8 O) h, }: G6 D7 e/ v* H8 G) i
  18.         printf("socket...........- @1 g. [5 c& E& [; \
  19. ");. l* j. S' p8 q  o' }2 a
  20.         
    / m. ~& w  n  F" j& J+ T& K, Q
  21.         struct sockaddr_in srv_addr;0 k" x, j& G% c% }2 I2 e$ T
  22.         memset(&srv_addr, 0, sizeof(srv_addr));! h  _5 \+ H5 T5 L* J, u
  23.         srv_addr.sin_family                 = AF_INET;+ f6 X3 y' U9 Q; X3 @) q
  24.         srv_addr.sin_port                         = htons(8888);2 X. V* w' [8 Y. w* S
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");4 Y( g- ]* w  D+ O" e+ U9 H
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))
    1 Z/ B, _* ]3 t2 i  v3 R+ F
  27.         {
    & n# k* H  C* v/ D3 _' Y
  28.                 perror("connect");
    ; `3 }. G  _! l. G! V$ R
  29.                 return -1; //exit //pthread_exit
    : N& F/ F/ `& ^$ A3 ?8 `. s+ P9 g
  30.         }
    & M( ]9 N6 C1 q6 \* r) o. i+ J+ h
  31.         printf("connect..............
    2 v5 Z, ]+ n" o9 v+ {4 ?
  32. ");( R" e+ C5 d% j  A5 w
  33.         char buf[100];
    ' C* n- n5 w/ }: @% m/ n: ]! t
  34.         int ret;
      S0 k% o& `. y% t! X
  35.         while (1)
    ! b9 Q3 \6 ^. V+ a& I$ w* N
  36.         {! ^* W, b% _+ q
  37.                 printf("send: ");
    : X6 I$ l% u# q' T% k$ E
  38.                 fgets(buf, sizeof(buf), stdin);" n8 y' h8 H& ?2 K: }; Y9 J
  39.                 ret = write(sockfd, buf, sizeof(buf));
    ( r: S- l: j' q2 n& N. e0 |
  40.                 if (ret < 0)/ i2 }& [9 u1 h( e
  41.                 {
    , o+ a- [, a. f+ g
  42.                         perror("write");9 @- K! W' ~! \$ L7 U5 O! `; f( k
  43.                         break;
    0 t6 V2 t( q0 a! c
  44.                 }9 u! O* ^( K! y4 p! Z$ y# L
  45.                 if (strncmp(buf, "quit", 4) == 0)
    + Q& s# h( ?# p3 F$ J4 n
  46.                         break;3 L8 Y) |6 @  n  L8 Y
  47.         }; r& v6 N3 I# l
  48.         close(sockfd);
    4 |& }- }6 @! @% ~1 X
  49.         return 0;8 R7 }1 N( x, V  Y8 W
  50. }
复制代码

5 p: c6 d  _3 l7 _5 D5 k  r( @4 }$ l7 m" y+ O" U% K7 S' _9 h0 K- P' C
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-17 16:00 , Processed in 0.068503 second(s), 23 queries .

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