网络编程 基于tcp/ip协议的C/S模型

news2024/12/24 9:36:27

1.socket编程的一些概念

        socket作用:运行在计算机中的两个程序通过socket建立起一个通道,数据在通道中传输。socket提供了流(stream),是基于TCP协议,是一个有序、可靠、双向字节流的通道,传输数据不会丢失、不会重复、顺序也不会错乱。

        TCP与UDP的区别:
1.TCP基于连接与UDP无连接
2.对系统资源的要求(TCP 较多,UDP 少)
3.UDP 程序结构较简单
4.流模式(TCP)与数据报模式(UDP)
        TCP 保证数据正确性,UDP可能丢包
        TCP 保证数据顺序,UDP 不保证

具体编程时的区别
1.socket的参数不同
2.UDP Server不需要调用 listen 和accept

3.UDP 收发数据用 sendto/recvfrom函数

4.TCp:地址信息在 connect/accept 时确定,UDP :在sendtorecvfrom函数中每次均 需指定地址信息5.UDP : shutdown 函数无效

        socket中的一些名词解释:

多重协议支持: 通过SPI接口支持其他协议;

多重命名空间: 根据服务与主机名选择协议

重叠I/0模式: 增强I/0香叶量与提高性能

分散与聚合:从多个缓冲区发送与接收数据

有条件接受:有选择性地决定是否接受连接

套接字共享:多个进程共享一个套接字句柄

socket简单的通信流程

2.tcp/ip协议的基本知识

3.网络编程代码

        1.头文件

c++网络编程有两个版本的头文件,第一版 <WinSock.h>,第二版 <WinSock2.h>,现在使用一般都是使用第二个版本,网络库也有两个版本,第一版 "wsock32.lib", 第二版 "ws2_32.lib",这两个版本都能用

#include<WinSock.h>//第一版
#include<WinSock2.h>//第二版
#pragma comment(lib,"wsock32.lib")//第一版 网络库
#pragma comment(lib,"ws2_32.lib")//第二版

        2.WSAStartup()函数的使用

        该函数的作用是用进程启动网络库(Winsock DLL),函数的原型

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

        WORD类型实际上是一个 unsigned short 类型,参数wVersionRequested需要指定网络库的版本号,参数lqWSAData是一个结构体类型,返回的各种版本信息会存入结构体中的成员。

        函数如果执行成功会返回一个0,失败会返回错误信息,返回的错误信息有一些几类

错误代码含义
WSASYSNOTREADY基础网络子系统尚未准备好进行网络通信
WSAVERNOTSUPPORTED此特定 Windows 套接字实现不提供请求的 Windows 套接字支持版本。
WSAEINPROGRESS正在执行阻止 Windows 套接字 1.1 操作。
WSAEPROCLIM已达到 Windows 套接字实现支持的任务数的限制。
WSAEFAULTlpWSAData 参数不是有效的指针。

代码样例

#include<stdio.h>
#include<WinSock2.h> //网络头文件 window socket 第二版
#pragma comment(lib,"ws2_32.lib")//包含网络库的名字, ws2_32.lib是网络库 名字 //不区分大小写
//#pragma comment(lib, "wsock32.lib") //第一版的库

