网络编程 重叠IO模型

news2024/12/23 17:02:32

目录

1.概念

2.代码详解

        事件通知实现逻辑​

        1.WSASocket函数

        2.AcceptEx函数

        3.WSARecv函数

        4.WSAGetOverlappedTesult函数

        5.WSAResetEvent函数

        6.WSASend函数

##重叠IO模型事件通知整体代码

        完成例程实现逻辑​编辑

##重叠IO模型完成例程的整体代码


1.概念

        重叠IO模型是对C/S模型的直接优化,使用到的函数和概念如下图:

        重叠IO的流程图

 

2.代码详解

        事件通知实现逻辑

        重叠IO模型中的代码与select模型,事件选择模型以及异步选择模型差别很大,使用的函数也不一样。

        1.WSASocket函数

该函数创建绑定到特定传输服务提供程序的套接字,函数原型

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

前三个参数和socket函数的三个参数是一样的

参数4 lpProtocolInfo:设置套接字详细属性,是一个指向 WSAPROTOCOL_INFO 结构体的指针,一般填写NULL

参数5 g:该参数填写0

参数6 dwFlags:指定套接字属性,填写 WSA_FLAG_OVERLAPPED

        返回值和socket函数一样

##代码样例

	SOCKET socketServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (socketServer == INVALID_SOCKET)
	{
		//创建了无效的socket
		int a = WSAGetLastError();//获取错误码
		printf("创建出错\n");
		WSACleanup();
		return 0;
	}

        2.AcceptEx函数

该函数接受新连接,返回本地和远程地址,并接收客户端应用程序发送的第一个数据块,函数原型

BOOL AcceptEx(
  [in]  SOCKET       sListenSocket,
  [in]  SOCKET       sAcceptSocket,
  [in]  PVOID        lpOutputBuffer,
  [in]  DWORD        dwReceiveDataLength,
  [in]  DWORD        dwLocalAddressLength,
  [in]  DWORD        dwRemoteAddressLength,
  [out] LPDWORD      lpdwBytesReceived,
  [in]  LPOVERLAPPED lpOverlapped
);

参数1:填服务器socket

参数2:需要手动创建一个socket,并且填入,函数会把客户端发来的IP地址和端口号绑定在该socket上

参数3:填字符串数组,会接收到新连接上发来的第一个数据

参数4:一般设置成0,表示取消参数3的功能

参数5:填写字节长度,要比本地的最大传输协议地址长度大至少16个字节,即sizeof(struct sockaddr_in)+16

参数6:填写字节长度,要比远程的最大传输协议地址长度大至少16个字节,即sizeof(struct sockaddr_in)+16

参数7:接收新连接第一次发来的数据,配合参数3和4使用,填DWORD变量的地址

参数8:填重叠IO结构,填的是服务器对应的IO事件

        返回值:如果返回TRUE,表示立即完成即同步,如果返回FALSE,错误码为 ERROR_IO_PENDING 表示异步等待,否则出错。

##代码样例

g_allsock[g_count] = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);//手动创建一个socket,用来绑定客户端的ip和端口号
	g_allOlp[g_count].hEvent = WSACreateEvent();//绑定事件
	char str[1024] = { 0 };
	DWORD dwRecvcount;
	//返回值 返回TRUE表示立即完成,返回FALES,如果错误码是ERROR_IO_PENDING表示异步等待,否则出错
	BOOL bRes = AcceptEx(g_allsock[0], g_allsock[g_count], str, 0, sizeof(struct sockaddr_in) + 16, 
		sizeof(struct sockaddr_in) + 16, &dwRecvcount, g_allOlp[g_count]);//接收连接 

        3.WSARecv函数

该函数投递异步接收消息,函数原型

