MFC网络编程2——异步套接字

 从上一节(MFC网络编程1——网络基础及套接字 )中,我们了解了网络的部分基础知识以及套接字的使用,这一节,我们学习异步套接字的使用。
 Windows套接字在两种模式下执行I/O操作,阻塞模式和非阻塞模式。在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回,例如,程序中调用了recvfrom函数后,如果这时网络上没有数据传送过来,该函数就会阻塞程序的执行,从而导致调用线程暂停运行。
 在非阻塞模式下,Winsock函数无论如何都会立即返回,在该函数执行的操作完成之后,系统会采用某种方式将操作结果通知给调用线程。在很多情况下,阻塞方式会影响应用程序的性能,所以有时需要采用非阻塞方式实现网络应用程序。Windows Sockets为了支持Windows消息驱动机制,使应用程序开发者能够方便的处理网络通信,它对网络事件采用了基于消息的异步存取策略。Windows Sockets的异步选择函数WSAAsyncSelect提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与该事件相关的一些信息。
 因此,可以针对不同的网络事件进行登记,例如,如果登记一个网络读取事件,一旦有数据到来,就会触发这个事件,操作系统就会通过一个消息来通知调用线程,后者就可以在相应的消息响应函数中接受到这个数据。因为是在该数据到来之后,操作系统发出的通知,所以这时肯定能够接收到数据。采用异步套接字能够有效地提高应用程序的性能。
 这一节,我们利用异步套接字完成一个聊天室程序,如下图所示。项目工程下载链接为:
 https://download.csdn.net/download/mary288267/88344113
在这里插入图片描述

1 相关函数

 Windows Socket规范提供了一组基于Berkeley套接字函数的扩展函数。这些扩展函数在实现Socket功能的基础上,还允许基于消息或函数进行处理,处理异步网络事件,开启重叠I/O功能。除了WSAStartup()函数和WSACleanup()函数外,编写Socket程序可以不使用这些扩展API函数,但是建议使用这些扩展函数以保持与Windows编程模式一致。
 下面,为了使用异步套接字进行编程,我们将介绍几个比较重要的扩展函数,这些扩展函数均是以WSA开头。

1.1 WSAStartup

 WSAStartup函数将初始化进程使用的Winsock DLL(WS2_32.DLL),原型如下

int WSAAPI WSAStartup(
[in] WORD wVersionRequested,
[out] LPWSADATA lpWSAData
);

参数
 [in] wVersionRequested: 调用方可以使用的最高版本的 Windows 套接字规范。 高序字节指定次要版本号;低序字节指定主版本号。
 [out] lpWSAData: 指向 WSADATA 数据结构的指针,用于接收 Windows 套接字实现的细节。

1.2 WSACleanup

 WSACleanup函数将终止程序对套接字库Winsock 2 DLL(WS2_32.DLL)的使用,该函数的原型声明如下。

int WSAAPI WSACleanup();

1.3 WSASocket

 Winsock库中的扩展函数WSASocket将创建套接字,其原型声明如下:

SOCKET WSAAPI WSASocketW(
[in] int af,
[in] int type,
[in] int protocol,
[in] LPWSAPROTOCOL_INFOW lpProtocolInfo,
[in] GROUP g,
[in] DWORD dwFlags
);

 参数
 [in] af:地址族。对于TCP/IP协议的套接字,只能是AF_INIET。
 [in] type:套接字的类型规范,具体如下表所示。在 Windows 套接字 1.1 中,唯一可能的套接字类型是 SOCK_DGRAM 和 SOCK_STREAM。SOCK_STREAM产生流式套接字;SOCK_DGRAM产生数据报套接字。

