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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2020-5-9 02:09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西——socket(套接字)。
. i! u, I( d, {4 c6 r: P: ^( V8 O  S
2 n9 _% _* _, ^3 I9 L3 ^0 ^( k
' [: S5 `9 y: L+ {% F% u4 O
socket(套接字):简单来讲,socket就是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。3 ]& v  R0 n2 S0 o! N. ?2 |; p( Q( x
7 o# y; m. ]3 ^
1 K9 |$ b. b+ J# H# q3 U, y$ b6 ^
TCP协议
  j+ P2 {  w6 ?( lTCP协议:是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
2 |3 n! E! l% h8 I4 z3 H% Q1 y
8 {+ ~! ^+ Q3 F1 Z1 h3 ~

& h6 P: ^$ s8 c关键词:三次握手,可靠,基于字节流。
6 X  V7 r( `: a: q/ f
" j+ b  G- k- |* q0 m

+ W: S( O6 V, ~' J1 b可能有朋友会问,TCP就这么简单一句话吗?当然不是,TCP作为非常重要的传输协议,细节知识是很多的,细讲起来这一篇文章怕是不够。不过在本篇内容中,我们只需了解他的几个关键词特性,就能很好的理解下面的内容。& o$ P; C# t2 f. M- `4 K
. A0 {" Y& i% z) p7 q% R
TCP服务器端和客户端的运行流程
0 `" P' f3 d" |* F9 X
; T  x; h/ _+ Y- Y* G
4 L: U/ y" k. U. U
如图,这是一个完整的TCP服务器——客户端的运行流程图,其实我个人认为程序啊,不管哪个语言都是一样,核心就在于算法的设计和函数的调用。那么图中的函数都是什么意思呢?
* }: n  y; f' u/ w( g4 T) Y2 @' E% a1 e
( J( u& I( Z6 x- x* F8 Y. z/ `8 A1 d0 @( V
1.创建socket/ a9 m, G9 X  A" N
socket是一个结构体,被创建在内核中! L8 ]1 Z# o0 s% {" ^- j
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
  K. M) n/ O& E# C: d3 f& t
# v. \; s7 R/ {* a5 s

$ D" j$ O% R9 ~1 h2.调用bind函数
. v8 p* B' a3 W+ ] 将socket和地址(包括ip、port)绑定。# g1 w( I  P) X( ?( @) @
需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序" Y4 t7 L: k, L7 h
struct sockaddr_in myaddr; //地址结构体
5 D6 k/ h. M, i- p* }/ N1 i  X bind函数/ [& d2 g, n$ w+ o! q9 ?: s* ?. F2 F8 j
bind(sockfd,(struct sockaddr*)&myaddr,sizeof(serveraddr))
* E' L6 E) Y+ ?0 v4 x& s: ?1 p9 Y. ]6 ?# `
: W2 O4 O4 R. K( h* K: b
3.listen监听,将接收到的客户端连接放入队列
7 M5 V' c, @6 b' o7 O listen(sockfd,8) //第二个参数是队列长度+ T8 U! d" e% u% r2 u* D: B

5 @5 P# R4 D7 a; E0 N" w% s

: p, A  }- f% N8 ?8 X$ d4.调用accept函数,从队列获取请求,返回socket描 述符
0 |" F! X; n  c+ `: k% E  如果无请求,将会阻塞,直到获得连接
- y- Z7 _* `$ R4 F8 N  int fd=accept(sockfd, NULL,NULL);//这边采用默认参数1 m9 u: E  i+ o5 H, z5 b

/ y3 u% K9 a8 t$ N# ]6 f
( O/ j! V! A3 W- A8 r
5.调用read/write进行双向通信
4 j' X+ Q( N4 @. [, Q( n% R8 ^
. W9 T1 y) z; o8 S; j
6.关闭accept返回的socket
# _, B& e9 U6 E' {) k  close(scokfd);3 M  W- U5 p' Z5 A% S7 x/ v8 H

* l* y- F" F* z, H) u

. n: E, p" g" U2 Z: [0 g% P- Y" |
$ i' ]3 i8 w9 T0 I3 |1 e
下面放出完整代码. @3 m! X/ Z4 V

! i- p. F* |* k+ H
  1. /*服务器*/9 [, z% t" A0 x/ U" r- c- O+ S, P
  2. #include <stdio.h>( Y* Z( K0 y* t2 W6 M1 W: a; f
  3. #include <string.h>" d6 u6 o+ u5 x' Q
  4. #include <stdlib.h>
    ( n2 i' ?) t  d$ u2 R
  5. #include <strings.h>
    4 R5 S) B: Y, z: B$ v2 L) k% f" k
  6. #include <sys/types.h>
    % Q! b& J* d: P% p3 J+ d3 t/ n/ S
  7. #include <sys/socket.h>
    1 a! l2 O8 E/ }: v2 O+ [
  8. #include <arpa/inet.h>6 L6 I9 s9 J3 i! J9 a& M8 J
  9. #include <netinet/in.h>
    - l) I/ C8 u/ W. n
  10. int main()
    $ n6 e  J$ M3 K- D% I
  11. {
    ' d9 z- n' @" |6 @4 Y
  12.         int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    3 j$ ^+ O3 [( b6 d
  13.         if (sockfd < 0). A7 K& Y4 ]  V' |. H; }, T
  14.         {
    * w: j4 s! ~" J1 I
  15.                 perror("socket");% X9 m7 X7 x! t) `. ?+ Q
  16.                 return -1;, M) K2 G. R8 z) H5 W- ]' y
  17.         } //创建失败的错误处理
    " [* B( L3 Q4 Q% I7 T' @
  18.          printf("socket..............7 Z9 I, y6 f/ ~+ i" ~
  19. "); //成功则打印“socket。。。。”6 Z  \; U: U7 R: i* w
  20.          ! R9 _0 P. o9 g# b. ]: G" f. s( W
  21.          struct sockaddr_in myaddr; //创建“我的地址”结构体
    - n; ^0 q6 L# {7 S, D
  22.          memset(&myaddr, 0, sizeof(myaddr)); //对内存清零(保险起见)6 J2 T  f, J1 R0 I. |+ W
  23.          myaddr.sin_family                 = AF_INET; //选择IPV4地址类型* O; |5 Z; Q3 Q8 g' r7 }4 [/ ^
  24.          myaddr.sin_port                 = htons(8888); //选择端口号
    2 l2 H& D& Y. c+ m  e" ?2 \
  25.          myaddr.sin_addr.s_addr         = inet_addr("192.168.3.169"); //选择IP地址7 [5 ?1 i  y9 c( y

  26. 5 T$ s; S. e, L% E) D& \; o+ Z4 k# ?; k
  27. if (0 > bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))//绑定套接字
    ' _. R7 j/ L, x8 ~
  28.          {
    " c; |) k6 N( c, ]6 \& U6 K# s$ i/ S
  29.                  perror("bind");
    & G9 k/ `5 v1 Q, c
  30.                  return -1;  B9 q3 V4 @# u
  31.          }
    8 f. Y/ K/ n# L
  32.          printf("bind..........6 a' d! O/ ^- [' [9 Y" g  f
  33. ");
    7 m: A' ~1 S1 b* {9 ]* s
  34. . m: D: Y  F. l" b
  35.          if (0 > listen(sockfd, 8))//调用listen对指定端口进行监听& o. @9 l, {# ~% d# a4 c0 M
  36.          {
    " V/ b; t" K& o5 P* V$ Q
  37.                  perror("listen");/ d- s+ m% d- R" S7 }
  38.                  return -1;- D# P" b) G4 k+ J0 X  T
  39.          }3 {: E& A! N, H" W# f
  40.          printf("listen............; ~" ]+ R, {5 I% c9 Q* ~& l' }
  41. ");* y% |; j' b& b& H. a& T, T
  42.          
    1 ]: E5 D  Q: V$ n1 A
  43.         int connfd = accept(sockfd, NULL, NULL);//使用accept从消息队列中获取请求. S. v# c' `" w9 B% K$ c
  44.         if (connfd < 0)! ^8 @* k( l' G2 z0 q  u
  45.         {
    / q. R% g2 O5 d4 J
  46.                 perror("accept");. v1 g) y/ i8 j
  47.                 return -1;
    - h8 p. Q8 @' ?3 B5 o+ ]
  48.         }
    4 t" g6 s+ m. r* @
  49.         printf("accept..............
    " y0 N/ t4 {' X: V+ |
  50. ");
    7 a% [1 F" c3 @7 R
  51.         char buf[100];//定义一个数组用来存储接收到的数据: b; h5 [! f2 z: @9 h  q% x
  52.         int ret;
      w# K6 R; S8 Z6 C6 B
  53.         while (1)
    * t; G5 Q2 ]/ v
  54.         {* T7 Q4 D" c2 e' U. K
  55.                 memset(buf, 0, sizeof(buf));
    8 `- g0 T  o3 z  j! m" C& _. u
  56.                 ret = read(connfd, buf, sizeof(buf));  x( b, @" y* w. ~4 C. H) E
  57.                 if (0 > ret)
    7 V( `+ s% d3 ~- X
  58.                 {
    3 D# l" T5 P+ A! N
  59.                         perror("read");
    ; A5 u- g& F! ]0 o% _3 u3 L( [
  60.                         break;
    / u0 [" `& ^" |4 z
  61.                 }//执行while循环读取数据,当- d; f# ]/ [! P( f) p
  62.                 else if (0 == ret)  r2 Y! O. `/ x8 X2 [& I
  63.                 {
    4 V3 r) S) a+ _& A9 A6 ~/ \2 a1 f0 Z
  64.                         printf("write close!9 [! X9 Y, P1 W2 \2 h/ w+ B1 b
  65. ");
    . ]0 N/ _1 h- T  M7 l1 Y! u4 |
  66.                         break;' \! H' n/ r' V, r2 W2 b# z* R
  67.                 }, G$ V# v9 h& f6 V6 r( G) o
  68.                 printf("recv: ");% J8 ?" q" Y: A% F- s
  69.                 fputs(buf, stdout);//打印接收到的数据  |7 i; R+ a. L4 f) c, o; f
  70.         }
    / P" s7 f  f$ a' t, u6 {
  71.         close(sockfd);//关闭套接字- ~+ H" T. O. u: K; O7 O0 E9 ^0 x; c
  72.         close(connfd);//断开连接
    " j1 {6 n  L9 e# ~! [* F) N
  73.         return 0;  F( V6 ?4 {4 `4 z* m
  74. }
复制代码

2 }2 n# A/ [* }( V  B+ ~% u4 u
2 R5 \+ ?( w! E1 ?
  1. /*客户端*/(具体功能和服务器一样,所以不再加注释)4 Z( h5 m) t; Q& _( |& Q% N' [
  2. #include <stdio.h>
    . Q# X: Y4 P. ^; V; e
  3. #include <string.h>5 f7 A4 _5 k1 o$ e
  4. #include <stdlib.h>; c% e1 E- {, K9 i$ U( @
  5. #include <strings.h>
    4 m8 U+ v! @/ ~' o# |5 g5 @* }9 n8 s
  6. #include <sys/types.h>2 a$ {2 x& d  A
  7. #include <sys/socket.h>
    9 j) T  Q0 z6 b6 Y  m, l" S% p
  8. #include <netinet/in.h>/ M3 ]1 C: B6 q6 C
  9. #include <arpa/inet.h>& `1 a  g- J$ e4 j
  10. int main()9 d1 p& {& {5 a* w( z" C$ T
  11. {
    " X  I9 j+ r) |1 D% N
  12. int sockfd;
    0 w* |9 B( y: }# {
  13.         if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0)))$ h3 G- ^; R: b+ o( R5 g' S
  14.         {  _7 g  x; Q" W7 M$ I0 u
  15.                 perror("socket");
    $ D4 ^3 L7 |/ q+ x3 i7 s2 ^
  16.                 return -1;
    9 i) d. H; t. ?2 F
  17.         }) ]0 f9 m% ?) b' I5 a
  18.         printf("socket...........
    * ?# @' K" d9 |
  19. ");, y0 n! s9 o: m) h6 x2 h
  20.         
    - Y# B! f) O$ N9 W; p
  21.         struct sockaddr_in srv_addr;
    . P, N& F! R5 t7 v* ^( C: r
  22.         memset(&srv_addr, 0, sizeof(srv_addr));& O) ~6 x$ f0 R2 R
  23.         srv_addr.sin_family                 = AF_INET;, \) ^% N: F0 _1 C$ S: ]  o
  24.         srv_addr.sin_port                         = htons(8888);/ N; `5 k: S5 _. I) [0 V4 n6 f
  25.         srv_addr.sin_addr.s_addr         = inet_addr("192.168.3.169");' E6 f3 Z  }/ S* n& `3 c
  26.         if (0 > connect(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)))( r3 \( D* t; {4 O
  27.         {0 t' ~, @# X9 B  N  Q1 n' V
  28.                 perror("connect");- J1 i9 T- @8 @5 a( y, ^
  29.                 return -1; //exit //pthread_exit- _" ]% [3 z7 ?
  30.         }$ o+ Q2 u; F: j4 B
  31.         printf("connect..............
    6 B/ K$ a- O7 E+ m" m
  32. ");
    * n8 U0 i2 L  e& L+ D; Y) i0 \; U6 Q" v6 J
  33.         char buf[100];; e& V$ `& ~3 E$ T0 g
  34.         int ret;+ l' @' J% k! F4 K
  35.         while (1)
    % ~9 X5 |9 D. M: Z1 d3 @% r2 O0 Y9 `
  36.         {
    . t1 b, s- E2 X! r) ~  C0 m7 w
  37.                 printf("send: ");
    , x+ R/ q- @8 V# e. C
  38.                 fgets(buf, sizeof(buf), stdin);: v) C% J# \+ W. K3 s* p9 l6 k* V$ [5 P
  39.                 ret = write(sockfd, buf, sizeof(buf));
    + }  R4 j& L: n( Q+ \' I
  40.                 if (ret < 0)
    3 y% W# }! Z1 i$ o$ @& X# B* |
  41.                 {! x! J# E4 {1 K" k4 q
  42.                         perror("write");' u! i0 `  d9 Z
  43.                         break;$ V" {4 x  d; K' T) s) i6 S
  44.                 }
    . o: R$ Y3 _+ E7 f8 e2 V
  45.                 if (strncmp(buf, "quit", 4) == 0)- K2 E) p2 M  G+ m% k
  46.                         break;
    ( _% @& p7 a% k' J5 q
  47.         }
    ) {- B) }9 I+ y) _* @9 e
  48.         close(sockfd);/ q) B" `$ }+ Y4 s. x9 y; f
  49.         return 0;
    2 D" q9 H& l" T
  50. }
复制代码

7 |/ u! Z, A2 H$ X" A- L
# x$ w1 y( \. a" S
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 10:59 , Processed in 0.128709 second(s), 22 queries .

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