int main()
{
	//WSAstartup函数有两个参数,一个是 WORD 类型,一个是 LPWSADATA 结构体类型 在文档中 LP 开头的变量表示需要的地址变量
	WORD wdVersion = MAKEWORD(2, 2);//选定版本号 这里表示2.2版本
	//LPWSADATA和WSADATA*等价
	//LPWSADATA lwq = malloc(sizeof(WSADATA));//创建一个堆区
	WSADATA wdSockMsg;
	int nRes = WSAStartup(wdVersion, &wdSockMsg);//有返回值,启动成功返回0
	if (nRes != 0)
	{//这里是5个错误码
		switch (nRes)
		{
		case WSASYSNOTREADY:
			printf("基础网络子系统尚未准备好进行网络通信");
			break;
		case WSAVERNOTSUPPORTED:
			printf("此特定 Windows 套接字实现不提供请求的 Windows 套接字支持版本");
			break;
		case WSAEINPROGRESS:
			printf("正在执行阻止 Windows 套接字 1.1 操作。 ");
			break;
		case WSAEPROCLIM:
			printf("已达到 Windows 套接字实现支持的任务数的限制。");
			break;
		case WSAEFAULT:
			printf("lpWSAData 参数不是有效的指针");
			break;
		}
	}
	//校验版本
    //HIBYTE表示高位:是主版本,LOBYTE表示地位:是副版本
	if (2 != HIBYTE(wdSockMsg.wVersion) || 2 != LOBYTE(wdSockMsg.wVersion))
	{
		//版本不对
		WSACleanup();//关闭网络库函数
		return 0;
	}

	system("pause");
	return 0;
}

        3.SOCKET函数的使用

        1.socket介绍

         2.socket()函数

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

SOCKET WSAAPI socket(
  [in] int af,
  [in] int type,
  [in] int protocol
);

 参数1 af:是地址的类型参数

参数名称含义例子
AF_INET 2表示IPV4的地址,4个字节,32位的地址192.168.179.132
AF_INET6 23表示IPV6的地址,16个字节,128位地址fh40::4fd5:cgya:85b:84a5%20

AF_BTH 32

蓝牙地址系列6D:2D:BC:AA:8G:11

参数2 type:套接字类型

类型含义
SOCK_STREAM 1一种套接字类型,使用OOB数据传输机制提供有序、可靠、双向、基于连接的字节流。这种套接字类型为Internet地址族(AF_INET或AF_INET6)使用传输控制协议(TCP)。
SOCK_DGRAM 2一种支持数据报的套接字类型,数据报是固定(通常是小的)最大长度的无连接、不可靠的缓冲区。这种套接字类型使用用户数据报协议(UDP)作为Internet地址族(AF_INET或AF_INET6)。
SOCK_RAW 3一种套接字类型,提供一个原始套接字,允许应用程序操作下一个上层协议头。要操作IPv4报头,必须在套接字上设置IP_HDRINCL套接字选项。要操作IPv6报头,必须在套接字上设置IPV6_HDRINCL套接字选项。
SOCK_RDM 4

提供可靠消息数据报的套接字类型。这种类型的一个例子是Windows中的实用通用多播(Pragmatic General Multicast, PGM)多播协议实现,通常称为可靠的多播编程。


该类型值仅在安装可靠组播协议时被支持。

SOCK_SEQPACKET 5一种套接字类型,提供基于数据报的伪流包。

参数3 protocol:协议的类型

类型含义
IPPROTO_TCP 6传输控制协议(TCP)。当af参数为AF_INET或AF_INET6,类型参数为SOCK_STREAM时,这个值是可能的。
IPPROTO_UDP 17用户数据报协议(UDP)。当af参数为AF_INET或AF_INET6,类型参数为SOCK_DGRAM时,这个值是可能的。
IPPROTO_IGMP 2

Internet组管理协议(IGMP)。当af参数为AF_UNSPEC、AF_INET或AF_INET6且类型参数为SOCK_RAW或未指定时,这个值是可能的。

IPPROTO_ICMP 1Internet控制报文协议(ICMP)。当af参数为AF_UNSPEC、AF_INET或AF_INET6且类型参数为SOCK_RAW或未指定时,这个值是可能的。

        返回值

socket函数如果成功返回一个可用的socket,如果失败会返回 INVALID_SOCKET ,表示失败,可以调用函数 WSAGetLastError() 获取错误码。

注意:在最后一定要调用函数 closesocket() 函数销毁socket。