int WSAAPI WSARecv(
  [in]      SOCKET                             s,
  [in, out] LPWSABUF                           lpBuffers,
  [in]      DWORD                              dwBufferCount,
  [out]     LPDWORD                            lpNumberOfBytesRecvd,
  [in, out] LPDWORD                            lpFlags,
  [in]      LPWSAOVERLAPPED                    lpOverlapped,
  [in]      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

参数1:客户端socket

参数2:是WSABUF类型的结构体对象,把接收到的消息存放在结构体buf中

参数3:是WSABUF对象的个数

参数5:接收消息成功,会把字节数返回到该参数

参数5:用于修改WSARecv函数调用行为的标志的指针

参数6:重叠IO结构

参数7:回调函数,在这里填NULL

        返回值:立即发生,会返回0,否则发生错误,如果错误码是 ERROR_IO_PENDING ,表示延迟处理。

##代码样例

int PostRecv(int index)
{
	WSABUF wsabuf;//参数2
	wsabuf.buf = g_strRecv;
	wsabuf.len = MAX_RECV_COUNT;

	DWORD dwRecvCount;//参数4
	DWORD dwFlag = 0;//参数5

	int wRes = WSARecv(g_allsock[index], &wsabuf, 1, &dwRecvCount, &dwFlag, &g_allOlp[index], NULL);
	if (wRes == 0)
	{
		//立即完成
		//输出信息
		printf("%s\n", wsabuf.buf);
		memset(g_strRecv, 0, MAX_RECV_COUNT);//把数组重新置0
		//根据情况投递send

		//继续对自己投递接收
		PostRecv(index);

		return 0;
	}
	else
	{
		int a = WSAGetLastError();
		if (ERROR_IO_PENDING == a)
		{
			//延迟处理
		}
		else
		{
			//出错
		}
	}
}

        4.WSAGetOverlappedTesult函数

该函数检索指定套接字上重叠操作的结果,即获取对应socket上的具体情况,函数原型

BOOL WSAAPI WSAGetOverlappedResult(
  [in]  SOCKET          s,
  [in]  LPWSAOVERLAPPED lpOverlapped,
  [out] LPDWORD         lpcbTransfer,
  [in]  BOOL            fWait,
  [out] LPDWORD         lpdwFlags
);

参数1:有发生信号的客户端socket

参数2:socket所对应的重叠结构

参数3:获取到发生或者接收到的实际字节数,返回0表示下线

参数4:填TRUE

参数5:装填WSARecv函数的参数5

        返回值:成功返回TRUE,失败返回FALSE

##代码样例

WORD dwState;
WORD dwFlag;
BOOL bFlag = WSAGetOverlappedResult(g_allsock[i], &g_allOlp[i], &dwState, TRUE, &dwFlag);

        5.WSAResetEvent函数

该函数重置指定事件对象的状态为无信号。函数原型

BOOL WSAAPI WSAResetEvent(
  [in] WSAEVENT hEvent
);

参数传递需要重置的事件

        返回值:成功返回TRUE,失败返回FALSE

        6.WSASend函数

该函数在连接的套接字上发送数据,函数原型

int WSAAPI WSASend(
  [in]  SOCKET                             s,
  [in]  LPWSABUF                           lpBuffers,
  [in]  DWORD                              dwBufferCount,
  [out] LPDWORD                            lpNumberOfBytesSent,
  [in]  DWORD                              dwFlags,
  [in]  LPWSAOVERLAPPED                    lpOverlapped,
  [in]  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

参数和WSARecv的参数是一样的,在这里参数5就不需要取地址

返回值也是一样的

##代码样例

int Postsend(int index)
{
	WSABUF wsabuf;//参数2
	wsabuf.buf = "你好";
	wsabuf.len = MAX_RECV_COUNT;

	DWORD dwSendCount;//参数4
	DWORD dwFlag = 0;//参数5
	int wRes = WSASend(g_allsock[index], &wsabuf, 1, &dwSendCount, dwFlag, &g_allOlp[index], NULL);
	if (wRes == 0)
	{
		//立即完成
		printf("send succee\n");
		return 0;
	}
	else
	{
		int a = WSAGetLastError();
		if (ERROR_IO_PENDING == a)
		{
			//延迟处理
			return 0;
		}
		else
		{
			//出错
			return 0;
		}
	}
}

##重叠IO模型事件通知整体代码

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include<stdio.h>
#include<Winsock2.h>
#include<mswsock.h>//需要放在Winsock2.h下面
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "Mswsock.lib")

#define MAX_COUNT 1024
#define MAX_RECV_COUNT 1024
SOCKET g_allsock[MAX_COUNT];//socket数组
OVERLAPPED g_allOlp[MAX_COUNT];//事件数组
int g_count;
char g_strRecv[MAX_RECV_COUNT];

int PostAccept();//接收连接的函数
int PostRecv(int index);//接收消息
int Postsend(int index);//发送消息

//清理函数
void Clear()
{
	for (int i = 0; i < g_count; i++)
	{
		closesocket(g_allsock[i]);
		WSACloseEvent(g_allOlp[i].hEvent);
	}
}
BOOL WINAPI fun(DWORD dwCtrlType)
{
	switch (dwCtrlType)
	{
	case CTRL_CLOSE_EVENT:
		//释放所有soket和事件
		Clear();
		break;
	}
	return TRUE;
}

int main()
{
	SetConsoleCtrlHandler(fun, TRUE);
	//第一步 打开网络库并校验版本
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA wdsockMsg;
	int nRes = WSAStartup(wdVersion, &wdsockMsg);
	if (nRes != 0)
	{
		printf("打开网络库失败\n");
		return 0;
	}
	if (2 != HIBYTE(wdsockMsg.wVersion) || 2 != LOBYTE(wdsockMsg.wVersion))
	{
		printf("版本不对\n");
		WSACleanup();
		return 0;
	}
	//第二步 创建socket
	SOCKET socketServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (socketServer == INVALID_SOCKET)
	{
		//创建了无效的socket
		int a = WSAGetLastError();//获取错误码
		printf("创建出错\n");
		WSACleanup();
		return 0;
	}
	//第三步 绑定ip地址和端口号
	struct sockaddr_in si;
	si.sin_family = AF_INET;
	si.sin_port = htons(12332);
	si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	bind(socketServer, (const struct sockaddr*)&si, sizeof(si));
	//第四步 开始监听
	if (SOCKET_ERROR == listen(socketServer, SOCK_STREAM))
	{
		printf("监听失败\n");
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

	//服务器socket绑定事件,并且放入到数组中
	g_allsock[g_count] = socketServer;
	g_allOlp[g_count].hEvent = WSACreateEvent();
	g_count++;

	if (0 != PostAccept())
	{
		Clear();
		WSACleanup();
		return 0;
	}

	while (1)
	{
		for (int i = 0; i < g_count; i++)
		{
			int wRes = WSAWaitForMultipleEvents(1, &(g_allOlp[i].hEvent), FALSE, 0, FALSE);//获取第i个事件的信号
			if (wRes == WSA_WAIT_FAILED || wRes == WSA_WAIT_TIMEOUT)//出错或者超时
			{

				continue;
			}
			//有信号
			WORD dwState;
			WORD dwFlag;
			BOOL bFlag = WSAGetOverlappedResult(g_allsock[i], &g_allOlp[i], &dwState, TRUE, &dwFlag);//获取socket上的对应情况
			WSAResetEvent(g_allOlp[i].hEvent);//把信号置空

			if (bFlag == FALSE)//出错
			{
				int a = WSAGetLastError();
				if (a == 10054)
				{
					printf("force close\n");
					//关闭
					closesocket(g_allsock[i]);
					WSACloseEvent(g_allOlp[i].hEvent);
					//从数组中删掉
					g_allsock[i] = g_allsock[g_count - 1];
					g_allOlp[i] = g_allOlp[g_count - 1];
					//循环控制变量-1
					i--;
					//下标减一
					g_count--;
				}
				continue;
			}
			if (0 == i)
			{
				Postsend(g_count);
				printf("accept\n");
				//完成连接
				//投递recv
				PostRecv(g_count);
				//根据情况投递send
				//客户端适量++
				g_count++;
				//投递accept
				PostAccept();
				continue;
			}
			if (0 == dwState)
			{
				//客户端下线
				printf("close\n");
				//关闭
				closesocket(g_allsock[i]);
				WSACloseEvent(g_allOlp[i].hEvent);
				//从数组中删掉
				g_allsock[i] = g_allsock[g_count - 1];
				g_allOlp[i] = g_allOlp[g_count - 1];
				//循环控制变量-1
				i--;
				//下标减一
				g_count--;
				continue;
			}
			if (0 != dwState)
			{
				//表示发送或者接收成功
				if (g_strRecv[0] != 0)//判断第一个元素不为空表示接收到消息
				{
					//输出信息
					printf("%s\n", g_strRecv);
					memset(g_strRecv, 0, MAX_RECV_COUNT);//把数组重新置0
					//继续对自己投递接收
					PostRecv(i);
				}
			}
		}
	}

	closesocket(socketServer);
	Clear();
	WSACleanup();

	return 0;
}

int PostAccept()
{
	//手动创建一个socket,用来绑定客户端的ip和端口号
	g_allsock[g_count] = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	g_allOlp[g_count].hEvent = WSACreateEvent();//绑定事件
	char str[1024] = { 0 };
	DWORD dwRecvcount;
	//返回值 返回TRUE表示立即完成,返回FALES,如果错误码是ERROR_IO_PENDING表示异步等待,否则出错
	BOOL bRes = AcceptEx(g_allsock[0], g_allsock[g_count], str, 0, sizeof(struct sockaddr_in) + 16, 
		sizeof(struct sockaddr_in) + 16, &dwRecvcount, &g_allOlp[0]);//接收连接 
	if (bRes == TRUE)
	{
		//立即完成
		//投递recv
		PostRecv(g_count);
		//根据情况投递send
		//客户端适量++
		g_count++;
		//投递accept
		PostAccept();//递归完成

		return 0;
	}
	else
	{
		int a = WSAGetLastError();
		if (a == ERROR_IO_PENDING)
		{
			//异步等待
			return 0;
		}
		else
		{
			//出错
			return 0;
		}
	}
}

int PostRecv(int index)
{
	WSABUF wsabuf;//参数2
	wsabuf.buf = g_strRecv;
	wsabuf.len = MAX_RECV_COUNT;

	DWORD dwRecvCount;//参数4
	DWORD dwFlag = 0;//参数5
	int wRes = WSARecv(g_allsock[index], &wsabuf, 1, &dwRecvCount, &dwFlag, &g_allOlp[index], NULL);
	if (wRes == 0)
	{
		//立即完成
		//输出信息
		printf("%s\n", wsabuf.buf);
		memset(g_strRecv, 0, MAX_RECV_COUNT);//把数组重新置0
		//根据情况投递send

		//继续对自己投递接收
		PostRecv(index);

		return 0;
	}
	else
	{
		int a = WSAGetLastError();
		if (ERROR_IO_PENDING == a)
		{
			//延迟处理
			return 0;
		}
		else
		{
			//出错
			return 0;
		}
	}
}

int Postsend(int index)
{
	WSABUF wsabuf;//参数2
	wsabuf.buf = "你好";
	wsabuf.len = MAX_RECV_COUNT;

	DWORD dwSendCount;//参数4
	DWORD dwFlag = 0;//参数5
	int wRes = WSASend(g_allsock[index], &wsabuf, 1, &dwSendCount, dwFlag, &g_allOlp[index], NULL);
	if (wRes == 0)
	{
		//立即完成
		printf("send succee\n");
		return 0;
	}
	else
	{
		int a = WSAGetLastError();
		if (ERROR_IO_PENDING == a)
		{
			//延迟处理
			return 0;
		}
		else
		{
			//出错
			return 0;
		}
	}
}

        完成例程实现逻辑

在这个模型中的WSAWaitFormultipleEvents函数中的参数6需要改为TRUE,意义:将等待事件函数与完成例程有机制结合在一起,实现等待事件函数与完成例程函数的异步执行,执行完并给等待事件函数信号,即 WSAWaitFormultipleEvents函数不仅能获取事件的信号通知,还能获取完成例程的执行通知。函数WSARecv和函数WSASend都需要使用回调函数

        回调函数需要使用系统给定的函数,函数原型

void
(CALLBACK * LPWSAOVERLAPPED_COMPLETION_ROUTINE)(
    IN DWORD dwError,
    IN DWORD cbTransferred,
    IN LPWSAOVERLAPPED lpOverlapped,
    IN DWORD dwFlags
    );

参数1:获取到客户端socke的错误码

参数2:获取到客户端的发收字节数,为0表示客户端退出

参数3:获取到重叠IO的地址

参数4:表示函数执行的方式

 

##重叠IO模型完成例程的整体代码

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<WinSock2.h>
#include<mswsock.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "Mswsock.lib")

#define MAX_COUNT 1024
#define MAX_RECV_COUNT 1024

SOCKET g_allsock[MAX_COUNT];
OVERLAPPED g_allIOp[MAX_COUNT];
int g_count;
char g_recvbuf[MAX_RECV_COUNT];//用来接收信息

int PostAccept();//投递连接
int PostRecv(int index);//投递接收信息
int PostSend(int index);

void Clear()
{
	for (int i = 0; i < g_count; i++)
	{
		closesocket(g_allsock[i]);
		WSACloseEvent(g_allIOp[i].hEvent);
	}
}

int main()
{
	//第一步 打开网络库并校验版本
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA wdSockMsg;
	int nRes = WSAStartup(wdVersion, &wdSockMsg);
	if (nRes != 0)
	{
		printf("打开网络库失败\n");
		return 0;
	}
	if (HIBYTE(wdSockMsg.wVersion) != 2 || LOBYTE(wdSockMsg.wVersion) != 2)
	{
		printf("版本不对\n");
		WSACleanup();
		return 0;
	}
	//第二步 创建socket
	SOCKET socketServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (socketServer == INVALID_SOCKET)
	{
		printf("创建socket失败\n");
		WSACleanup();
		return 0;
	}
	//第三步 绑定ip地址和端口号
	struct sockaddr_in si;
	si.sin_family = AF_INET;
	si.sin_port = htons(12332);
	si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*)&si, sizeof(si)))
	{
		printf("绑定出错\n");
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}
	//第四步 开始监听
	if (SOCKET_ERROR == listen(socketServer, SOCK_STREAM))
	{
		printf("监听失败\n");
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}
	//第五步 重叠IO
	//把socket和对应事件放入到数组中
	g_allsock[g_count] = socketServer;
	g_allIOp[g_count].hEvent = WSACreateEvent();
	g_count++;

	//投递连接
	if (0 != PostAccept())
	{
		Clear();
		WSACleanup();
		return 0;
	}
	//循环处理每一个socket
	while (1)
	{

		//接收消息
		int wRes = WSAWaitForMultipleEvents(1, &(g_allIOp[0].hEvent), FALSE, WSA_INFINITE, TRUE);//获取信号
		if (wRes == WSA_WAIT_FAILED || wRes == WSA_WAIT_IO_COMPLETION)
		{
			//获取信号错误
			continue;
		}
		WSAResetEvent(g_allIOp[0].hEvent);//把信号置空

		//PostSend(g_count);
		//完成连接
		printf("accept\n");
		//投递Recv
		PostRecv(g_count);
		//数量增加
		g_count++;
		//继续投递
		PostAccept();
	}
	Clear();
	WSACleanup();

	return 0;
}

