【计算机网络】Socket网络编程

💻文章目录
- 📄前言
- Socket编程基础
- 概念
- 工作原理
- Socket API介绍
- socket函数
- 绑定、监听函数
- accept、connect
- 接受/发送函数
- Socket API的应用
- Socket类与其派生类的设计
- 服务器与客户端的设计
- 使用
- 📓总结
📄前言
现今我们的日常生活当中,网络已经成为了必不可少的存在,大到覆盖全世界的互联网,小到身边的各种电器,可以说网络无处不在。我们作为一名程序员,如果对网络不甚了解,那么
注定会度过一个相对失败的一生,需要利用网络进行通信的应用正变得越来越多,企业对程序员网络知识的需求也越发变得重要,因此,学习网络一定会对你有所帮助。
Socket编程基础
概念
Socket 的中文名可以译为套接字、插座,就像它的直译插座一样,Socket 是两台机器网络通讯的端点,只要使用Socket就能连接两台机器,从而实现数据的传输、交换。
在介绍Socket是如何工作前,我们需要先了解一下网络的基本术语。
-
基础术语
-
IP地址:IP地址是设备在网络上的标识符,要进行网络通信就必须拥有一个IP地址
-
端口:端口的设计是为了让网络数据正确发送到应用程序,计算机通过IP+端口号来确保数据收发正确。
-
协议:协议是定义数据如何在网络传输的规则,Socket编程中会接触到的协议有UDP、TCP协议。
工作原理
正如上方所说Socket是网络通信的端点,Socket的工作原理是基于C—S模型,即必定会有客户端与服务端的存在。既然要通信,那么就一定会有协议的存在。socket 有面向字节流协议的SOCK_STREAM、面向数据报的 SOCK_DGRAM 和 直接将数据发往IP层的原始套接字 SOCK_RAW。
其实 SOCK_STREAM 与 SOCK_DGRAM 就已经可以完成99%的网络通讯设计,毕竟现在网络上主流的协议也就是UDP和TCP协议。虽然协议本身区别很大,但在应用层的使用上,大体还是差不多的。
-
服务器端的工作流程
-
创建套接字。
-
绑定地址。
-
接受数据。
-
发送数据。
-
-
客户端的工作流程
- 创建套接字
- 提前确定远端的地址、端口
- 发送数据
- 接受数据。
Socket API介绍
socket函数
socket函数是系统用于创建套接字描述符的接口,该函数会返回一个文件描述符,之后网络的通信便围绕着这个文件描述符进行。
#include <sys/socket.h>//函数原型
int socket(int domain, int type, int protocol);
// 返回值为文件描述符
int fd = socket(AF_INET, SOCK_STREAM, 0);
- 参数选项
- domain: 用于指定通信域,常用的选项为
AF_INET
(指定使用IPV4通信),AF_INET6
(指定IPV6通信),AF_UNIX
(指定本地进程间通信)。 - type: 用于指定socket的类型。常用的选项为
SOCK_STREAM
(提供可靠的流传输服务,也就是TCP),SOCK_DGRAM
(提供不可靠的数据报服务,也就是UDP)。 - protocl: 用于指定是否使用特殊协议,一般设为0。
- domain: 用于指定通信域,常用的选项为
绑定、监听函数
bind 函数用于让程序绑定一个固定的端口号,使套接字只从该端口号接受/发送数据,一般用于服务器显示绑定地址,客户端通过系统自动分配。listen 函数用于监听端口号,等待客户端的连接。
#include <sys/socket.h>int listen(int sockfd, int backlog); //成功返回0int bind(int sockfd, const struct sockaddr *addr, //成功返回0socklen_t addrlen);/* socketaddr是C语言历史缘由而留下来的结构体,因为当初C语言还不支持
void* 类型,所以设计出了sockaddr类型,以应对不同的选项。 *///以下是socketaddr家族
struct sockaddr { //基础类型sa_family_t sa_family; char sa_data[14];
}struct sockaddr_in { __uint8_t sin_len; //无特殊要求不会指定值sa_family_t sin_family; //设置协议家族(如AF_INET、AF_UNIX)in_port_t sin_port; //设置端口struct in_addr sin_addr; //设置IP地址char sin_zero[8];
};//socket_in6 用于IPV6设置。
- bind 参数选项
- sockfd: socket 文件描述符。
- addr: 绑定socket_addr。
- addrlen: 指定socket_addr的长度。
- listen 参数选项
- sockfd: 指定需要监听的套接字。
- backlog: 用于指定套接字中处于排队TCP连接数(还未得到处理),用于防止 SYN 泛洪攻击。
accept、connect
accept 和 connect 这两个函数,它们一般用于TCP协议,因为UDP是无连接的所以用不上(connect除外)。
accept 函数用于接受一个TCP连接,并返回它的套接字描述符,之后的读写则往该套接字描述符进行。注意,使用前需要先建立好监听状态。
connect 函数用于连接一个远端的服务器,成功则返回0.
#include <sys/types.h>
#include <sys/socket.h>int
accept(int socket, struct sockaddr *address, socklen_t *address_len);int //connect函数用于连接服务器
connect(int socket, const struct sockaddr *address, socklen_t address_len);
//UDP连接也可以使用connect函数,一般用于为UDP的套接字绑定一个固定的远端地址,从此该套接字就只能接受该地址的数据(过滤)。
-
accept 的参数选项
- socket: 指定需要接受数据的套接字接口
- address: 该结构用于接收连接方的协议地址。如果不想要远端的信息,可以设null。
- address_len: 用于指定address的长度。
-
connect 的参数选项
- socket: socket 文件描述符
- address: 指向存放目标服务器地址的信息。
- addlen: 指定addr结构体的长度。
接受/发送函数
unix like 系统中,UDP与TCP协议数据的收发所使用的函数有些许的差别,主要就是是否需要指定远端的地址、端口的差别,TCP方面因为已经通过 accpt 创建了一个包含远端信息的套接字,而UDP是无连接的,所以需要传入一个包含远端信息的sockaddr 结构体。
TCP协议所使用的接发函数:
#include <sys/socket.h>// recv send 参数都是一致的。
ssize_t recv(int sockfd, void buf, size_t len,int flags); ssize_t send(int sockfd, const void buf, size_t len, int flags);
- 参数选项:
- sockfd: 指定远端的套接字接口
- buf: 需要接受/发送的数据
- len: 数据的长度
- flags: 可提供额外的控制选项,如指定
阻塞等待(MSG_DONTWAIT)
。
UDP协议所使用的接收/发送函数:
ssize_t recvfrom(int sockfd, void buf, size_t len,int flags, struct sockaddr * src_addr, //可设为空,但如果要发送数据则要存储该结构体socklen_t * addrlen); ssize_t sendto(int sockfd, const void buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);// 额外所需要用的
uint16_t
htons(uint16_t hostshort); //将主机序列转为网络序列,网络数据使用大端传送unsigned long
inet_addr(const char *cp); //用于将字符串ip地址转为网络字节序的二进制形式
- 参数选项:
- sockfd: 指定需要接收/发送数据的套接字。
- buf: 数据所存放的内存
- flags: 发送的选项,与TCP一样
- addr: 如果需要发送数据,则需要在recvfrom指定中存储的sockadd结构体
- addrlen: sockaddr 的长度,如 sockaddr_in 。
Socket API的应用
介绍完了基本的函数信息,也该到实践的环节了,但如果只是简单写下函数的使用方法,也并没有什么实际意义,那么不如构建一个Socket编程的模版,这样以来使用 Socket 编程就不必再敲重复的代码,而且也能提高对设计模式的理解。
代码的大致样子:
Socket类与其派生类的设计
Socket 基类是TcpSocket、UdpSocket的抽象基类,用于提高代码的复用性。
#include <iostream>
#include <utility>
#include <arpa/inet.h>#define MAX_BUFFER_SIZE 1024struct RemoteData //用于获取远端数据
{
public:RemoteData() = default;explicit RemoteData(sockaddr_in& client, int fd = -1):_addr(client), _socket(fd){_data.resize(MAX_BUFFER_SIZE); }~RemoteData() = default;sockaddr_in _addr;std::string _data; // 改用 char buffer[];int _socket = -1;
};// 使用模版方法进行封装
class Socket
{
public:Socket(std::string ip, int port):_ip(std::move(ip)), _port(port){}void BuildServer() //用于服务器的构造{bool socket = CreateSocket();bool bindSocket = BindSocket();if(!(socket && bindSocket)){std::cerr << "socket build failed\n";return;}std::cout << "socket build success\n";}void BuildClient() // 用于客户端的构造{bool socket = CreateSocket();bool connectSocket = ConnectSocket();if(!(socket && connectSocket)){std::cerr << "socket build failed\n";return;}std::cout << "socket build success\n";}protected:virtual bool BindSocket() // 用于绑定套接字{if(::bind(_socket, (struct sockaddr*)&_addr, sizeof(_addr)) < 0)return false;return true;}virtual bool Accept(RemoteData* data) // 用于接受套接字{socklen_t len = sizeof (sockaddr_in);sockaddr_in* addr = &data->_addr;data->_socket = accept(_socket, (sockaddr*)addr, &len);if(data->_socket < 0){std::cerr << "accept failed " << strerror(errno) << std::endl;return false;}return true;}virtual bool ConnectSocket() // 用于连接套接字{if(::connect(_socket, (struct sockaddr*)&_addr, sizeof(_addr)) < 0)return false;return true;}virtual ~Socket() = default; // 基类继承需要把析构函数设为虚函数。virtual bool CreateSocket() = 0; // 创建套接字virtual bool RecvData(RemoteData*) = 0; // 接收数据virtual bool SendData(RemoteData*) = 0; // 发送数据
protected:int _socket=-1;int _port{};std::string _ip{};sockaddr_in _addr{};
};
TcpSocket 和 UdpSocket
TcpSocket 与 UdpSocket 就如其名,对应了TCP与UDP的socket编程设计。
class UdpSocket : public Socket
{ // UdpSocket如果使用connect函数,可以使用send、recv来代替sendto、recvfrom
public:UdpSocket(std::string ip, int port): Socket(std::move(ip), port) //初始化基类{}~UdpSocket() override = default;protected:bool CreateSocket() override{_addr.sin_family = AF_INET;_addr.sin_port = htons(_port); //将主机字节序列转为网络字节序列_addr.sin_addr.s_addr = inet_addr(_ip.c_str());_socket = socket(AF_INET, SOCK_DGRAM, 0);if(_socket < 0) return false; //错误处理return true;}bool RecvData(RemoteData* remoteData) override{char* buffer = remoteData->_data.data();socklen_t len = sizeof(sockaddr_in);sockaddr_in* client = &remoteData->_addr;ssize_t n = recvfrom(_socket, buffer, MAX_BUFFER_SIZE-1, 0, (struct sockaddr*)client, &len);if(n == 0){ std::cout << "client close\n";return false;}else if(n > 0){buffer[n] = '\0';std::cout << "recv data : " << buffer << std::endl;return true;}else{std::cerr << "recvfrom error\n";return false;}}bool ConnectSocket() override{return true;}bool SendData(RemoteData* data) override{char* buffer = data->_data.data();sockaddr_in* client = &data->_addr;ssize_t n = sendto(_socket, buffer, strlen(buffer), 0, (struct sockaddr*)client, sizeof(*client));if(n < 0){std::cerr << "sendto error: " << strerror(errno) << std::endl;return false;}return true;}
};class TcpSocket : public Socket
{
public:TcpSocket(std::string ip, int port): Socket(std::move(ip), port){}~TcpSocket() override = default;bool CreateSocket() override{ _addr.sin_family = AF_INET;_addr.sin_port = htons(_port);_addr.sin_addr.s_addr = inet_addr(_ip.c_str());_socket = socket(AF_INET, SOCK_STREAM, 0); //使用SOCK_STREAMif(_socket < 0) return false;return true;}bool BindSocket() override{if(::bind(_socket, (struct sockaddr*)&_addr, sizeof(_addr)) ){std::cerr << "bind failed\n";return false;}listen(_socket, 5); //TCP服务器需要监听端口return true;}bool RecvData(RemoteData* remoteData) override{int socket = remoteData->_socket;char* buffer = remoteData->_data.data();ssize_t n = recv(socket, buffer, MAX_BUFFER_SIZE-1, 0);if(n == 0){std::cout << "client close\n";return false;}else if(n > 0){buffer[n] = '\0';std::cout << "recv data : " << buffer << std::endl;return true;}else{std::cerr << "recv error\n";return false;}}bool SendData(RemoteData* data) override{int socket = data->_socket;char* buffer = data->_data.data();ssize_t n = send(socket, buffer, strlen(buffer), 0);if(n < 0){std::cerr << "send error\n";return false;}return true;}
};
服务器与客户端的设计
服务器设计
#include <memory>
#include <print>
#include "Socket.hpp"
//#include "ThreadPool.hpp" //不懂线程池的可以去看看我写的线程池博客class UdpServer : protected UdpSocket
{
public:UdpServer(int port, std::function<void(RemoteData*)> handler): UdpSocket("0.0.0.0", port), _handle(std::move(handler)){BuildServer(); //构建Socket}void Run(RemoteData* data){_handle(data); //业务处理函数SendData(data);}void start(){std::string msg;while (true){sockaddr_in client{};std::shared_ptr<RemoteData> data = std::make_shared<RemoteData>(RemoteData(client));if(!RecvData(data.get()))continue;
// ThreadPool::GetInstance()->enqueue([this, data]{ Run(data.get());});Run(data.get());}}private:std::function<void(RemoteData*)> _handle; //业务处理函数
};class TcpServer : TcpSocket // 注意:这个TCP协议需要进行粘包处理。
{
public:TcpServer(int port, std::function<void(RemoteData*)> handler): TcpSocket("0.0.0.0", port), _handle(std::move(handler)){BuildServer();}void ThreadRun(RemoteData* data){while (true){if(!RecvData(data)) break;_handle(data);if(!SendData(data)) break;}close(data->_socket);}void start(){std::string msg;while (true){sockaddr_in client{};std::shared_ptr<RemoteData> data = std::make_shared<RemoteData>(RemoteData(client));if(!Accept(data.get()))break;
// ThreadPool::GetInstance()->enqueue([this, data]{ ThreadRun(data.get());}); //最好使用多线程进行业务处理,否则将只能处理一条连接ThreadRun(data.get());}}
private:std::function<void(RemoteData*)> _handle;
};
客户端设计
class UdpClient : public UdpSocket
{
public:UdpClient(std::string ip, int port, std::function<void(RemoteData*)> func): UdpSocket(std::move(ip), port), _func(std::move(func)){BuildClient(); //}void start(){while (true){std::shared_ptr<RemoteData> data = std::make_shared<RemoteData>(RemoteData(_addr));_func(data.get());SendData(data.get());RecvData(data.get());}}private:std::function<void(RemoteData*)> _func;
};class TcpClient : public TcpSocket
{
public:TcpClient(std::string ip, int port, std::function<void(RemoteData*)> func): TcpSocket(std::move(ip), port), _func(std::move(func)){BuildClient();}void start(){while (true){std::shared_ptr<RemoteData> data = std::make_shared<RemoteData>(RemoteData(_addr, _socket));_func(data.get());SendData(data.get());RecvData(data.get());}}private:std::function<void(RemoteData*)> _func;
};
使用
//client.cpp
#include "Client.hpp"void handler(RemoteData* data)
{std::cout << "client: ";std::cin >> data->_data;
}int main()
{ // 使用本地环回进行通信UdpClient client("127.0.0.1", 8888, handler);client.start();return 0;
}//server.cpp
#include "Server.hpp"void handler(RemoteData* data)
{}int main() {ThreadPool* pool = ThreadPool::GetInstance(5);UdpServer server(8888, std::function<void(RemoteData*)>(handler));server.start();return 0;
}
📓总结
学习Socket编程只是迈入网络编程的第一步,计算机网络中还有TCP、UDP协议、IP协议等各种难关来等着我们来一一攻破。虽然你可能觉得学习Socket编程对学习TCP/IP协议这些没什么帮助,学校的老师也从来不会从代码开始攻坚计算机网络,但计算机网络就应该自顶至下,从应用层的应用开始学起。
📜博客主页:主页
📫我的专栏:C++
📱我的github:github

相关文章:

【计算机网络】Socket网络编程
💻文章目录 📄前言Socket编程基础概念工作原理 Socket API介绍socket函数绑定、监听函数accept、connect接受/发送函数 Socket API的应用Socket类与其派生类的设计服务器与客户端的设计使用 📓总结 📄前言 现今我们的日常生活当中…...

Ansible自动运维工具之playbook
目录 一.inventory主机清单 1.定义 2.变量 (1)主机变量 (2)组变量 (3)组嵌套 二.playbook基本内容 1.组成 (1)Tasks: 任务,即调用模块完成的某操作 ࿰…...

【启明智显技术分享】SSD201/SSD202D核心板UI界面开发全攻略:LVGL使用指南
提示:作为Espressif(乐鑫科技)大中华区合作伙伴及sigmastar(厦门星宸)VAD合作伙伴,我们不仅用心整理了你在开发过程中可能会遇到的问题以及快速上手的简明教程供开发小伙伴参考。同时也用心整理了乐鑫及星宸…...

数据可视化(九):Pandas北京租房数据分析——房源特征绘图、箱线图、动态可视化等高级操作
Tips:"分享是快乐的源泉💧,在我的博客里,不仅有知识的海洋🌊,还有满满的正能量加持💪,快来和我一起分享这份快乐吧😊! 喜欢我的博客的话,记得…...

ADOP带你了解:跳线与交叉电缆有何不同?
如果您想将设备连接到互联网,您可能想知道要使用的正确电缆。跳线和交叉电缆都是类型的以太网电缆,可帮助连接计算机、调制解调器、路由器和交换机等设备。那么,跳线和交叉电缆有什么区别呢?让我们讨论这两种类型的电缆࿰…...
Django 和 Spring Boot
标题 Django (Python)Django提供的组件Django 的处理逻辑 Spring Boot (Java)Spring Boot 的特点Spring Boot 的处理逻辑 MVC设计模式模型(Model)视图(View)控制器(Controller)逻辑处理过程 Django 和 Spri…...

上位机图像处理和嵌入式模块部署(树莓派4b的替代品)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 实话实说,树莓派4b的产品力还是比较优秀的,价格还算适中。但是和国产卡片电脑比起来,则逊色不少。功能差不多的…...

Springboot整合 Spring Cloud Gateway
1.Gateway介绍 1.是spring cloud官方推出的响应式的API网关框架,旨在为微服务架构提供一种简单有效的API路由的管理方式,并基于Filter的方式提供网关的基本功能,例如:安全认证,监控,限流等等。 2.功能特征…...

Rust开发工具有哪些?
目录 一、JetBrains公司的RustRover编辑 二、微软公司的Visual Studio Code 三、Rust编译工具 一、JetBrains公司的RustRover RustRover是由JetBrains开发的一款专为Rust开发量身定制的新兴IDE,目前还处于早期访问阶段。它支持Rust、Cargo、TOML、Web和数据库等…...

20240514基于深度学习的弹性超材料色散关系预测与结构逆设计
论文:Dispersion relation prediction and structure inverse design of elastic metamaterials via deep learning DOI:https://doi.org/10.1016/j.mtphys.2022.100616 1、摘要 精心设计的超材料结构给予前所未有的性能,保证了各种各样的具…...

SAP:FI 财务凭证行项目文本前台修改
一、问题描述 财务凭证行项目文本点击修改,但是前台有的行可以修改,有的行是灰色的不能修改,如下图所示,这个文本信息有误,必须修改怎么办? 二、思路分析 有的行可以修改,有的行不能修改&#x…...

【linux系统学习教程 Day02】网络安全之Linux系统学习教程,管道,文件内容统计,过滤排序,去重,目录介绍
1-4 管道 管道符号: | ,可以将前面指令的执行结果,作为后面指令的操作内容。 ## 比如过滤ip地址 ip addr | tail -4 | head -1 解释一下就是先执行 ip addr ,得到的结果当做 tail -4 的输入,意思就是查看ip addr 结果的后四行内容…...
Spring Cloud LoadBalancer 4.1.2
LoadBalancer位于Spring Cloud Commons 模块 Spring Cloud 提供了自己的客户端负载均衡器抽象和实现。对于负载均衡机制,添加了 ReactiveLoadBalancer 接口,并为其提供了基于Round-Robin和Random的实现。为了让实例从反应式中进行选择,使用了…...

使用Xshell工具连接ubuntu-方便快捷
使用Xshell连接ubuntu 在命令行输入 “sudo apt-get install openssh-server”安装openssh-server 开启 ssh-server,在命令行输入 “service ssh start”,然后输入密码即可...
leetcode22 括号生成-组合型回溯
题目 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 示例 输入:n 3 输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”] 解析 func generateParenthesis(n int) …...

mac定时任务、自启动任务
https://quail.ink/mynotes/p/mac-startup-configuration-detailed-explanation <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.d…...

重磅 | 国家标准《网络安全技术 零信任参考体系架构》正式发布
根据2024年4月25日国家市场监督管理总局、国家标准化管理委员会发布的中华人民共和国国家标准公告(2024年第6号),其中易安联参编的国家标准GB/T 43696-2024《网络安全技术 零信任参考体系架构》正式发布,并于2024年11月1日正式施行…...

【C++】可变参数模板简单介绍
前言 可变参数模板是C11中的新特性,它能够让我们创建可以接收可变参数的函数模板和类模板,相比C98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数是一个巨大的改进,通过系统系统推演数据的类型…...

RabbitMQ--死信队列
目录 一、死信队列介绍 1.死信 2.死信的来源 2.1 TTL 2.2 死信的来源 3.死信队列 4.死信队列的用途 二、死信队列的实现 1.导入依赖 pom.xml 2.application.properties 3.配置类 4.生产者 5.业务消费者(正常消费者) 6.死信队列消费者 一、…...

微信小程序毕业设计-基于Java后端的微信小程序源码150套(附源码+数据库+演示视频+LW)
大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。 🧡今天给大家分享150的微信小程序毕业设计,后台用Java开发,这些项目都经过精心挑选,涵盖了不同的实战主题和用例,可做毕业设…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...