Rust 实战练习 - 4. 网络 TCP/UDP/Channel

目标:

  • tcp 服务器和客户端
  • udp服务器和客户端
  • 多线程实现服务器,异步并发
  • channel

tcp server & client

use std::{prelude::*, net, thread, env};
use std::io::{Read, Write};fn main() {let args = env::args().into_iter().collect::<Vec<_>>();if args.len()>1 {match args[1].as_str() {"-s" => tcp_server(),"-c" => tcp_client(),_ => println!("unknown cmd {}", args[1]),}}else{println!("Usage:\r\n\t-s Open Tcp Server.\r\n\t-c Open Tcp client to connect the server.")}
}
fn tcp_server() {let s = net::TcpListener::bind("0.0.0.0:8000").unwrap();println!("Listen on addr: {}", s.local_addr().unwrap().to_string());for req in s.incoming() {if let Ok(req_s) = req {_ = req_s.set_nodelay(true);thread::spawn( move || {handler_tcp(req_s);});}}
}
fn handler_tcp(mut c: net::TcpStream) {let mut buf = [0u8;1024];let info = format!("[{:?}] => client in: {}", thread::current().id(), c.peer_addr().unwrap().to_string());let n = c.read(&mut buf).unwrap();println!("{} {}", n, String::from_utf8_lossy(&buf[..n]));println!("{}", info);_ = c.write(format!("HTTP/1.1 200 OK\r\n\r\n{}\r\n", info).as_bytes());
}fn tcp_client(){let mut c = net::TcpStream::connect("127.0.0.1:8000").unwrap();c.set_nodelay(true).unwrap();_ = c.write("GET / HTTP/1.1\r\nAccept: */*\r\n\r\n".as_bytes());let mut strbuf = String::new();_ = c.read_to_string(&mut strbuf);println!("resp: {}", strbuf);
}

异步版本

use std::fs;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;fn main() {// Listen for incoming TCP connections on localhost port 7878let listener = TcpListener::bind("127.0.0.1:7878").unwrap();// Block forever, handling each request that arrives at this IP addressfor stream in listener.incoming() {let stream = stream.unwrap();handle_connection(stream);}
}fn handle_connection(mut stream: TcpStream) {// Read the first 1024 bytes of data from the streamlet mut buffer = [0; 1024];stream.read(&mut buffer).unwrap();let get = b"GET / HTTP/1.1\r\n";// Respond with greetings or a 404,// depending on the data in the requestlet (status_line, filename) = if buffer.starts_with(get) {("HTTP/1.1 200 OK\r\n\r\n", "hello.html")} else {("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")};let contents = fs::read_to_string(filename).unwrap();// Write response back to the stream,// and flush the stream to ensure the response is sent back to the clientlet response = format!("{status_line}{contents}");stream.write_all(response.as_bytes()).unwrap();stream.flush().unwrap();
}

改写