类型含义
SOCK_STREAM一种套接字类型,它通过 OOB 数据传输机制提供排序的可靠双向基于连接的字节流。 此套接字类型使用 Internet 地址系列 (AF_INET 或AF_INET6) 的传输控制协议 (TCP) 。
SOCK_DGRAM支持数据报的套接字类型,这些数据报是固定 (通常较小) 最大长度的无连接、不可靠的缓冲区。 此套接字类型对 Internet 地址系列 (AF_INET 或AF_INET6) 使用用户数据报协议 (UDP) 。
SOCK_RAW一种套接字类型,它提供允许应用程序操作下一层协议标头的原始套接字。 若要操作 IPv4 标头,必须在套接字上设置 IP_HDRINCL 套接字选项。 若要操作 IPv6 标头,必须在套接字上设置 IPV6_HDRINCL 套接字选项。
SOCK_RDM提供可靠消息数据报的套接字类型。 此类型的一个示例是 Windows 中的实用常规多播 (PGM) 多播协议实现,通常称为 可靠的多播编程。仅当安装了可靠多播协议时,才支持此 类型值。
SOCK_SEQPACKET提供基于数据报的伪流数据包的套接字类型。

 [in] protocol:要使用的协议。如果指定值 0,那么系统就会根据地址格式和套接字的类别,自动选择一个合适的协议。
 [in] lpProtocolInfo:指向 WSAPROTOCOL_INFO 结构的指针,该结构定义要创建的套接字的特征。 如果此参数为 NULL,则WinSock2.DLL使用前三个参数来决定使用哪一个服务提供者。如果此参数不为NULL,则套接字绑定到与指定的结构WSAPROTOCOL_INFO相关的提供者。
 [in] g:保留。
 [in] dwFlags:一组用于指定其他套接字属性的标志。

1.4 WSAAsyncSelect

 WSAAsyncSelect函数为指定的套接字请求基于Windows消息的网络事件通知,并自动将该套接字设置为非阻塞模式。

int WSAAPI WSAAsyncSelect(
[in] SOCKET s,
[in] HWND hWnd,
[in] u_int wMsg,
[in] long lEvent
);

 参数
 [in] s:标识需要事件通知的套接字的描述符。
 [in] hWnd:标识在发生网络事件时接收消息的窗口的句柄。
 [in] wMsg:发生网络事件时要接收的消息。
 [in] lEvent:位掩码,指定应用程序感兴趣的网络事件的组合。
 指定应用程序感兴趣的网络事件,该参数可以是下表中列出的值之一,并且可以用位或操作构造多个事件。

含义
FD_READ设置为接收读取就绪通知。
FD_WRITE想要接收准备写入的通知。
FD_OOB想要接收有关 OOB 数据到达的通知。
FD_ACCEPT想要接收传入连接的通知。
FD_CONNECT想要接收已完成连接或多点联接操作的通知。
FD_CLOSE想要接收套接字关闭的通知。
FD_QOS想要接收套接字服务质量 (QoS) 更改的通知。
FD_GROUP_QOS想要接收有关套接字组服务质量 (QoS) 更改的通知, (保留以供将来用于套接字组) 。 保留。
FD_ROUTING_INTERFACE_CHANGE想要接收指定目标 () 的路由接口更改通知。
FD_ADDRESS_LIST_CHANGE想要接收套接字协议系列的本地地址列表更改通知。

1.5 WSARecvFrom

 WSARecvFrom函数接收数据报类型的数据,并保存数据发送方的地址。

