网络编程六--UDP服务器客户端

2023-10-30

写在前面

UDP(User Datagram Protocol)称为用户数据报协议,是一种无连接的传输协议。

UDP的主要应用在即使丢失部分数据,也不影响整体效果的场景。例实时传输视频或音频时,即使丢失部分数据,也不会影响整体效果,只是会有轻微的画面抖动或杂音。

UDP中的服务器/客户端没有连接

UDP服务器/客户端不像TCP那样,交换数据前需进行connect和accept进行连接。UDP中只有创建套接字和数据交互的过程。

UDP服务器和客户端均只需要一个套接字

在TCP服务器/客户端程序中,套接字是一一对应的关系。服务器若要向10个客户端提供服务,除了监听套接字外,还需要10个对应客户端的服务器套接字。

而在UDP中,不管是服务器还是客户端均只有一个套接字。在服务器端,可以通过服务器端的这个套接字向多个不同的客户端提供服务。同理,在客户端,也可以通过客户端的这个套接字向不同服务器请求服务。

基于UDP的IO函数

创建好TCP套接字后,需要事先通过bind函数绑定IP和端口,并维持和对方的连接。
而UDP没有绑定IP和端口的步骤,因此不会保持连接。那么传输数据时就需要在IO函数中指定要目的地地址。

#include <winsock2.h>
int WSAAPI sendto(SOCKET s, const char *buf,  int len, int flags, const sockaddr *to, int tolen);
//s: 标识 (可能连接的) 套接字的描述符
//buf: 指向包含要传输数据的缓冲区的指针
//len: buf 参数指向的数据长度(以字节为单位)
//flags: 一组标志,用于指定调用的进行方式
//to: 指向包含目标套接字地址的 sockaddr 结构的可选指针
//tolen: 参数指向的地址的大小(以字节为单位)
//返回值:如果未发生错误, sendto 将返回发送的字节总数,这可能小于 len 指示的数字。 否则,将返回SOCKET_ERROR值

int WSAAPI recvfrom( SOCKET s,  char *buf, int len, int flags, sockaddr* from, int* fromlen);
//s: 标识绑定套接字的描述符
//buf: 传入数据的缓冲区
//len: buf 参数指向的缓冲区的长度(以字节为单位)
//flags: 一组选项,用于修改函数调用的行为,超出为关联套接字指定的选项
//from: 指向 sockaddr 结构中缓冲区的可选指针,将在返回时保存源地址, 注意这是一个输出参数
//fromlen: 指向 参数 指向的缓冲区大小(以字节为单位)的可选指针

下面将给出基于UDP的服务器/客户端代码示例。

服务器

// UDP_Server.cpp : 定义控制台应用程序的入口点。
//

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

#define BUF_SIZE 1024

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc != 2)
	{
		return -1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("WSAStartup Error!\n");
		return -1;
	}
	
	//服务器端UDP套接字,第三个参数也可传0
	SOCKET srvSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == srvSock)
	{
		printf("socket Error!\n");
		WSACleanup();
		return -1;
	}

	//服务器端地址信息
	SOCKADDR_IN srvAddr;
	memset(&srvAddr, 0, sizeof(srvAddr));
	srvAddr.sin_family = PF_INET;
	srvAddr.sin_addr.s_addr = htonl(ADDR_ANY);
	srvAddr.sin_port = htons(_ttoi(argv[1]));
	int srvAddrLen = sizeof(srvAddr);

	//因为该示例先使用recvfrom从客户端接收数据,因此这里需先调用bind函数绑定服务器端UDP套接字的地址信息。否则接收到的数据不知道要给哪个应用程序
	//可尝试屏蔽这部分,查看终端是否会打印客户端发来的数据
	if (SOCKET_ERROR == bind(srvSock, (sockaddr*)&srvAddr, sizeof(srvAddr)))
	{
		printf("bind Error!\n");
		closesocket(srvSock);
		WSACleanup();
		return -1;
	}
	
	//这里事先创建并初始化客户端的地址信息变量。
	//之后recvfrom时会添加该变量值
	SOCKADDR_IN cltAddr;
	memset(&cltAddr, 0, sizeof(cltAddr));
	int nCltAddrLen = sizeof(cltAddr);

	char Msg[BUF_SIZE];
	int recvLen = 0;
	while (true)
	{
		printf("Wait Msg From Client...\n");
		//无连接的UDP套接字,sendto和recvfrom必须一一对应,也没有数据边界
		//无连接的UDP套接字不会保持连接状态,因此每次传输数据都需要添加目标地址信息。
		//第五、六个参数用来填充保存客户端的地址信息
		//这里使用一个服务器端UDP套接字srvSock,从多个客户端接收数据
		recvLen = recvfrom(srvSock, Msg, BUF_SIZE - 1, 0, (sockaddr*)&cltAddr, &nCltAddrLen);
		if (recvLen == -1)
		{
			//UDP属于无连接协议,因此没有断开连接的说法,这里不会进来
			printf("Client Disconnected.");
			break;
		}
		Msg[recvLen] = 0;
		printf("Receive Msg from Client: %s\n", Msg);
		
		//第五、六个参数为上面保存的客户端的地址信息
		//这里使用一个服务器端UDP套接字srvSock,回复多个客户端,通过接收数据时填充的地址信息标识多个客户端
		sendto(srvSock, Msg, recvLen, 0, (sockaddr*)&cltAddr, nCltAddrLen);
	}
	closesocket(srvSock);
	WSACleanup();

	return 0;
}