int PostAccept()
{
	while (1)
	{
		//手动创建socket
		g_allsock[g_count] = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
		g_allIOp[g_count].hEvent = WSACreateEvent();//绑定事件
		if (g_allsock[g_count] == SOCKET_ERROR)//创建失败
		{
			return 0;
		}
		char str[1024] = { 0 };
		DWORD dwRecvcount;
		BOOL aRes = AcceptEx(g_allsock[0], g_allsock[g_count], str, 0, sizeof(struct sockaddr) + 16,
			sizeof(struct sockaddr) + 16, &dwRecvcount, &g_allIOp[0]);
		if (aRes == TRUE)//表示立即完成
		{
			//接收信息
			//投递RECV
			PostRecv(g_count);
			//数量增加
			g_count++;
			//循环该客户端
			//PostAccept();
			continue;
		}
		else
		{
			int a = WSAGetLastError();
			if (a == ERROR_IO_PENDING)
			{
				//异步等待
				break;
			}
			else
			{
				//出错
				break;
			}
		}
	}
	return 0;
}

void CALLBACK RecvCall(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
	int i = (int)(lpOverlapped - &g_allIOp[0]);//地址相减拿到下标
	if (dwError == 10054 || cbTransferred == 0)//客户端退出
	{
		//关闭
		printf("close\n");
		closesocket(g_allsock[i]);
		WSACloseEvent(g_allIOp[i].hEvent);
		g_allsock[i] = g_allsock[g_count - 1];
		g_allIOp[i] = g_allIOp[g_count - 1];
		g_count--;
	}
	else
	{
		printf("%s\n", g_recvbuf);
		memset(g_recvbuf, 0, MAX_RECV_COUNT);
		PostRecv(i);
	}
}