代码样例

	//SOCKET的使用
	SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//三个参数 如果成功会返回一个可以的socket,
																	//在最后一定要销毁socket套接字
	if (INVALID_SOCKET == socketServer)
	{
		int a = WSAGetLastError();//当socket函数绑定传输协议套接字失败,会调用该函数,返回错误码。
		WSACleanup();
		return 0;
	}
	closesocket(socketServer);//销毁套接字的函数

4.bind()函数使用

该函数将本地地址与套接字相关联。函数原型

int WSAAPI bind(
  [in] SOCKET         s,
  [in] const sockaddr *name,
  [in] int            namelen
);

参数1 SOCKET:是socket()函数返回的值

参数2 name:是ip地址

参数3 namelen:空间大小

        参数2 name 是 sockaddr类型,这是网络库中内置的结构体类型,如下

typedef struct sockaddr_in {
  short          sin_family;
  u_short        sin_port;
  struct in_addr sin_addr;
  char           sin_zero[8];
} SOCKADDR_IN, *PSOCKADDR_IN, *LPSOCKADDR_IN;

sin_family是一个与socker函数中第一个参数一样的变量

sin_port表示指定的端口号

sin_addr表示指定的ip地址

        返回值

函数执行成功会返回0,失败会返回一个SOCKET_ERROR,实际上是一个-1

bind函数使用实例

//bing函数的使用
struct sockaddr_in si;//定义一个结构体
si.sin_family = AF_INET;//需要和socket函数中的参数一致
si.sin_port = htons("12345");//指定的端口号
si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//指定ip地址
bind(socketServer, (const struct sockaddr*)&si, sizeof(struct sockaddr_in));

5.listen()函数的使用

该函数将套接字置于侦听传入连接的状态(就是等待客户端连接),函数原型

int WSAAPI listen(
  [in] SOCKET s,
  [in] int    backlog
);

参数1 s:是socket函数的返回值

参数2 backlog:挂起的连接队列的最大长度

        返回值:连接成功返回0,失败返回SOCKET_ERROR

listen函数使用实例

//listen()函数的使用
//listen(socketServer, SOMAXCONN);
if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
{
	//出错了
	int a = WSAGetLastError();
	//释放
	closesocket(socketServer);
	//释放网络库
	WSACleanup();
}

6.accept函数使用

该函数允许在套接字上尝试传入连接,即把接收到的客户端信息创建成socket,函数原型

SOCKET WSAAPI accept(
  [in]      SOCKET   s,
  [out]     sockaddr *addr,
  [in, out] int      *addrlen
);

参数1 s:是socket函数的返回值

参数2 addr:是sockaddr定义的结构体对象

参数3 addrlen:第一个参数的空间长度

        返回值:连接成功返回给客户端包好的socket,失败返回 INVALID_SOCKET,表示无效

注意:该函数在没有客户端连接时,会一直阻塞,直到有客户端连接

accept函数代码样例

	//accept()函数的使用
	//创建客户端连接
	struct sockaddr_in clientMsg;//定义sockaddr_in的结构体类型
	int len = sizeof(clientMsg);//作为参数传入到accept函数
	//accept(socketServer,NULL,NULL);//可以这样
	SOCKET socketClient = accept(socketServer, (struct sockaddr*)&clientMsg, &len);//成功返回一个客户端socket
	if (INVALID_SOCKET == socketClient)
	{
		//出错了
		int a = WSAGetLastError();
		//释放
		closesocket(socketServer);
		//释放网络库
		WSACleanup();
	}

7.recv()函数使用

该函数从连接的套接字或绑定的无连接套接字接收数据,即得到指定客户端发来的消息,本质是由协议本身去做的,也就是socket底层实现,函数原型

int WSAAPI recv(
  [in]  SOCKET s,
  [out] char   *buf,
  [in]  int    len,
  [in]  int    flags
);

参数1 s:是收到信息的socket

参数2 buf:收到信息时,内容的保存空间

参数3 len:每次接收信息的字节大小