客户端

// UDP_Client.cpp : 定义控制台应用程序的入口点。
//

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

#define BUF_SIZE 1024

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc != 3)
	{
		return -1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("WSAStartup Error!\n");
		return -1;
	}

	//客户端UDP套接字
	SOCKET cltSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == cltSock)
	{
		printf("socket Error!\n");
		WSACleanup();
		return -1;
	}

	//服务器端地址信息
	SOCKADDR_IN srvAddr;
	memset(&srvAddr, 0, sizeof(srvAddr));
	srvAddr.sin_family = PF_INET;
	srvAddr.sin_addr.s_addr = inet_addr(argv[1]);
	srvAddr.sin_port = htons(_ttoi(argv[2]));
	int nSrvAddrLen = sizeof(srvAddr);

	char Msg[BUF_SIZE];
	int recvLen = 0;
	while (true)
	{
		fputs("Input Msg(q to quit): ", stdout);
		fgets(Msg, sizeof(Msg), stdin);
		
		if (!strcmp(Msg, "q\n") || !strcmp(Msg, "Q\n"))
		{
			printf("Disconnected...");
			break;
		}
		
		//这里向地址信息为srvAddr传输数据
		//这里客户端UDP套接字cltSock没有事先绑定IP和端口,因此每次调用sendto时都会自动分配IP和端口
		sendto(cltSock, Msg, strlen(Msg), 0, (sockaddr*)&srvAddr, sizeof(srvAddr));

		recvLen = recvfrom(cltSock, Msg, BUF_SIZE - 1, 0, (sockaddr*)&srvAddr, &nSrvAddrLen);
		Msg[recvLen] = 0;

		printf("Msg From Server: %s\n", Msg);
	}

	closesocket(srvSock);
	WSACleanup();

	return 0;
}

UDP客户端套接字地址分配

还记得此前的TCP客户端代码,通过connect函数连接服务器,并自动完成客户端套接字的IP和端口分配。

而在UDP程序中,调用sendto函数传输数据前应该完成对套接字的地址分配工作。
在UDP中能否通过bind函数为套接字绑定IP和端口呢?答案是可以的,因为bind不区分UDP和TCP,因此在UDP程序中也可以使用。

另外,调用sendto函数时发送尚未分配地址信息,则在首次调用sendto函数时给相应套接字自动分配IP和端口,而且此时分配的IP和端口会一直保留到程序结束为止。

综上,在UDP中,调用sendto函数时会自动分配地址信息。

存在数据边界的UDP套接字

UDP是具有数据边界的协议,这意味着输入函数的调用次数必须严格对应输出函数的调用次数,这样才能接收到完整的数据。

这里在客户端调用三次sendto函数,即发送三次数据到服务器,在服务器端只调用一次recvfrom函数试图接收所有数据,这是行不通的。