int WSARecvFrom(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
struct sockaddr FAR* lpFrom,
LPINT lpFromlen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

 参数:
 s:标识套接字的描述符。
 lpBuffers:一个指向WSABUF结构数组的指针。每个WSABUF结构包含缓冲区的指针和缓冲区的大小。
 dwBufferCount:lpBuffers数组中WSABUF结构的数目。
 lpNumberOfBytesRecvd:如果接收操作立即完成,则为一个指向所接收数据字节数的指针。
 lpFlags:一个指向标志位的指针。
 lpFrom:(可选)指针,指向重叠操作完成后存放源地址的缓冲区。
 lpFromlen:指向from缓冲区大小的指针,仅当指定了lpFrom才需要。
 lpOverlapped:指向WSAOVERLAPPED结构的指针(对于非重叠套接口则忽略)。
 lpCompletionRoutine:一个指向接收操作完成后调用的完成例程的指针。(对于非重叠套接口则忽略)。

1.6 WSASendTo

 WSASendTo函数使用重叠的I/O(如果适用)将数据发送到特定目标。

int WSAAPI WSASendTo(
[in] SOCKET s,
[in] LPWSABUF lpBuffers,
[in] DWORD dwBufferCount,
[out] LPDWORD lpNumberOfBytesSent,
[in] DWORD dwFlags,
[in] const sockaddr *lpTo,
[in] int iTolen,
[in] LPWSAOVERLAPPED lpOverlapped,
[in] LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

 参数:
 [in] s:标识一个套接字(可能已连接)的描述符。
 [in] lpBuffers:指向 WSABUF 结构数组的指针。 每个 WSABUF 结构都包含指向缓冲区的指针和缓冲区的长度(以字节为单位)
 [in] dwBufferCount:lpBuffers 数组中的 WSABUF 结构数。
 [out] lpNumberOfBytesSent:如果 I/O 操作立即完成,则指向此调用发送的字节数的指针。如果 lpOverlapped 参数不是 NULL,请对此参数使用 NULL,以避免潜在的错误结果。 仅当 lpOverlapped 参数不为 NULL 时,此参数才能为 NULL。
 [in] dwFlags:用于修改 WSASendTo 函数调用行为的标志。
 [in] lpTo:指向 SOCKADDR 结构中目标套接字地址的可选指针。
 [in] iTolen:lpTo 参数中地址的大小(以字节为单位)。
 [in] lpOverlapped:对于未重叠的套接字) , (忽略指向 WSAOVERLAPPED 结构的指针。
 [in] lpCompletionRoutine:类型_In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE 指向完成发送操作时调用的完成例程的指针, (忽略未重叠的套接字) 。

1.7 gethostbyname

 gethostbyname不属于套接字的扩展函数。该函数从主机数据库中获取主机名相对应的IP地址。

struct hostent* FAR gethostbyname(
__in const char* name
);

2 网络聊天室的实现

 下面采用基于消息的异步套接字来实现一个带图形界面的网络聊天室程序。
 首先,新建一个基于对话框的工程,工程取名:ChatAsync。

2.1 设置对话框控件

 该对话框资源上已有的控件全部删除,然后添加一些控件,并设置它们相关的属性,结果下图所示:
在这里插入图片描述
 该对话框上各控件的ID及说明如下(按控件在对话框上从上到下、从左到右的顺序)。

控件名称ID说明
接收组框IDC_STATIC标示作用
接收编辑框IDC_EDT_RECV显示所接受的数据
发送组框IDC_STATIC标示作用
IP地址控件IDC_IPADDRESS1允许用户按照点分十进制格式输入IP地址
发送编辑框IDC_EDT_SEND允许用户输入将要发送的内容
发送按钮IDC_BTN_SEND单击按钮,就将发送编辑框中的内容发送给聊条的对方
主机名编辑框IDC_EDT_HOSTNAME允许用户输入对方主机名

2.2 加载套接字库

 由于AfxSocketInit只能加载1.1版本的套接字库,而本程序需要使用套接字库2.0版本的一些函数,因此调用WSAStartup函数初始化程序所使用的套接字库。套接字库的初始化操作应该放在app类中的InitInstance函数中。

BOOL CChatAsyncApp::InitInstance()
{//初始化套接字库WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(2, 2);err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) {return FALSE;}/* Confirm that the WinSock DLL supports 2.2.*//* Note that if the DLL supports versions greater    *//* than 2.2 in addition to 2.2, it will still return *//* 2.2 in wVersion since that is the version we      *//* requested.                                        */if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){WSACleanup();return FALSE;}......省略
}

 请注意本实现文件需要包含头文件:winsock2.h,为了方便,可以将此头文件放在stdafx.h中;另外,本工程需要链接 ws2_32.lib引入库文件。

2.3 创建并初始化套接字

 接下来创建并初始化套接字,在dialog类中增加一个SOCKET类型的成员变量m_socket,即套接字描述符。然后,在CChatAsyncDlg类中添加下面成员函数的声明:

BOOL InitSocket();

 其实现代码为:

BOOL CChatAsyncDlg::InitSocket()
{m_socket = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, 0);if (INVALID_SOCKET == m_socket){MessageBox(_T("创建套接字失败!"));return FALSE;}SOCKADDR_IN addrSock;addrSock.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addrSock.sin_family = AF_INET;addrSock.sin_port = htons(6000);if (SOCKET_ERROR == bind(m_socket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR))){MessageBox(_T("绑定失败!"));return FALSE;}if (SOCKET_ERROR == WSAAsyncSelect(m_socket, m_hWnd, UM_SOCK, FD_READ))//if (SOCKET_ERROR == WSAEventSelect(m_socket, m_hWnd, UM_SOCK, FD_READ)){MessageBox(_T("注册网络读取事件失败!"));return FALSE;}return TRUE;
}

 在实现代码中,首先用WSASocket函数创建了套接字。然后用bind函数将套接字绑定到本地IP地址和端口上(端口号6000)。然后,调用WSAAsyncSelect请求一个基于Windows消息的网络事件通知,该函数的第1个参数就是标识请求网络事件通知的套接字描述符;第2个参数就是该对话框的窗口句柄,即CChatAsyncDlg的m_hWnd成员;第3个参数指定一个自定义消息(UM_SOCK),一旦指定的网络事件发生时,操作系统就会发送该自定义的消息通知调用线程;第4个参数是注册的事件,本例注册了一个读取事件(FD_READ)。这样,一旦有数据到来,就会触发FD_READ事件,系统就会通过UM_SOCK这则消息来通知调用线程,于是,在该消息的响应函数中接受数据,就可以接收到数据了。
 可以在CChatAsyncDlg类的OnInitDialog函数中调用这个函数,以便程序完成套接字的初始化工作。
 在CChatAsyncDlg类的头文件中,定义自定义消息:UM_SOCK,定义代码如下所示:

#define UM_SOCK WM_USER+1

2.4 实现接收端功能

 这里应注意,在注册的事件发生后,操作系统向调用进程发送相应的消息时,还会将该消息相应的信息一起传递给调用进程。事实上,自定义消息的消息映射宏为:

ON_MESSAGE(message, memberFxn )

 消息响应函数(memberFxn )的原型为:

afx_msg LRESULT (CWnd:: * )(WPARAM, LPARAM).

 因此,信息实际上是通过WPARAM和LPARAM传递的。
 在CChatAsyncDlg类的头文件中添加下列代码:


#define UM_SOCK WM_USER+1	//定义自定义消息
class CChatAsyncDlg : public CDialogEx
{.......省略afx_msg LRESULT OnSock(WPARAM wParam, LPARAM lParam);//声明自定义消息的响应函数DECLARE_MESSAGE_MAP().......省略
};

 在实现文件中,增加关于UM_SOCK的消息映射宏

BEGIN_MESSAGE_MAP(CChatAsyncDlg, CDialogEx).......省略ON_MESSAGE(UM_SOCK,OnSock).......省略
END_MESSAGE_MAP()

 以及OnSock函数的实现

LRESULT CChatAsyncDlg::OnSock(WPARAM wParam, LPARAM lParam)
{switch (LOWORD(lParam)){case FD_READ:{CString str;CString strTemp;WSABUF wsabuf;wsabuf.buf = new char[200];wsabuf.len = 200;DWORD dwRead;DWORD dwFlag = 0;SOCKADDR_IN addrFrom;int len = sizeof(SOCKADDR);if (SOCKET_ERROR == WSARecvFrom(m_socket, &wsabuf, 1, &dwRead, &dwFlag,(SOCKADDR*)&addrFrom, &len, NULL, NULL)){MessageBox(_T("接受数据失败!"));return 0;}HOSTENT* pHost;pHost = gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr, 4, AF_INET);//注意,这里面从char*→CSTring,费了一些工夫,CString构造函数里面不能将char*转为CString,//但是赋值函数可以,神奇CString psz1;//psz1=inet_ntoa(addrFrom.sin_addr);psz1 = pHost->h_name;CString psz2;psz2 = CA2T(wsabuf.buf);str.Format(_T("%s说:%s"), psz1, psz2);str += _T("\r\n");GetDlgItemText(IDC_EDT_RECV, strTemp);str += strTemp;SetDlgItemText(IDC_EDT_RECV, str);break;}default:break;}return 0;
}

 请注意响应函数中的switch语句,由于基于套接字上请求网络事件通知时,可以同时请求多个网络事件,也就说,不但可以请求FD_READ网络读取事件,还可以同时请求FD_WRITE网络写入事件,所以说需要用switch语句区分各类事件。
 通过读取lParam参数的低位字,就可以知道当前发生的网络事件类型。然后用WSARecvFrom接收数据。

2.5 实现发送端的功能

 主界面上发送按钮的单击消息响应函数为

void CChatAsyncDlg::OnBnClickedBtnSend()
{USES_CONVERSION;DWORD dwIP;CString strSend;WSABUF wsaBuf;DWORD dwSend;int len;SOCKADDR_IN addrTo;CString sHostName;HOSTENT* pHost;if (GetDlgItemText(IDC_EDT_HOSTNAME, sHostName), sHostName == _T("")){((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);addrTo.sin_addr.S_un.S_addr = htonl(dwIP);}else{pHost = gethostbyname(T2A(sHostName));if (!pHost)return;addrTo.sin_addr.S_un.S_addr = *((DWORD*)pHost->h_addr_list[0]);}addrTo.sin_family = AF_INET;addrTo.sin_port = htons(6000);//这里面也有个特殊处理,CT2A不可以用,但是T2A可以用,不知为何//另外,因为传递的是字节,所有要用strlen计算字节数之后再加1GetDlgItemText(IDC_EDT_SEND, strSend);len = strSend.GetLength();	wsaBuf.buf = T2A(strSend);wsaBuf.len = len + 1;wsaBuf.len = strlen(wsaBuf.buf) + 1;SetDlgItemText(IDC_EDT_SEND, _T(""));if (SOCKET_ERROR == WSASendTo(m_socket, &wsaBuf, 1, &dwSend, 0,(SOCKADDR*)&addrTo, sizeof(SOCKADDR), NULL, NULL)){MessageBox(_T("发送数据失败!"));return;}
}

 在发送数据时,首先要根据用户输入的主机名获取IP地址,这是通过gethostbyname实现的;然后通过WSASendTo将数据发送出去。

2.6 终止套接字库的使用

 在APP类析构时,应该终止对套接字库的使用。

CChatAsyncApp::~CChatAsyncApp()
{WSACleanup();
}

 在对话框类析构时,应当关闭套接字。

CChatAsyncDlg::~CChatAsyncDlg()
{if (m_socket)closesocket(m_socket);	//关闭套接字
}

 以上为本程序的代码实现,本程序是在同一线程中实现了接收和发送端,如果采用阻塞套接字,可能会因为WSARecvFrom函数的阻塞调用而导致线程暂停运行。在编程网络应用程序时,采用异步选择机制可以提高网络应用程序的性能,如果在配合多线程技术,将大大提高所编写的网络应用程序的性能。

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

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

相关文章

麒麟v10安装Redis(ARM架构)

下载Redis安装包 华为开源镜像站_软件开发服务_华为云 上面的选择一个下载 或者用命令下载 wget https://repo.huaweicloud.com/kunpeng/yum/el/7/aarch64/Packages/bigdata/redis-5.0.5-1.el7.aarch64.rpm 检查是否已经安装Redis rpm -qa | grep redis将包卸载掉 rpm -e -…

探索数据库管理的利器 - PHPMyAdmin

有一个项目,后端由博主独自负责,最近需要将项目交接给另一位同事。在项目初期,博主直接在数据库中使用工具创建了相关表格,并在完成后利用PhpMyAdmin生成了一份数据字典,供团队使用。然而,在随后的开发过程…

Linux与shell命令行学习

文章目录 走进shell基本的bash shell命令2.1 遍历目录 cd2.2 查看文件和目录列表 ls2.3 创建文件 touch2.4 复制文件 cp2.5 自动补全 tab2.6 链接文件 ln2.7 文件重命名 mv2.8 删除文件 rm2.9 创建目录 mkdir2.10 删除目录 rmdir2.11 查看文件类型 file2.12 查看整个文件 cat、…

常用的管理方法论分享

在多年的软件研发团队管理过程中,我积累了一些方法,跟大家分享。 软件研发团队非常注重成本和效率,大多数管理者是从一线开发者升上去的。往往非常善于解决技术问题,但不知道如何管理下属,如何对待上级,如…

【深度学习】 Python 和 NumPy 系列教程(廿四):Matplotlib详解:2、3d绘图类型(10)3D箱线图(3D Box Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 3D线框图(3D Line Plot) 2. 3D散点图(3D Scatter Plot) 3. 3D条形图(3D Bar Plot) 4. 3D曲面图…

mysql的索引结构

索引概述 索引( index )是帮助 MySQL 高效获取数据的数据结构 ( 有序 ) 。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些…

学会SpringMVC之自定义注解各种场景应用,提高开发效率及代码质量

目录 一、简介 ( 1 ) 是什么 ( 2 ) 分类 ( 3 ) 作用 二、自定义注解 ( 1 ) 如何自定义注解 ( 2 ) 场景演示 场景一(获取类与方法上的注解值) 场景二( 获取类属性上的注解属性值 ) 场景三( 获取参数修…

访问者模式

图片转载自 #include<iostream> using namespace std; #include<list> /*模板工厂单例化&#xff0c;所有的商品被注册进工厂中*/ /*访问者模式&#xff08;行为型模式&#xff09; 访问者&#xff0c;被访问者 visit accept 让访问变成一种操作&#xff0c;不同…

【论文解读】元学习:MAML

一、简介 元学习的目标是在各种学习任务上训练模型&#xff0c;这样它就可以只使用少量的训练样本来解决新任务。 论文所提出的算法训练获取较优模型的参数&#xff0c;使其易于微调&#xff0c;从而实现快速自适应。该算法与任何用梯度下降训练的模型兼容&#xff0c;适用于…

前端JavaScript深拷贝与浅拷贝

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 引言 1. 深拷贝的实现 1.1 基本类型和特殊类型的处理 1.2 处理循环引用 1.3 性能优化 1.4 完整的深拷贝实现示…

[qt]vs2022+qt5.13.2代码报错QChartView不明确

报错类似下面&#xff1a; 鼠标指上去错误代码显示QChartView不明确,解决方法 在xxx.ui对应的头文件包含”ui_xxx.h“的前方添加如下代码&#xff1a; #include <qchart.h> QT_CHARTS_USE_NAMESPACE

赋能3D智慧校园!老子云数字孪生可视化,学校运维高效之选!

老子云专注于3D领域&#xff0c;自主研发3D可视化底层&#xff0c;已打造了行业智慧园区、智慧交通、智慧机房、智慧水利等标杆案例&#xff0c;构建了可视化数字孪生智慧体系&#xff0c;其中智慧校园不仅实现了技术上的三维落地&#xff0c;更是成为了管控超百万师生校园安全…

SwiftUI 中的几种毛玻璃效果

Preview Code // // testtt.swift // bill2 // // Created by 朱洪苇 on 2023/8/9. //import SwiftUIstruct testtt: View {var body: some View {ZStack {Image("bg1").blur(radius: 5) // 给背景图加模糊VStack {Text("ultraThinMaterial").padding()…

【Linux从入门到精通】线程 | 线程介绍线程控制

本篇文章主要对线程的概念和线程的控制进行了讲解。其中我们再次对进程概念理解。同时对比了进程和线程的区别。希望本篇文章会对你有所帮助。 文章目录 一、线程概念 1、1 什么是线程 1、2 再次理解进程概念 1、3 轻量级进程 二、进程控制 2、1 创建线程 pthread_create 2、2…

AI绘画Stable Diffusion原理之扩散模型DDPM

前言 传送门&#xff1a; stable diffusion&#xff1a;Git&#xff5c;论文 stable-diffusion-webui&#xff1a;Git Google Colab Notebook部署stable-diffusion-webui&#xff1a;Git kaggle Notebook部署stable-diffusion-webui&#xff1a;Git AI绘画&#xff0c;输入一段…

SpringMVC文件的上传下载JRebel的使用

目录 前言 一、JRebel的使用 1.IDea内安装插件 2.激活 3.离线使用 使用JRebel的优势 二、文件上传与下载 1 .导入pom依赖 2.配置文件上传解析器 3.数据表 4.配置文件 5.前端jsp页面 6.controller层 7.测试结果 前言 当涉及到Web应用程序的开发时&…

Android窗口层级(Window Type)分析

前言 Android的窗口Window分为三种类型&#xff1a; 应用Window&#xff0c;比如Activity、Dialog&#xff1b;子Window&#xff0c;比如PopupWindow&#xff1b;系统Window&#xff0c;比如Toast、系统状态栏、导航栏等等。 应用Window的Z-Ordered最低&#xff0c;就是在系…

uni-app 使用uCharts-进行图表展示(折线图带单位)

前言 在uni-app经常是需要进行数据展示&#xff0c;针对这个情况也是有人开发好了第三方包&#xff0c;来兼容不同平台展示 uCharts和pc端的Echarts使用差不多&#xff0c;甚至会感觉在uni-app使用uCharts更轻便&#xff0c;更舒服 但是这个第三方包有优点就会有缺点&#xf…

医院安全不良事件报告系统源码 PHP+ vue2+element+ laravel8+ mysql5.7+ vscode开发

不良事件上报系统通过 “事前的人员知识培训管理和制度落地促进”、“事中的事件上报和跟进处理”、 以及 “事后的原因分析和工作持续优化”&#xff0c;结合预存上百套已正在使用的模板&#xff0c;帮助医院从对护理事件、药品事件、医疗器械事件、医院感染事件、输血事件、意…

数字人员工成企业得力助手,虚拟数字人为企业注入高科技基因

随着互联网和人工智能技术的快速发展&#xff0c;以“数字员工”为代表的数字生产力&#xff0c;正在出现在各行各业的业务场景中。数字人员工的出现不是替代人类&#xff0c;而是通过技术提高工作效率&#xff0c;实现更加智能化的服务体验&#xff0c;帮助企业实现大规模自动…