参数4 flags:一般为0,表示在系统缓冲区中读完就删除缓冲,如果传入参数 MSG_WAITALL 表示接收到的信息必须是参数3 len的大小才可以执行,否则会被阻塞

        返回值:如果执行没有错误,返回的是收到的字节个数,如果连接正常关闭返回值为0,如果出现错误返回值SOCKET_ERROR,使用WSAGetLastError()函数可以拿到错误码

recv函数代码实例

	//recv()函数使用
	char buf[1500] = { 0 };//作为收到信息的存储空间
	int res = recv(socketClient, buf, 1499, 0);//执行成功会返回接收到信息的字节大小
	if (res == 0)
	{
		printf("连接中断,客户端下线\n");
	}
	else if (SOCKET_ERROR == res)
	{
		//出错了
		int a = WSAGetLastError();//拿到错误码
		//需要根据实际情况处理
	}
	else
	{
		printf("%d		%s\n", res, buf);
	}

8.send()函数使用

该函数在连接的套接字上发送数据,即把要发送的数据发送给客户端,函数原型

int WSAAPI send(
  [in] SOCKET     s,
  [in] const char *buf,
  [in] int        len,
  [in] int        flags
);

参数1 s:客户端socket

参数2 buf:要发送的字符数组

参数3 len:一次发送的字节个数

参数4 flags:一般为0

        返回值:发送成功返回发送的字节个数,发送失败返回SOCKET_ERROR

send函数实例

	//send()函数使用
	if (SOCKET_ERROR == send(socketClient, "abcd\0qwer", sizeof("abcd\0qwer"), 0))//成功会返
回发送出去的字节个数,一次发送出去的字节个数应该在15000以内
	{
		//发送不成功
		printf("发送出错\n");
		int a = WSAGetLastError();//拿到错误码
		//需要根据实际情况处理
	}

9.connect()函数的使用

该函数在客户端中使用,建立与指定套接字的连接,即连接指定ip地址和端口号的服务器,函数原型

int WSAAPI connect(
  [in] SOCKET         s,
  [in] const sockaddr *name,
  [in] int            namelen
);

参数1 s:socket函数返回的值

参数2 name:struct sockaddr定义的结构体对象

参数3 namelen:第二个参数所占用的空间长度

        返回值:连接成功返回0,不成功返回SOCKET_ERROR,需要使用WSAGetLastError()获取错误码,在工具中的错误查找,可以找到具体原因

connect函数代码实例

	//连接到服务器
	struct sockaddr_in serverMsg;
	serverMsg.sin_family = AF_INET;
	serverMsg.sin_port = htons("12345");
	serverMsg.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (SOCKET_ERROR == connect(socketClient, (struct sockaddr*)&serverMsg, sizeof(serverMsg)))
	{
		int a = WSAGetLastError();//获取错误码
		printf("连接出错\n");
		closesocket(socketClient);
		WSACleanup();
		return 0;
	}

10.模型缺点

        客户端与服务器连接后,数据的传送是你一发,我一发的,这样就就造成如果一方没有发送数据,另一方就会死等的情况,如果在等待数据的过程中又有其它的连接请求,会出现无法处理的情况。

11.tcp/ip代码

服务端代码

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include<WinSock2.h> //网络头文件 window socket 第二版
#pragma comment(lib,"ws2_32.lib")//包含网络库的名字, ws2_32.lib是网络库 名字 //不区分大小写
//#pragma comment(lib, "wsock32.lib") //第一版的库