服务器端代码如下:

//UDP_Server.cpp

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

#define BUF_SIZE 1024

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc != 2)
	{
		return -1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("WSAStartup Error!\n");
		return -1;
	}

	SOCKET srvSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == srvSock)
	{
		printf("socket Error!\n");
		WSACleanup();
		return -1;
	}

	SOCKADDR_IN srvAddr;
	memset(&srvAddr, 0, sizeof(srvAddr));
	srvAddr.sin_family = PF_INET;
	srvAddr.sin_addr.s_addr = htonl(ADDR_ANY);
	srvAddr.sin_port = htons(_ttoi(argv[1]));
	int srvAddrLen = sizeof(srvAddr);

	if (SOCKET_ERROR == bind(srvSock, (sockaddr*)&srvAddr, sizeof(srvAddr)))
	{
		printf("bind Error!\n");
		closesocket(srvSock);
		WSACleanup();
		return -1;
	}
	
	char Msg[BUF_SIZE];
	int recvLen = 0;

	SOCKADDR_IN cltAddr;
	memset(&cltAddr, 0, sizeof(cltAddr));
	int nCltAddrLen = sizeof(cltAddr);

	recvLen = recvfrom(srvSock, Msg, BUF_SIZE - 1, 0, (sockaddr*)&cltAddr, &nCltAddrLen);
	Msg[recvLen] = 0;

	printf("recvfrom client msg: %s\n", Msg);

	closesocket(srvSock);
	WSACleanup();

	getchar();

	return 0;
}


客户端代码如下:

//UDP_Client.cpp
#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

#define BUF_SIZE 1024

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc != 3)
	{
		return -1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("WSAStartup Error!\n");
		return -1;
	}

	SOCKET cltSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == cltSock)
	{
		printf("socket Error!\n");
		WSACleanup();
		return -1;
	}

	SOCKADDR_IN srvAddr;
	memset(&srvAddr, 0, sizeof(srvAddr));
	srvAddr.sin_family = PF_INET;
	srvAddr.sin_addr.s_addr = inet_addr(argv[1]);
	srvAddr.sin_port = htons(_ttoi(argv[2]));

	int nSrvAddrLen = sizeof(srvAddr);

	char Msg[BUF_SIZE] = "0123456789";
	int recvLen = 0;

	//客户端发送三次数据,服务器端调用一次recvfrom试图接收三次数据。是行不通的
	int nSendLend = 0;
	
	//因为没有事先为cltSock分配地址信息,因此这里每次调用都会自动分配IP和端口
	nSendLend += sendto(cltSock, Msg, strlen(Msg), 0, (sockaddr*)&srvAddr, nSrvAddrLen);
	nSendLend += sendto(cltSock, Msg, strlen(Msg), 0, (sockaddr*)&srvAddr, nSrvAddrLen);
	nSendLend += sendto(cltSock, Msg, strlen(Msg), 0, (sockaddr*)&srvAddr, nSrvAddrLen);


	closesocket(cltSock);
	WSACleanup();

	getchar();

	return 0;
}


存在数据边界的UDP套接字测试

服务器端也必须调用相应次数的recvfrom才能接收到客户端发来的完整数据:

//调整UDP_Server.cpp中接收数据部分处理
//上文同上,故省略
	for (int i = 0; i < 3; i++)
	{
		recvLen = 0;
		recvLen = recvfrom(srvSock, Msg, BUF_SIZE - 1, 0, (sockaddr*)&cltAddr, &nCltAddrLen);
		Msg[recvLen] = 0;
		printf("recvfrom client msg: %s\n", Msg);
	}
//下文同上,故省略

数据边界处理测试结果

已连接(connected)和未连接(unconnected)的UDP套接字

TCP套接字中需注册待传输数据的目标IP和端口号,而UDP中无需事先注册。因此,通过sendto函数传输数据的过程大致可分为以下3个阶段:
①向UDP套接字注册目标IP和端口号
②传输数据
③删除UDP套接字中注册的模板地址信息