int PostRecv(int index)
{
	WSABUF wsabuf;
	wsabuf.buf = g_recvbuf;
	wsabuf.len = MAX_RECV_COUNT;
	DWORD dwRecvCount;
	DWORD dwFlag = 0;
	int wRes = WSARecv(g_allsock[index], &wsabuf, 1, &dwRecvCount, &dwFlag, &g_allIOp[index], RecvCall);
	if (wRes == 0)
	{
		//立即完成
		printf("%s\n", wsabuf.buf);
		//把内存置为0
		memset(g_recvbuf, 0, MAX_RECV_COUNT);
		//继续投递
		PostRecv(index);
		return 0;
	}
	else
	{
		int a = WSAGetLastError();
		if (a == WSA_IO_PENDING)
		{
			//已成功启动重叠的操作,延迟完成
			return 0;
		}
		else
		{
			//出现错误
			return 0;
		}
	}
}

void CALLBACK SendCall(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
	printf("send succee\n");
}

int PostSend(int index)
{
	WSABUF wsabuf;
	wsabuf.buf = "你好";
	wsabuf.len = MAX_RECV_COUNT;

	DWORD dwSendcount;
	DWORD dFlag = 0;
	int wRes = WSASend(g_allsock[index], &wsabuf, 1, &dwSendcount, dFlag, &g_allIOp[index], SendCall);
	if (wRes == 0)
	{
		//立即完成发送
		printf("send succee\n");
		return 0;
	}
	else
	{
		int a = WSAGetLastError();
		if (a == ERROR_IO_PENDING)
		{
			//延迟处理,异步等待
			return 0;
		}
		else
		{
			//出现错误
			return 0;
		}
	}
}

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

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