int main()
{
	//第一步打开网络库
	//WSAstartup函数有两个参数,一个是 WORD 类型,一个是 LPWSADATA 结构体类型 在文档中 LP 开头的变量表示需要的地址变量
	WORD wdVersion = MAKEWORD(2, 2);//选定版本号 这里表示2.2版本
	//LPWSADATA和WSADATA*等价
	//LPWSADATA lwq = malloc(sizeof(WSADATA));//创建一个堆区
	WSADATA wdSockMsg;
	int nRes = WSAStartup(wdVersion, &wdSockMsg);//有返回值,启动成功返回0
	if (nRes != 0)
	{//这里是5个错误码
		switch (nRes)
		{
		case WSASYSNOTREADY:
			printf("基础网络子系统尚未准备好进行网络通信");
			break;
		case WSAVERNOTSUPPORTED:
			printf("此特定 Windows 套接字实现不提供请求的 Windows 套接字支持版本");
			break;
		case WSAEINPROGRESS:
			printf("正在执行阻止 Windows 套接字 1.1 操作。 ");
			break;
		case WSAEPROCLIM:
			printf("已达到 Windows 套接字实现支持的任务数的限制。");
			break;
		case WSAEFAULT:
			printf("lpWSAData 参数不是有效的指针");
			break;
		}
		printf("打开网络库出错\n");
		return 0;
	}
	//第二步 校验版本
	if (2 != HIBYTE(wdSockMsg.wVersion) || 2 != LOBYTE(wdSockMsg.wVersion))//HIBYTE表示高位:表示主版本,LOBYTE表示地位:表示副版本
	{
		//版本不对
		WSACleanup();//关闭网络库函数
		printf("校验版本出错\n");
		return 0;
	}


	//第三步 创建SOCKET
	//SOCKET的使用
	SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//三个参数 如果成功会返回一个可以的socket,
																	//在最后一定要销毁socket套接字
	if (INVALID_SOCKET == socketServer)
	{
		int a = WSAGetLastError();//当socket函数绑定传输协议套接字失败,会调用该函数,返回错误码。
		WSACleanup();
		printf("创建socket出错\n");
		return 0;
	}

	//struct sockaddr_in si;
	//si.sin_family = AF_INET;
	//si.sin_port = htons(27015);
	//struct in_addr s;//存放Ipv4地址的结构体
	//inet_pton(AF_INET, "127.0.0.1", (void*)&si.sin_addr.S_un.S_addr);//进行转换

	//第四步 绑定地址与端口号
	//bing函数的使用
	struct sockaddr_in si;//定义一个结构体
	si.sin_family = AF_INET;//需要和socket函数中的参数一致
	si.sin_port = htons(12332);//指定的端口号
	si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//指定ip地址
	//bind(socketServer, (const struct sockaddr*)&si, sizeof(struct sockaddr_in));
	//返回值,成功返回0,不成功返回SOCKET_ERROR实际上是一个-1
	if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*)&si, sizeof(struct sockaddr_in)))
	{
		//出错了
		int a = WSAGetLastError();
		//释放
		closesocket(socketServer);
		//释放网络库
		WSACleanup();
		printf("绑定ip和端口号出错\n");
		return 0;
	}

	//第五步 开始监听
	//listen()函数的使用
	//listen(socketServer, SOMAXCONN);
	if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
	{
		//出错了
		int a = WSAGetLastError();
		//释放
		closesocket(socketServer);
		//释放网络库
		WSACleanup();
		printf("监听函数出错\n");
		return 0;
	}

	//第六步 创建客户端socket/接受连接
	//accept()函数的使用
	//创建客户端连接
	struct sockaddr_in clientMsg;//定义sockaddr_in的结构体类型
	int len = sizeof(clientMsg);//作为参数传入到accept函数
	//accept(socketServer,NULL,NULL);//可以这样
	SOCKET socketClient = accept(socketServer, (struct sockaddr*)&clientMsg, &len);//成功返回一个客户端socket
	if (INVALID_SOCKET == socketClient)
	{
		//出错了
		int a = WSAGetLastError();
		//释放
		closesocket(socketServer);
		//释放网络库
		WSACleanup();
		printf("连接函数出错\n");
		return 0;
	}
	printf("服务器\n");

	char buf[1500] = { 0 };//作为收到信息的存储空间
	int res = recv(socketClient, buf, 1499, 0);//执行成功会返回接收到信息的字节大小
	if (res == 0)
	{
		printf("连接中断,客户端下线\n");
	}
	else if (SOCKET_ERROR == res)
	{
		//出错了
		int a = WSAGetLastError();//拿到错误码
		//需要根据实际情况处理
	}
	else
	{
		printf("%d	%s\n", res, buf);
	}
	if (SOCKET_ERROR == send(socketClient, "我是服务器,我收到了你的消息", sizeof("我是服务器,我收到了你的消息"), 0))//成功会返回发送出去的字节个数,一次发送出去的字节个数应该在15000以内
	{
		//发送不成功
		printf("发送出错\n");
		int a = WSAGetLastError();//拿到错误码
		//需要根据实际情况处理
	}

	//循环一下
	while (1)
	{
		//第七步 与客户端收发消息
	//recv()函数使用
		int res = recv(socketClient, buf, 1499, 0);//执行成功会返回接收到信息的字节大小
		if (res == 0)
		{
			printf("连接中断,客户端下线\n");
		}
		else if (SOCKET_ERROR == res)
		{
			//出错了
			int a = WSAGetLastError();//拿到错误码
			//需要根据实际情况处理
		}
		else
		{
			printf("%d	%s\n", res, buf);
		}

		scanf("%s", buf);
		//send()函数使用
		if (SOCKET_ERROR == send(socketClient, buf, strlen(buf), 0))//成功会返回发送出去的字节个数,一次发送出去的字节个数应该在15000以内
		{
			//发送不成功
			printf("发送出错\n");
			int a = WSAGetLastError();//拿到错误码
			//需要根据实际情况处理
		}
	}

	closesocket(socketServer);//销毁套接字的函数 释放成功返回0
	closesocket(socketClient);
	WSACleanup();//清理网络库 释放成功返回0

	system("pause");
	return 0;
}