每次调用sendto函数时重复上述过程。每次都变更目标地址,因此可以重复利用同一UDP套接字向不同目标传输数据。
这种事先注册目标地址信息,在sendto时才注册的套接字称为未连接套接字。反之事先注册了目标地址的套接字称为连接套接字。显然UDP套接字默认属于未连接套接字。

但UDP套接字在只需向一个目标地址传输数据时就显得不太合理。
例:IP为169.21.32.110的主机9190号端口共准备了3个数据,因此需要调用三次sendto函数进行传输。
此时需要重复上述三阶段,上述三个阶段中,第①、③个阶段占整个通信过程近1/3的时间,缩短这部分时间将大大提高整体效率。
因此,要与同一主机进行长时间通信时,将UDP套接字编程连接套接字会提供效率。

如何将UDP套接字变成连接套接字?
对UDP套接字调用connect函数即可。

修改上面服务器/客户端代码如下:

//UDP_Server.cpp
#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

#define BUF_SIZE 1024

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc != 2)
	{
		return -1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("WSAStartup Error!\n");
		return -1;
	}

	SOCKET srvSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == srvSock)
	{
		printf("socket Error!\n");
		WSACleanup();
		return -1;
	}

	SOCKADDR_IN srvAddr;
	memset(&srvAddr, 0, sizeof(srvAddr));
	srvAddr.sin_family = PF_INET;
	srvAddr.sin_addr.s_addr = htonl(ADDR_ANY);
	srvAddr.sin_port = htons(_ttoi(argv[1]));
	int srvAddrLen = sizeof(srvAddr);

	if (SOCKET_ERROR == bind(srvSock, (sockaddr*)&srvAddr, sizeof(srvAddr)))
	{
		printf("bind Error!\n");
		closesocket(srvSock);
		WSACleanup();
		return -1;
	}

	char Msg[BUF_SIZE];
	int recvLen = 0;

	SOCKADDR_IN cltAddr;
	memset(&cltAddr, 0, sizeof(cltAddr));
	int nCltAddrLen = sizeof(cltAddr);

	while (true)
	{
		printf("Wait Msg From Client...\n");
		//无连接的UDP套接字,存在数据边界,sendto和recvfrom必须一一对应
		//无连接的UDP套接字不会保持连接状态,因此每次传输数据都需要添加目标地址信息
		recvLen = recvfrom(srvSock, Msg, BUF_SIZE - 1, 0, (sockaddr*)&srvAddr, &srvAddrLen);
		if (recvLen == -1)
		{
			//UDP属于无连接协议,因此没有断开连接的说法,这里不会进来
			printf("Client Disconnected.");
			break;
		}
		Msg[recvLen] = 0;
		printf("Receive Msg from Client: %s\n", Msg);

		sendto(srvSock, Msg, recvLen, 0, (sockaddr*)&srvAddr, srvAddrLen);
	}

	closesocket(srvSock);
	WSACleanup();

	getchar();

	return 0;
}

//UDP_Client.cpp

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

#define BUF_SIZE 1024


