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

news2024/9/23 19:29:14

写在前面

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与服务器交互
⑤关闭客户端套接字

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

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

相关文章

C++11大杂烩

C11大杂烩 文章目录 C11大杂烩介绍语法统一的列表初始化&#xff1a;{}初始化initializer_list简化声明的方式autotypeid().name():获取类型名decltype nullptr范围for循环stl库中的一些变化arrayforward_list final和override右值引用和移动语义左值引用和右值引用 移动构造和…

有没有好用的UI在线设计工具?这5个设计师必备!

这篇文章介绍了 5 款在线UI设计工具&#xff0c;分别是即时设计、InVision Studio、Axure、Framer 和 Principle。其中&#xff0c;即时设计是一款次世代的在线协作UI设计工具&#xff0c;支持多人协同在线设计一键交付、插入交互式动画等功能&#xff0c;最近还更新了全球首款…

网络基础知识1—网络

文章目录 1.网络划分1.1局域网&#xff08;内网&#xff09;1.2广域网&#xff08;公网&#xff09; 2.网络的作用3.端口号3.1作用3.2两台主机中的进程传输数据3.3格式3.4注意 4.协议4.1概念4.2三要素4.3最终体现4.4作用 5.五元组5.1源IP5.2源端口5.3目的IP5.4目的端口5.5协议 …

MySQL中这14个神仙功能

1.group_concat 在我们平常的工作中&#xff0c;使用group by进行分组的场景&#xff0c;是非常多的。 比如想统计出用户表中&#xff0c;名称不同的用户的具体名称有哪些&#xff1f; 具体sql如下&#xff1a; select name from user group by name;但如果想把name相同的c…

IO 流学习总结

一&#xff1a;IO 流的概述 1. 什么是 IO 流&#xff1f; 存储和读取数据的解决方法 I&#xff1a;input O&#xff1a;output 流&#xff1a;像水流一样传输数据 2. IO 流的作用&#xff1f; 用于读写数据&#xff08;本地文件&#xff0c;网络&#xff09; 3. IO 流按…

三年亏百亿仍要造“跑车”,哪吒还有几次试错?

文丨智能相对论 作者丨leo陈 燃油车时代&#xff0c;国产品牌没有一款真正意义上成功的“低价跑车”&#xff0c;那在新能源时代&#xff0c;“电”是否可以创造这种可能&#xff1f; 第一个交出答卷的是哪吒汽车。不久前&#xff0c;哪吒发布首款纯电跑车“哪吒GT”&#x…

3个方法提高电脑运行速度,亲测有效!

案例&#xff1a;怎样提高电脑运行的速度&#xff1f; 【随着使用时间的增长&#xff0c;我的电脑运行速度越来越慢&#xff0c;这样我感到十分不方便和烦恼。有什么办法可以提高电脑的运行速度吗&#xff1f;】 在日常使用电脑过程中&#xff0c;我们难免会遇到电脑运行缓慢…

【C++】第13章: 类继承

文章目录 第十三章 类继承13.1 一个简单的基类13.1.1 派生一个类13.1.2 构造函数&#xff1a;访问权限的考虑13.1.3 使用派生类13.1.4 派生类和基类之间的特殊关系 13.2 继承&#xff1a;is-a关系13.3 多态公有继承13.4 静态联编与动态联编13.4.1 指针和引用类型的兼容性13.4.2…

Doris简介、部署、功能介绍以及架构设计

Doris简介、部署、功能介绍以及架构设计 1. Doris简介 Doris 中文官方文档&#xff1a;https://doris.apache.org/zh-CN/docs/dev/summary/basic-summary 1.1 Doris概述 ​ Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人…

5.10晚间黄金CPI精准分析及多空交易策略

近期有哪些消息面影响黄金走势&#xff1f;本周黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周三&#xff08;5月10日&#xff09;亚欧盘中&#xff0c;现货黄金震荡下跌&#xff0c;现报2030美元/盎司&#xff0c;稍早一度触及2038美元/盎司高点。美联储理事…