相关文章

微信小程序集成three.js--1.创建各种光源的场景

1.实例演示 微信小程序集成Three.js&#xff0c;各种光源效果演示2.源码 &#xff08;1&#xff09;引入three.js库文件 import * as THREE from ../../libs/three.weapp.js import {OrbitControls } from ../../jsm/controls/OrbitControls const app getApp() 库文件下载…

第七章面向对象编程

第七章面向对象编程 7.1对象在内存中存在形式 7.1.1属性/成员变量/字段&#xff08;field) 1.属性成员变量字段field&#xff0c;概念上相等 public class Object02 {//编写一个 main 方法public static void main(String[] args) {}} class Car {String name;//属性, 成员变…

数据防泄露之图文档及业务数据经验分享

场景描述 信息化时代发展迅速&#xff0c;数据防泄露一词也频繁的出现在我们身边。无论企业或政府单位&#xff0c;无纸化办公场景越来越多&#xff0c;数据泄露的时间也层出不穷。例如&#xff1a;世界最大职业中介网站Monster遭到黑客大规模攻击&#xff0c;黑客窃取在网站注…

跟着pink老师学JS的第三天总结

* 这个仿京东的商品放大镜效果真不好做&#xff01; 鼠标拖拽&#xff1a; * 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"…

FineReport报表设计工具- 配置DB2外接数据库(1)