int _tmain(int argc, _TCHAR* argv[])
{
	if (argc != 3)
	{
		return -1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("WSAStartup Error!\n");
		return -1;
	}

	SOCKET cltSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == cltSock)
	{
		printf("socket Error!\n");
		WSACleanup();
		return -1;
	}

	SOCKADDR_IN srvAddr;
	memset(&srvAddr, 0, sizeof(srvAddr));
	srvAddr.sin_family = PF_INET;
	srvAddr.sin_addr.s_addr = inet_addr(argv[1]);
	srvAddr.sin_port = htons(_ttoi(argv[2]));

	int nSrvAddrLen = sizeof(srvAddr);

	//将UDP套接字转换成连接套接字,在此函数内分配cltSock的地址信息
	connect(cltSock, (sockaddr*)&srvAddr, sizeof(srvAddr));

	char Msg[BUF_SIZE] = "0123456789";
	int recvLen = 0;

	//客户端发送三次数据,服务器端调用一次recvfrom试图接收三次数据。是行不通的
	//int nSendLend = 0;
	//nSendLend += sendto(cltSock, Msg, strlen(Msg), 0, (sockaddr*)&srvAddr, nSrvAddrLen);
	//nSendLend += sendto(cltSock, Msg, strlen(Msg), 0, (sockaddr*)&srvAddr, nSrvAddrLen);
	//nSendLend += sendto(cltSock, Msg, strlen(Msg), 0, (sockaddr*)&srvAddr, nSrvAddrLen);


	while (true)
	{
		fputs("Input Msg(q to quit): ", stdout);
		fgets(Msg, sizeof(Msg), stdin);
		
		if (!strcmp(Msg, "q\n") || !strcmp(Msg, "Q\n"))
		{
			printf("Disconnected...");
			break;
		}
		//上面已事先分配IP和端口,因此后续的sendto调用都不再有分配和删除地址信息处理,从而提交整体效率。
		//sendto(cltSock, Msg, strlen(Msg), 0, (sockaddr*)&srvAddr, sizeof(srvAddr));

		//recvLen = recvfrom(cltSock, Msg, BUF_SIZE - 1, 0, (sockaddr*)&srvAddr, &nSrvAddrLen);
		//Msg[recvLen] = 0;

		//已连接的UDP套接字可以直接使用send 和 recv函数
		send(cltSock, Msg, strlen(Msg), 0);

		recvLen = recv(cltSock, Msg, BUF_SIZE - 1, 0);
		Msg[recvLen] = 0;

		printf("Msg From Server: %s\n", Msg);
	}
	

	closesocket(cltSock);
	WSACleanup();

	getchar();

	return 0;
}

已连接的UDP套接字测试结果

总结

综上,可总结UDP服务器/客户端的开发步骤如下:
服务器端:
①创建服务器端UDP套接字
②通过bind绑定服务器端UDP套接字的地址信息
③事先准备SOCKADDR_IN变量保存往来的客户端的地址信息
④使用recvfrom、sendto函数交互数据
⑤关闭服务器端套接字

客户端:
①创建客户端UDP套接字
②初始化服务器端地址信息
③【可选】调用connect函数事先分配客户端UDP套接字的地址信息
④未事先连接的情况下,可使用sendto、recvfrom函数与服务器交互。事先通过connect函数连接的情况下,还可使用send、recv与服务器交互
⑤关闭客户端套接字

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