客户端代码

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS

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

int main()
{
	//第一步 打开网络库
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA wdSocketMsg;
	int nRes = WSAStartup(wdVersion, &wdSocketMsg);
	if (nRes != 0)
	{
		printf("打开失败");
		return 0;
	}
	//第二部 校验版本
	if (HIBYTE(wdSocketMsg.wVersion) != 2 || LOBYTE(wdSocketMsg.wVersion) != 2)
	{
		//版本不对
		WSACleanup();//关闭网络库
		printf("版本出错\n");
		return 0;
	}
	//第三部 创建socket
	SOCKET socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//还是服务器的socket
	if (socketClient == INVALID_SOCKET)
	{
		//创建了无效的socket
		int a = WSAGetLastError();//获取错误码
		printf("创建出错\n");
		WSACleanup();
		return 0;
	}
	//第四步  连接到服务器
	struct sockaddr_in serverMsg;
	serverMsg.sin_family = AF_INET;
	serverMsg.sin_port = htons(12332);
	serverMsg.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (SOCKET_ERROR == connect(socketClient, (struct sockaddr*)&serverMsg, sizeof(serverMsg)))
	{
		int a = WSAGetLastError();//获取错误码
		printf("连接出错\n");
		closesocket(socketClient);
		WSACleanup();
		return 0;
	}
	printf("客户端\n");

	if (SOCKET_ERROR == send(socketClient, "我是客户端", sizeof("我是客户端"), 0))//发送成功会返回发送的字节个数
	{
		//发送失败
		printf("发送出错\n");
		int a = WSAGetLastError();//获取错误码
	}

	//循环一下
	while (1)
	{
		//第五步 与服务起收发消息
		char buf[1500] = { 0 };
		int res = recv(socketClient, buf, 1499, 0);
		if (res == 0)
		{
			printf("发送中断,服务器断开连接\n");
		}
		else if (res == SOCKET_ERROR)
		{
			//连接出错
			int a = WSAGetLastError();//获取错误码
			printf("接收出错\n");
		}
		else
		{
			printf("%d	%s\n", res, buf);
		}
		scanf("%s", buf);
		if (SOCKET_ERROR == send(socketClient, buf, strlen(buf), 0))//发送成功会返回发送的字节个数
		{
			//发送失败
			printf("发送出错\n");
			int a = WSAGetLastError();//获取错误码
		}
	}
	

	closesocket(socketClient);
	WSACleanup();

	system("pause");
	return 0;
}

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

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