[package]
name = "d5"
version = "0.1.0"
edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]
futures="*"[dependencies.async-std]
version = "*"
features = ["attributes"]
use async_std::net::TcpListener;
use async_std::net::TcpStream;
use futures::stream::StreamExt;
use async_std::task::spawn;
use async_std::prelude::*;
use std::fs;#[async_std::main]
async fn main() {let listener = TcpListener::bind("127.0.0.1:7878").await.unwrap();listener.incoming().for_each_concurrent(/* limit */ None, |tcpstream| async move {let tcpstream = tcpstream.unwrap();//spawn(handle_connection(stream));handle_connection(tcpstream).await;}).await;
}async fn handle_connection(mut stream: TcpStream) {// Read the first 1024 bytes of data from the streamlet mut buffer = [0; 1024];stream.read(&mut buffer).await.unwrap();let get = b"GET / HTTP/1.1\r\n";// Respond with greetings or a 404,// depending on the data in the requestlet (status_line, filename) = if buffer.starts_with(get) {("HTTP/1.1 200 OK\r\n\r\n", "hello.html")} else {("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")};let contents = fs::read_to_string(filename).unwrap();// Write response back to the stream,// and flush the stream to ensure the response is sent back to the clientlet response = format!("{status_line}{contents}");stream.write_all(response.as_bytes()).await.unwrap();stream.flush().await.unwrap();
}

UDP

  • 广播使用广播地址255.255.255.255,将消息发送到在同一广播网络上的每个主机,广播仅仅在同一局域网上才能进行,但是广播还是要指明接收者的端口号的

  • 多播,也称为“组播”,与单播一样,多播是允许在广域网即Internet上进行传输的,多播的地址是特定的,D类地址用于多播。即224.0.0.0至239.255.255.255之间的IP地址。

    1、局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包。

    2、预留多播地址:在224.0.1.0~238.255.255.255之间,可用于全球范围(如Internet)或网络协议。

    3、管理权限多播地址:在239.0.0.0~239.255.255.255之间,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。

  • 单播的数据只是收发数据的特定主机进行处理,根据地址不同,可以跨越局域网进行internet通讯。

    • 单播流程:主机A向主机B发送UDP数据报,发送的目的IP为192.168.1.151,端口为 80,目的MAC地址为00:00:00:00:00:02。
    • 广播的流程:主机A向整个网络发送广播数据,发送的目的IP为192.168.1.255,端口为 80,目的MAC地址为FF:FF:FF:FF:FF:FF。
use std::{prelude::*, net, thread, env};
use std::io::{Read, Write};fn main() {let args = env::args().into_iter().collect::<Vec<_>>();if args.len()>1 {match args[1].as_str() {"-s" => udp_server(),"-c" => udp_client(),_ => println!("unknown cmd {}", args[1]),}}else{println!("Usage:\r\n\t-s Open UDP Server.\r\n\t-c Open UDP client to connect the server.")}
}
fn udp_server() {let s = net::UdpSocket::bind("0.0.0.0:8000").unwrap();println!("Listen on addr: {}", s.local_addr().unwrap().to_string());let mut buf = [0u8; 1024];loop{if let Ok((n, addr)) = s.recv_from(&mut buf) {println!("addr: {}, content: {}", addr.to_string(), String::from_utf8_lossy(&buf[..n]));_ = s.send_to(format!("ok! you are: {}", addr.to_string()).as_bytes(), addr);}}
}fn udp_client(){// 不指定地址let mut c = net::UdpSocket::bind("0.0.0.0:0").unwrap();println!("client addr: {}", c.local_addr().unwrap().to_string());let s_addr = "127.0.0.1:8000";let mut buf = [0u8; 1024];for i in 0..10 {_ = c.send_to(format!("client: I'm coming! on {}!", i).as_bytes(), s_addr);if let Ok((n, addr)) = c.recv_from(&mut buf) {println!("client: s addr: {}, content: {}", addr.to_string(), String::from_utf8_lossy(&buf[..n]));}}
}

Channel

通道(Channel)是一种用于在多个线程之间传递数据的并发原语。通道提供了一种安全且高效的方式,允许线程之间进行通信和同步。

我们可以使用 std::sync::mpsc 模块提供的 channel 函数来创建一个通道。mpsc 是“多个生产者,单个消费者”(Multiple Producers, Single Consumer)的缩写,意味着多个线程可以同时向通道发送数据,但只有一个线程可以从通道接收数据。

use std::sync::mpsc;
use std::thread;
use std::time::Duration;fn main() {// 创建通道,返回发送者和接收者// 默认是异步的,不阻塞对方。// mpsc::sync_channel(0) 就是同步的,缓冲区满了就会阻塞let (tx, rx) = mpsc::channel();// thread 1let tx2 = tx.clone();thread::spawn(move || {for i in 0..30 {let message = format!("2 - {}. Hello from the sender!", i);tx2.send(message).unwrap();thread::sleep(Duration::from_secs(1));}println!("Thread 2 End");// drop(tx2);});// thread 2thread::spawn(move || {for i in 0..20 {let message = format!("1 - {}. Hello from the sender!", i);tx.send(message).unwrap();thread::sleep(Duration::from_secs(1));}println!("Thread 1 End");// drop(tx);});// 在主线程接收数据// for received in rx {//     println!("Got: {}", received);// }loop {if let Ok(r) = rx.recv() {println!("Received: {}", r);}else{println!("Receiced error!");break;}}println!("End");
}

一个常见的坑

use std::sync::mpsc;
fn main() {use std::thread;let (send, recv) = mpsc::channel();let num_threads = 3;for i in 0..num_threads {let thread_send = send.clone();thread::spawn(move || {thread_send.send(i).unwrap();println!("thread {:?} finished", i);});}// 在这里如果不drop,会因为Send的生命周期一直到main结束,所以recv也不会结束,所以无法退出// drop(send);for x in recv {println!("Got: {}", x);}println!("finished iterating");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/771100.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

淘宝扭蛋机小程序开发:开启线上扭蛋新体验

随着科技的飞速发展和移动互联网的普及&#xff0c;线上娱乐方式也变得越来越多样化。为了满足广大用户对于新鲜、有趣的娱乐体验的需求&#xff0c;我们决定开发一款淘宝扭蛋机小程序&#xff0c;为用户带来全新的线上扭蛋乐趣。 淘宝扭蛋机小程序将结合淘宝平台的优势资源&a…

flask_restful渲染模版

渲染模版就是在 Flask_RESTful 的类视图中要返回 html 片段代码&#xff0c;或 者是整个html 文件代码。 如何需要浏览器渲染模板内容应该使用 api.representation 这个装饰器来定 义一个函数&#xff0c; 在这个函数中&#xff0c;应该对 html 代码进行一个封装&#xff…

LeetCode-88

题目&#xff1a; 88. 合并两个有序数组 思路&#xff1a; 最开始想到的就是使用一个中间数组来存放合并后的数据&#xff0c;对两个数组使用双指针进行遍历&#xff0c;将两个数组中较小的放入中间数据。 看了题解&#xff0c;发现可以逆序进行遍历&#xff0c;因为nums1中后…

基于nginx 动态 URL反向代理的实现

背景&#xff1a; 我们在项目中在这样一个场景&#xff0c;用户需要使用固定的软件资源&#xff0c;这些资源是以服务器或者以容器形式存在的。 资源以webAPI方式在内网向外提供接口&#xff0c;资源分类多种类型&#xff0c;每种类型的资源程序和Wapi参数都一样。这些资源部属…

setitimer实现每隔一秒打印输出一次

setitimer是一种定时器&#xff0c;可以通过C语言中的系统调用函数setitimer()来实现。 struct itimerval { struct timerval it_interval; // 闹钟触发周期 struct timerval it_value; // 闹钟触发时间 }; struct timeval { long tv_sec; // 秒 lo…

学习SpringBoot笔记--知识点(1)

目录 SpringBoot介绍 创建一个最基础的springbooot项目 使用Spring Initializr创建springboot项目 Spring Boot 自动配置机制 SpringBoot常用注解 1.组件注册 2.条件注解 3.属性绑定 SpringBoot自动配置流程​编辑 学习SpringBoot的方法 ​编辑 SpringBoot日志配置…

2015年认证杯SPSSPRO杯数学建模A题(第一阶段)绳结全过程文档及程序

2015年认证杯SPSSPRO杯数学建模 A题 绳结 原题再现&#xff1a; 给绳索打结是人们在日常生活中常用的技能。对登山、航海、垂钓、野外生存等专门用途&#xff0c;结绳更是必不可少的技能之一。针对不同用途&#xff0c;有多种绳结的编制方法。最简单的绳结&#xff0c;有时称…

PyCharm环境下Git与Gitee联动:本地与远程仓库操作实战及常见问题解决方案

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 前言下载及安装GitGit的使用设置用户签名设置用户安全目录Git基本操作Git实操操作 Pyc…

牛可编程题

提示&#xff1a;文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问&#xff1a; 本文目标&#xff1a; 一、背景 最近 二、 2.1 坐标移动 https://www.nowcoder.com/practice/119bcca3befb405fbe58abe9c532eb29?tpId37&tqId21240&rp1&ru/exam/…

SpringBoot+Vue前后端分离项目在Linux系统中基于Docker打包发布,并上传镜像到阿里镜像私仓

文章目录 SpringBootVue前后端分离项目在Linux系统中基于Docker打包发布&#xff0c;并上传镜像到阿里镜像私仓一、Java项目基于Docker打包发布1.打包应用&#xff0c;将打好的jar包放到我们的linux系统中2.新建dockerfile3.打包镜像4.测试运行5.上传镜像到阿里云免费私仓 二、…

Webpack生成企业站静态页面 - 项目搭建

现在Web前端流行的三大框架有Angular、React、Vue&#xff0c;很多项目经过这几年的洗礼&#xff0c;已经都 转型使用这三大框架进行开发&#xff0c;那为什么还要写纯静态页面呢&#xff1f;比如Vue中除了SPA单页面开发&#xff0c;也可以使用nuxt.js实现SSR服务端渲染&#x…

牛客HJ43 迷宫问题中使用python,append在递归调用时的问题

题目描述 牛客&#xff1a;HJ43 迷宫问题 定义一个二维数组 N*M &#xff0c;如 5 5 数组下所示&#xff1a; int maze[5][5] { 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; 它表示一个迷宫&#xff0c;其中的1表示墙壁&#xff0c;0…

基于前端技术实现的全面预算编制系统

前言 在现代商业环境中&#xff0c;预测销售数据和实际成本是每个公司CEO和领导都极为重视的关键指标。然而&#xff0c;由于市场的不断变化&#xff0c;准确地预测和管理这些数据变得愈发具有挑战性。为了应对这一挑战&#xff0c;建立一个高效的系统来管理和审查销售数据的重…

hbase启动错误-local host is“master:XXXX“ destination is:master

博主的安装前提&#xff1a; zookeeper安装完成&#xff0c;且启动成功 hdfs高可用安装&#xff0c;yarn高可用安装&#xff0c;且启动成功 报错原因&#xff1a;端口配置不对 解决方案&#xff1a; 输入&#xff1a;hdfs getconf -confKey fs.default.name 然后把相应的…

考研数学一——概率论真题——自我总结题型整理(总分393)

系列文章目录 终于考完研了&#xff0c;本人考的是南京航空航天大学的仪器科学与技术&#xff0c;英一数一电路&#xff0c;以下是成绩单&#xff1a; 平时习惯整理自己的学习体系&#xff0c;以下是一个记录。 其实&#xff0c;每个人都应该训练&#xff0c;看到某一类题目…

差异性分析通常包括以下几个基本步骤

差异性分析通常包括以下几个基本步骤&#xff1a; 明确研究目的&#xff1a;首先需要明确研究的目的和问题&#xff0c;确定要比较的变量或因素是什么&#xff0c;以及希望从数据中获得怎样的信息。 收集数据&#xff1a;收集与研究问题相关的数据样本&#xff0c;确保数据的质…

2024/03/25(C++·day1)

一、思维导图 二、练习 练习一 定义自己的命名空间&#xff0c;其中有string类型的变量&#xff0c;再定义两个函数&#xff0c;一个函数完成字符串的输入&#xff0c;一个函数完成求字符串长度&#xff0c;再定义一个全局函数完成对该字符串的反转 #include <iostream&g…

小白windows虚拟机共享文件

ubuntu访问windows共享文件 [windows 管理员 创建共享用户bat set userflyShare set pwd123 net user %user% /add net user %user% %pwd% net localgroup Users %user% /delete net user %user% /passwordchg:nonet share toUbuntuH: ( /grant:%user%,full 指定用户权限…

windows powershell连接linux 上传下载文件

连接&#xff1a;输入下面命令&#xff0c;回车 输入密码进入linux系统 ssh root192.168.188.128退出linux logoutwindow上传文件到Linux服务器 把桌面的123.txt 上传到linux home文件夹下 scp C:\Users\pzx\Desktop\123.txt root192.168.188.128:/homelinux下载文件到windo…

Midjourney公司新功能发布公告

亲爱的Midjourney用户们&#xff0c;您好&#xff01; 在这个创新不断的时代&#xff0c;Midjourney一直致力于为您提供最前沿的技术和服务。今天&#xff0c;我们非常兴奋地宣布推出两个全新的算法和一个新的版本控制功能&#xff0c;旨在进一步优化您的体验&#xff0c;并且更…