网络编程六--UDP服务器客户端 的相关文章

  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 自动从 C# 代码进行调试过程并读取寄存器值

    我正在寻找一种方法来读取某个地址的 edx 注册表 就像这个问题中所问的那样 读取eax寄存器 https stackoverflow com questions 16490906 read eax register 虽然我的解决方案需要用
  • Signalr 在生产服务器中总是陷入长轮询

    当我在服务器中托管应用程序时 它会检查服务器端事件并始终回退到长轮询 服务器托管环境为Windows Server 2012 R1和IIS 7 5 无论如何 我们是否可以解决这个问题 https cloud githubuserconten
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • 嵌入式系统中的malloc [重复]

    这个问题在这里已经有答案了 我正在使用嵌入式系统 该应用程序在 AT91SAMxxxx 和 cortex m3 lpc17xxx 上运行 我正在研究动态内存分配 因为它会极大地改变应用程序的外观 并给我更多的力量 我认为我唯一真正的路线是为
  • fgets() 和 Ctrl+D,三次才能结束?

    I don t understand why I need press Ctrl D for three times to send the EOF In addition if I press Enter then it only too
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • 跨多个控件共享事件处理程序

    在我用 C 编写的 Windows 窗体应用程序中 我有一堆按钮 当用户的鼠标悬停在按钮上时 我希望按钮的边框发生变化 目前我有以下多个实例 每个按钮一个副本 private void btnStopServer MouseEnter ob
  • 如何在 WPF RichTextBox 中跟踪 TextPointer?

    我正在尝试了解 WPF RichTextBox 中的 TextPointer 类 我希望能够跟踪它们 以便我可以将信息与文本中的区域相关联 我目前正在使用一个非常简单的示例来尝试弄清楚发生了什么 在 PreviewKeyDown 事件中 我
  • 如何针对 Nancy 中的 Active Directory 进行身份验证?

    这是一篇过时的文章 但是http msdn microsoft com en us library ff650308 aspx paght000026 step3 http msdn microsoft com en us library
  • A* 之间的差异 pA = 新 A;和 A* pA = 新 A();

    在 C 中 以下两个动态对象创建之间的确切区别是什么 A pA new A A pA new A 我做了一些测试 但似乎在这两种情况下 都调用了默认构造函数 并且仅调用了它 我正在寻找性能方面的任何差异 Thanks If A是 POD 类
  • 如何在 Team Foundation 上强制发表有意义的签入评论?

    我有一个开发团队有一个坏习惯 他们写道poor签入评论 当我们必须在团队基础上查看文件的历史记录时 这使得它成为一场噩梦 我已经启用了变更集评论政策 这样他们甚至可以在签到时留下评论 否则他们不会 我们就团队的工作质量进行了一些讨论 他们很
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 如何在内存中存储分子?

    我想将分子存储在内存中 这些可以是简单的分子 Methane CH4 C H bond length 108 7 pm H H angle 109 degrees But also more complex molecules like p
  • 方法参数内的变量赋值

    我刚刚发现 通过发现错误 你可以这样做 string s 3 int i int TryParse s hello out i returns false 使用赋值的返回值是否合法 Obviously i is but is this th
  • 如何在 C# 中播放在线资源中的 .mp3 文件?

    我的问题与此非常相似question https stackoverflow com questions 7556672 mp3 play from stream on c sharp 我有音乐网址 网址如http site com aud
  • 将变量分配给另一个变量,并将一个变量的更改反映到另一个变量中

    是否可以将一个变量分配给另一个变量 并且当您更改第二个变量时 更改会瀑布式下降到第一个变量 像这样 int a 0 int b a b 1 现在 b 和 a 都 1 我问这个问题的原因是因为我有 4 个要跟踪的对象 并且我使用名为 curr
  • 如何将字符串“07:35”(HH:MM) 转换为 TimeSpan

    我想知道是否有办法将 24 小时时间格式的字符串转换为 TimeSpan 现在我有一种 旧时尚风格 string stringTime 07 35 string values stringTime Split TimeSpan ts new
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器
  • 为什么 strtok 会导致分段错误?

    为什么下面的代码给出了Seg 最后一行有问题吗 char m ReadName printf nRead String s n m Writes OK char token token strtok m 如前所述 读取字符串打印没有问题 但