相关文章

C#中的String和StringBuilder

一&#xff1a;前言 String和StringBuilder都是引用类型 StringBuilder是可变的字符串&#xff0c;它不会创建当前字符串的新修改实例而是在现有字符串对象中进行修改 String是不可变字符串&#xff0c;一旦被初始化后就不能改变其内容&#xff0c;String值改变的过程其实是创…

如何自定义注解

在 Spring Boot 应用中&#xff0c;使用自定义注解时需要用到 AOP&#xff0c;因此引入 AOP 相关依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependenc…

Python办公自动化|批量生成请假条

大家好&#xff0c;我是毕加锁 本文就将基于一个真实的办公案例进行讲解如何提取Excel内容并创建Word&#xff0c;主要将涉及以下三个知识点 “ openpyxl 读取 Excel 文件 python-docx 写入 Word 文件 python-docx 各类样式的设计和调整 ”需求描述 你是公司的底层小虾米&…

2022年AIGC简单展望

2022 对于社会是不平凡的一年&#xff0c;而对于科技也同样是不平凡的一年。人们在社会中遭受着失意&#xff0c;却在科技中寻找希冀。对于一个命运共同体&#xff0c;它想着如何破除衰退&#xff0c;而同样对于一个活生生的个体或者家庭&#xff0c;他们也在摸索改变命运的机遇…

SpringBoot拦截器HandlerInterceptor

一、HandlerInterceptor是什么&#xff1f; Spring Boot 拦截器主要应用于登陆校验、权限验证、乱码解决&#xff0c;同样提供了拦截器功能。 二、使用方式 在项目中实现HandlerInterceptor接口开箱即用 三、HandlerInterceptor 中实现的方法 方法介绍preHandle(HttpServletRe…

Linux操作系统之进程间的通讯—管道

文章目录进程间通讯&#xff08;IPC机制&#xff09;有哪几种方式&#xff1f;1、管道有名管道无名管道2、信号量进程间通讯&#xff08;IPC机制&#xff09;有哪几种方式&#xff1f; 管道、信号量、共享内存、消息队列、套接字 1、管道 什么是管道&#xff1f; 有名管道…

JavaScript 入门基础 - 变量 / 数据类型(二)

JavaScript 入门基础 - 变量 / 数据类型&#xff08;二&#xff09; 文章目录JavaScript 入门基础 - 变量 / 数据类型&#xff08;二&#xff09;1.变量1.1 什么是变量1.2 变量在内存中的存储1.3 变量的使用1.4 变量语法扩展1.4.1 更新变量1.4.2 声明多个变量1.4.3 声明变量的特…