1. 概述 1.1 版本 报表服务器版本 功能变更 11.0 - 11.0.3 1&#xff09;首次配置外接数据库时&#xff0c;支持自行选择是否「迁移数据至要启用的数据库」 2&#xff09;迁移外接数据库的过程提示细化&#xff0c;方便用户了解迁移进度 1.2 功能简介 报表系统配置外接数…

Seata使用教程

文章目录一、Seata简介1.Seata 概念介绍2.分布式事务3.Seata核心组件4.Seata 工作流程5.Seata四大模式二、Seata实战教程1.下载资源2.配置Seata-Server3.增加相关表结构4.代码配置三、常见报错解决一、Seata简介 1.Seata 概念介绍 Seata 是一款阿里巴巴开源的分布式事务解决方…

eNSP 设备启动失败,错误代码:40 解决方案

eNSP 路由器启动失败&#xff0c;错误代码&#xff1a;40 解决方案 eNSP 路由器启动失败&#xff0c;错误代码&#xff1a;40 解决方案 文章目录eNSP 路由器启动失败&#xff0c;错误代码&#xff1a;40 解决方案一、出现错误代码&#xff1a;40二、解决方法1.确定相关的软件安…

《设计模式》外观模式

《设计模式》外观模式《设计模式》设计模式的基本原则 《设计模式》单例模式 《设计模式》工厂模式 《设计模式》原型模式 《设计模式》建造者模式 《设计模式》适配器模式 《设计模式》桥接模式 《设计模式》装饰者模式 《设计模式》组合模式 《设计模式》外观模式 定义&#…

免费刷题!初级软件测试面试题目和答案这个小程序很全

有没有软件测试面试题库小程序&#xff1f;相信这是很多准备找工作的新手测试人都想要知道的吧&#xff01; 今天&#xff0c;我就根据大家的需求&#xff0c;为大家整理了一些有关初级软件测试的面试题目以及一个可以免费刷题的题库&#xff0c;希望能帮助你们早日拿下心仪的…