随机推荐

  • [pytorch报错]ValueError: num_samples should be a positive integer value, but got num_samples=0

    项目场景 在mmeg中运行自定义数据集报错情况 问题描述 运行代码后 mmSeg提示说 2021 12 08 18 39 33 991 mmseg INFO Loaded 0 images 说明模型没有加载数据 数据地址出错 报错信息 Fi
  • 轮胎的魔术公式(Magic Fomula)模型

    本篇结合Adams中的TR rear pac89 tir文件 介绍一下魔术公式的基本知识 使用魔术公式的轮胎模型主要有Pacejka 89 Pacejka 94 MF Tyre MF Swift四种 1 Pacejka 89和 94轮胎模型
  • java常用框架总结

    今天想看看现在常用的框架有哪些 发现网上文章不多决定根据自己的理解写一篇文章 如有错误希望大家包涵 1 java的5大框架 springboot都不说了 网上资料很多 2 缓存工具 Ehcache redis 3 消息队列中间工具 Rabb
  • 层次聚类AgglomerativeClustering()树形图可视化与children_属性解析

    层次聚类函数AgglomerativeClustering 我们主要来讲解层次聚类的可视化画图和层次聚类后返回的children 属性 children 属性的理解需要借助可视化图形 1 层次聚类的可视化画图 1 1步骤 step1 使用
  • Linux中修改jar包中的文件内容

    1 命令行中输入vim jar包路径 2 回车 打开jar包中的文件目录 并定位到要修改的文件位置 3 回车 打开文件内容进行修改 4 修改后 Ctrl wq保存退出上步 再次Ctrl q退出 完成修改
  • 计算机二级页表页框大小,两级页表

    本文是在基本分页存储管理的基础上对分页管理的优化 在上篇文章中说到 操作系统会为每个进程建立一张页表 实现页号和内存块号之间的对应关系 本文从单级页表存在的问题引出两级页表 以及两级页表如何实现地址的变换 1单级页表存在的问题 假设某计算机
  • 两层和三层的讨论

    下面的东西都是转贴的 包括那个声明 都跟我没关系 google搜出来的 在这之前我的确觉得DataSetProvider ClientDataSet和我理解中的三层有点差别 看完这些讨论 我的结论是 DataSetProvider Clie
  • 2023华为OD机试真题-跳格子2JAVA、Python、C++)

    题目描述 小明和朋友玩跳格子游戏 有 n 个连续格子组成的圆圈 每个格子有不同的分数 小朋友可以选择从任意格子起跳 但是不能跳连续的格子 不能回头跳 也不能超过一圈 给定一个代表每个格子得分的非负整数数组 计算能够得到的最高分数 输入描述
  • BFS常见模板题(初学BFS推荐,附例题由浅入深)

    BFS类题目 主要考查对广度搜索的理解 BFS相比于暴力枚举来说效率更高 BFS只要将范围矩阵扫一次即可得出答案 本文通过队列来实现求解 当然也可以用其他方式实现广度搜索 First question 馋嘴羊 BFS思路 可能会比较抽象 建
  • 图论之Dijkstra

    Dijkstra模板 include
  • JavaScript V8 > 牛客OJ在线编程常见输入输出练习场

    C 处理输入输出 牛客OJ在线编程常见输入输出练习场 A A B 1 B A B 2 C A B 3 D A B 4 E A B 5 F A B 6 G A B 7 H 字符串排序 1 I 字符串排序 2 J 字符串排序 3 牛客OJ在线编
  • C语言函数大全-- _w 开头的函数(4)

    w 开头的函数 4 1 wstrtime 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 wstrtime s 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 wsetlocale 3 1 函数说明 3 2 演示示例 3
  • STM32 ADC

    1 什么是ADC ADC指模数转换 模拟信号是指时间幅值均连续的信号 对其采样后得到的信号称之为离散时间信号 若再对其进行量化处理则得到数字信号 请注意对模拟信号采样得到的信号不是数字信号 仅仅是离散时间信号 详情请见数字电子电路 信号与系
  • python稳健性检验_有哪些比较好的做异常值检测的方法?

    最近很多小伙伴都比较关注异常值检测的方法 接下来小编就为大家介绍几种 希望能帮到大家 摘要 本文介绍了异常值检测的常见四种方法 分别为Numeric Outlier Z Score DBSCA以及Isolation Forest 在训练机器
  • 算法导论 第三节 分治法

    分治法 1 分 把一个大问题分成若干个小问题 即原问题的n变小 2 治 递归的解决每一个子问题 然后把这些子问题的解合并成整个大问题的解 归并排序 1 一分为二 2 递归的对每一个子数组进行排序 3 合并 线性的n时间内就可以完成 归并排序
  • vscode修改npm镜像源

    npm install g cnpm registry https registry npm taobao orgnpm install g cnpm registry https registry npm taobao org npm c
  • 内核空间和应用空间的数据拷贝(copy_to_user & copy_from_user)

    1 copy to user copy from user long copy to user void user to const void from unsigned long n long copy from user void to
  • 【C++学习笔记(五十一)】之Qt中的信号和槽机制

    一 信号和槽机制 信号和槽机制分为信号和槽函数 用于处理事件 当某个事件发生时 比如说某个按钮被点击后 它就会发出一个信号 signal 如果有对象对这个信号感兴趣 那么它就会使用连接 connect 函数 将该信号与自己的一个槽函数 sl
  • ubuntu 18.04安装pycharm及编译环境配置

    PyCharm介绍 PyCharm是一种Python IDE 其带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具 比如 调试 语法高亮 Project管理 代码跳转 智能提示 自动完成 单元测试 版本控制等等 此外 该ID
  • 网络编程六--UDP服务器客户端

    写在前面 UDP User Datagram Protocol 称为用户数据报协议 是一种无连接的传输协议 UDP的主要应用在即使丢失部分数据 也不影响整体效果的场景 例实时传输视频或音频时 即使丢失部分数据 也不会影响整体效果 只是会有轻