node.js+uni计算机毕设项目个性化服务系统小程序(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

一文稿定 Appium 环境配置

Appium 是一个开源的、跨平台的测试框架&#xff0c;可以用来测试 Native App、混合应用、移动 Web 应用&#xff08;H5 应用&#xff09;等&#xff0c;也是当下互联网企业实现移动自动化测试的重要工具。Appium 坚持的测试理念&#xff1a; •无需用户对 App 进行任何修改或…

vue后台管理系统项目-vue-quill-editor实现富文本编辑器功能

富文本编辑器功能实现详细过程 目录 富文本编辑器功能实现详细过程 1.安装富文本插件 2.实现效果 3.实现详细过程 可直接使用 全局引入 局部引入 配置option 扩展需求 自定义配置文字大小 1.安装富文本插件 npm install vue-quill-editor --save //或者 yarn add vu…

AI城管自动识别摊贩占道经营出店经营行为

AI城管自动识别摊贩占道经营出店经营行为通过pythonyolov7对现场画面自动AI实时监测&#xff0c;当监测到流动商贩占道经营违规摆摊出店经营时时&#xff0c;立即告警。Python是一种由Guido van Rossum开发的通用编程语言&#xff0c;它很快就变得非常流行&#xff0c;主要是因…

论多窗口相互关联下window.open打开已在的窗口时只激活不刷新的实现方案

前端博主&#xff0c;热衷各种前端向的骚操作&#xff0c;经常想到哪就写到哪&#xff0c;如果有感兴趣的技术和前端效果可以留言&#xff5e;博主看到后会去代替大家踩坑的&#xff5e; 主页: oliver尹的主页 格言: 跌倒了爬起来就好&#xff5e; 来个关注吧&#xff0c;点个赞…

Springboot+Netty实现基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-设备终端(南向设备)

电信的天翼物联网平台CTWing(AIOT)原先是我们俗称的aep&#xff0c;主要用于接入nb-iot设备&#xff0c;当然也可以接入其他的设备&#xff0c;在熟悉AIOT平台后&#xff0c;做后端的我有时候急需终端样品(智能门禁&#xff0c;支付识别终端&#xff0c;CPE终端&#xff0c;考勤…

使用 Swift/SwiftUI 的音频可视化

IOS 应用程序中的音频可视化是一项流行的功能,在实现聊天功能时可能需要它。将它添加到我最近的项目之一时,我个人遇到了一些问题。 你们中的一些人可能在开发包含聊天功能的应用程序时遇到过问题,其中某些消息可能包含音频。这些消息需要在应用程序内进行适当的音频可视化,…

【算法题解】3. 颠倒二进制位

文章目录题目解法一解题思路代码实现复杂度分析解法二解题思路代码实现复杂度分析解法三解题思路代码实现复杂度分析题目 颠倒给定的 32 位无符号整数的二进制位。来自&#xff1a;leetcode 解法一 解题思路 取 n 的最低位&#xff0c;赋值给 ans 的最低位&#xff08;ans 初…

mybatis 多对一查询的处理方式,1.按照查询嵌套处理(子查询),2、按照结果嵌套处理(连表查询)

多对一查询 1、实体类 Student实体类&#xff1a; public class Student { private int id; private String name; private Teacher teacher;//对象属性}Teacheer 实体类&#xff1a; public class Teacher { private int id; private String name;2、Mybatis配置文件mybatis…

FastDDS(4)安装步骤

eProsima Fast DDS for Linux的最新二进制安装版本可在eProssma网站找到。 eProsima Fast DDSSpecialized on high performance middleware. Check out our comparatives such as Apache Thrift vs Protocol Buffers vs Fast Buffers or ZMQ vs Fast RTPS.https://www.eprosim…

从一道经典题来弄懂Eventloop(搞不懂算我输)

前言 时间不知不觉来到了11月底&#xff0c;马上也要准备一下寒假的实习了。 最近打算把面试中的一些拦路虎给解决掉&#xff01;&#xff01; 先拿臭名昭著的Eventloop开刀~ 经典题 async function foo() {console.log(foo) } async function bar() {console.log(bar start…

RabbitMQ:基本消息模型

单生产单消费模型&#xff0c;即基本消息模型或简单消费模型&#xff0c;即完成基本的一对一消息转发。 RabbitMQ 单生产单消费模型主要有以下三个角色构成&#xff1a; 生产者&#xff08;producer/ publisher&#xff09;&#xff1a;一个发送消息的用户应用程序。消费者&…

JS面试题--JavaScript数据类型

数据类型 1.JavaScript有哪些数据类型&#xff0c;它们的区别&#xff1f; JavaScript共有八种数据类型&#xff0c;分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。 其中 Symbol 和 BigInt 是ES6 中新增的数据类型&#xff1a; ● Symbol 代表…