UDS-10 Diagnostic and communication management functional unit

10 诊断与通信管理功能单元 来自&#xff1a;ISO 14229-1-2020.pdf 10.1概述 表22指定了诊断和通信管理功能单元。 注&#xff1a; DiagnosticSessionControl&#xff1a;客户端请求控制服务器的诊断会话。ECUReset&#xff1a;客户端强制服务器执行重置。SecurityAccess&am…

知识蒸馏原理

文章目录0.知识蒸馏&#xff08;模型压缩的一种方法&#xff09;1.蒸馏2.为什么要蒸馏3.知识的表示与迁移4.蒸馏温度T5.知识蒸馏过程6.知识蒸馏的应用场景7.知识蒸馏背后的机理8.为什么用soft targets 而不用 label smoothing?9.知识蒸馏的研究方向10.知识蒸馏代码库11.扩展阅…

回顾2022,展望2023,笔耕不辍,钟情翰墨

目录 回顾2022 博客概览 博客成就 获得测试领域优质创作者认证 获得博客专家认证 获得额外收入 创建第一个属于自己的个人社区 获得第一个实体奖牌【博客专家】 首次登榜梦想照进现实CSDN实体奖牌榜 首次参与社区新锐和社区先锋评选 开启了6个知识体系系列教程 个人…

2023春招面试:消息中间件面试题整理

RabbitMQ如何确保消息发送 &#xff1f; 消息接收&#xff1f; 开启生产者确认机制&#xff0c;确保生产者的消息能到达队列&#xff08;config机制保证消息正确到达交换机、return机制保证消息正确到达队列&#xff09;开启持久化功能&#xff0c;确保消息未消费前在队列中不会…

如何通过WindowsIIS部署网站

1.winR输入control 打开【控制面板】 2.选择程序 3.选择【启用或关闭Windows功能】 4.在【Windows功能】对话框中勾选【Internet Information Services】下的【FTP服务器】、【Web管理工具】和【万维网服务】中的所有选项&#xff0c;并点击【确定】 5.Windows功能会开始下载并…

Lambda表达式的来龙去脉,全在这篇文章里了

一. 前言部分 大家都知道Lambda表达式作为JAVA 8中提供的新特性之一&#xff0c;在现在的企业开发中已经非常的流行了。今天壹哥就通过一个具体的案例&#xff0c;来带大家一起详细地探究一下Lambda表达式是如何被提出来的&#xff0c;以及它的出现主要是用来解决什么问题的。…

乌班图(Ubuntu)单系统或者乌班图+Win双系统安装教程

单ubuntu系统安装 1、将ubuntu系统U盘插入电脑USB接口&#xff0c;建议优先插USB3.0蓝色(彩色)接口&#xff0c;这样可以保证安装过程中文件的读取速度&#xff0c;加快安装进程。 2、然后电脑关机状态下&#xff0c;开机。开机后快速按主机的快捷启动键&#xff1a; 3、在出现…

Qt编写雷达模拟仿真工具1-背景布局

一、前言 雷达模拟仿真工具&#xff0c;整体结构采用的QGraphicsView框架&#xff0c;背景布局采用的分层绘制&#xff0c;这样可以控制该需要重新绘制的重新绘制&#xff0c;不需要重新的绘制的就没必要再多余的浪费&#xff0c;这里定义了一个GraphicsBackGroundItem类继承自…

Spring框架(容器)--简介(实现原理、核心模块、组成部分)

spring框架&#xff08;容器&#xff09; spring简介 1、Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言&#xff0c;任何Java应用都可以从Spring中受益。 2、Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。 3、轻量——从大小…

Creo9.0 Windows 3D建模工具

前言 creo9.0正式版是一款非常优秀的3D建模设计软件。该软件界面美观&#xff0c;提供了CAD 技术、模制造绘图、多实体建模、多体设计、实时仿真、框架和焊缝设计等一系列强大的辅助设计功能&#xff0c;通过这些功能&#xff0c;让用户轻松解决设计中带来的各种问题。 下载 …

律所管理系统的功能以及作用分别有哪些?

在全球进入信息化的时代&#xff0c;随着网络的普及与发展&#xff0c;网络所带来的信息交流与利用的优势愈发明显。尤其是随着法律制度的不断健全和人民法律意识的提高&#xff0c;涉及法律诉讼的案件也在不断地增加&#xff0c;律师事务所作为中介的法律机构&#xff0c;要处…