day30_jdbc

今日内容 零、 复习昨日 一、作业 二、SQL注入 三、PreparedStatement 四、事务 五、DBUtil 零、 复习昨日 见晨考 一、作业 package com.qf.homework;import com.qf.entity.User;import java.sql.*; import java.text.SimpleDateFormat; import java.util.ArrayList; import …

Selenium技术在CentOS6.8系统的腾讯云服务器上的相关使用(Linux环境下)

目录 一、解释说明二、操作过程中Linux相关命令1、下载谷歌浏览器2、查看谷歌浏览器的版本3、下载对应版本的谷歌驱动&#xff08;或者本地上传&#xff09;4、解压下载的文件5、移动下载文件6、给予文件执行权限7、更新pip3到最高版本8、下载Selenium第三方库9、正式测试10、最…

Rust 快速入门60分① 看完这篇就能写代码了

Rust 一门赋予每个人构建可靠且高效软件能力的语言https://hannyang.blog.csdn.net/article/details/130467813?spm1001.2014.3001.5502关于Rust安装等内容请参考上文链接&#xff0c;写完上文就在考虑写点关于Rust的入门文章&#xff0c;本专辑将直接从Rust基础入门内容开始讲…

如何预测药品市场规模

药品市场规模预测是一个非常关键的步骤&#xff0c;可以帮助判断该项目是否值得投资或开发。以下是一些常见的方法&#xff1a; 药品市场规模可以细分为治疗领域市场规模、药品种类市场规模、区域市场规模、渠道市场规模、品牌市场规模、性质市场规模等。这些规模的了解是一个非…

【Hello Algorithm】异或法

作者&#xff1a;小萌新 专栏&#xff1a;算法 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;介绍算法中的异或法 异或法 异或的概念异或的两个性质题目一 不使用额外变量交换两个数字题目二 出现奇数次的数字题目三 如何从一个整型数字中提取出…

石油化工行业室内外高精度人员定位系统解决方案

石油化工行业是高危行业&#xff0c;很容易发生安全事故&#xff0c;对于石化企业来说&#xff0c;加强人员的安全管控非常有必要。我们可以通过人员定位技术&#xff0c;提升石化企业安全管理水平。下面给大家分享石油化工行业室内外高精度人员定位系统解决方案。 方案概述 石…

BERT原理Fine TuningBert变种

文章目录 BERT原理训练时的任务任务一任务二任务二的改进 模型的输入 BERT - Fine Tuning单个句子的预测类序列标注类Q&A类seq2seq&#xff1f; BERT 变种Transformer-XLXLNetAutoregressive Language ModelDenoising Auto-Encoder乱序Two-Stream Attention与Transformer-X…

RocketMQ双主双从环境搭建

环境要求 64位操作系统&#xff0c;推荐 Linux/Unix/macOS 64位 JDK 1.8 服务器准备 准备4台服务器两台master两台slave&#xff0c;如果服务器紧凑&#xff0c;则至少需要两台服务器相互master-slave IP HOSTS 172.*******.120 rocketmq-nameserver1 rocketmq-master1 …

ElasticSearch小计

1、ElasticSearch简介 1.1、ElasticSearch&#xff08;简称ES&#xff09; Elasticsearch是用Java开发并且是当前最流行的开源的企业级搜索引擎。能够达到近实时搜索&#xff0c;稳定&#xff0c;可靠&#xff0c;快速&#xff0c;安装使用方便。客户端支持Java、.NET&#x…

Class 00 - 学习编程的方法不同职业所使用的编程语言

Class 00 - 学习编程的方法&不同职业所使用的编程语言 学习编程的方法什么是编程&#xff1f;不同职业所使用的编程语言数据分析网页设计移动应用开发Web应用开发游戏开发 Tips&#xff1a;学习编程语言的技巧 从电子表格到 SQL 再到 R电子表格、SQL和R:一个比较 学习编程的…