百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

使用 Rust 和 Tokio 构建 TCP 服务器示例

bigegpt 2024-08-23 11:51 2 浏览

Tokio 是一个用于构建异步应用程序的 Rust 框架,其核心特点是使用了基于事件循环的模型,可以实现高效的异步 IO 操作。在本文中,我们将通过一个示例程序来了解如何使用 Tokio 来构建一个基本的 TCP 服务器。

引入完整的 Tokio 功能集,将 tokio 的版本声明为 1.26.0 并指定 features = ["full"],如下所示:

[dependencies]
tokio = { version = "1.26.0", features = ["full"] }

这将确保所有 Tokio 的功能都可用,包括网络和 I/O 相关的模块。接下来我们看一下示例代码:

use tokio::net::{TcpListener,TcpStream} ;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let listener = TcpListener::bind("127.0.0.1:8080").await?;
    println!("Server listening on port 8080");

    loop {
        let (mut socket, _) = listener.accept().await?;
        tokio::spawn(async move {
            let mut buffer = [0; 1024];

            loop {
                let n = match socket.read(&mut buffer).await {
                    Ok(n) if n == 0 => break,
                    Ok(n) => n,
                    Err(e) => {
                        eprintln!("failed to read from socket; err = {:?}", e);
                        break;
                    }
                };

                if let Err(e) = socket.write_all(&buffer[0..n]).await {
                    eprintln!("failed to write to socket; err = {:?}", e);
                    break;
                }
            }

            println!("Connection closed");
        });
    }
    Ok(())
}

这段代码是一个使用Tokio实现的TCP服务器,它会监听在本地的8080端口,并接受来自客户端的连接请求。一旦连接建立,服务器将为每个客户端连接生成一个新的任务,并在任务中处理客户端发送的数据。

首先,我们使用 tokio::net::TcpListener 绑定本地地址和端口,开始监听传入连接。然后,我们使用 loop 循环来等待客户端连接,每当有新的连接进入时,使用 listener.accept().await 方法接收连接,并返回一个包含新建立连接的 TcpStream 对象和远程连接的地址的元组。

接下来,我们使用 tokio::spawn() 方法创建一个新的异步任务,每个新的连接都将创建一个任务,以避免阻塞主循环。在异步任务中,我们使用 tokio::io::AsyncReadExt 和 tokio::io::AsyncWriteExt trait 来实现异步读取和写入数据。我们使用一个循环来持续从客户端读取数据,并将读取的数据写回客户端。如果读取到的字节数为 0,则表示客户端已经关闭连接,退出循环。

最后,我们在异步任务完成后打印一条消息并返回 Ok(())。

下面是对代码的分析:

引入了 tokio 库的 net 和 io 模块:

use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

定义了一个 async 函数 main,它是程序的入口函数。由于 main 函数是异步函数,因此在函数签名中加上了 async 关键字,并使用 tokio::main 宏来启动 tokio 运行时。

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // ...
}

调用 TcpListener::bind 方法创建一个 TcpListener 实例,并将其绑定到本地的 8080 端口上。

let listener = TcpListener::bind("127.0.0.1:8080").await?;

在控制台输出信息,表示服务器已经开始监听连接请求。

println!("Server listening on port 8080");

进入一个无限循环,等待客户端连接请求。当有新的连接请求到来时,调用 listener.accept() 方法,该方法会返回一个包含新连接的 TcpStream 实例以及一个表示远程地址的元组(由于我们不需要远程地址,因此用 _ 占位符来表示)。

loop {
    let (mut socket, _) = listener.accept().await?;
    // ...
}

为每个新连接创建一个新的任务。tokio::spawn 方法接受一个 Future 对象,并在一个新的任务中异步执行该 Future。在这里,我们将一个闭包作为 Future 对象,并将它传递给 tokio::spawn 方法。

tokio::spawn(async move {
    // ...
});

在任务中定义一个可变的 buffer 数组,用于存储从客户端读取的数据。

let mut buffer = [0; 1024];

进入一个无限循环,不断从客户端读取数据,直到客户端关闭连接。在循环中,使用 socket.read() 方法异步读取数据,并使用 match 匹配读取结果。如果读取成功并且读取的字节数为 0,则表示客户端已经关闭连接,跳出循环。如果读取成功并且读取的字节数不为 0,则将读取到的数据写回客户端,使用 socket.write_all() 方法将数据异步写回。

 loop {
   let n = match socket.read(&mut buffer).await {
     Ok(n) if n == 0 => break,
       Ok(n) => n,
         Err(e) => {
         eprintln!("failed to read from socket; err = {:?}", e);
         break;
       }
   };

   if let Err(e) = socket.write_all(&buffer[0..n]).await {
     eprintln!("failed to write to socket; err = {:?}", e);
     break;
   }
 }

要运行该示例程序,我们需要在控制台中执行以下命令:

 cargo run

该程序将会开始监听本地的 8080 端口,当有客户端连接时,会返回 "Server listening on port 8080" 消息。当客户端发送数据时,程序将会将接收到的数据返回给客户端,直到客户端关闭连接。

在本文中,我们了解了如何使用 Tokio 来构建一个基本的 TCP 服务器。通过使用 Tokio 提供的异步 IO 操作,我们可以实现高效的网络应用程序。

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...