TCP/IP网络编程-C++(上)
TCP/IP网络编程-C++ (上)
- 一、基于TCP的服务端/客户端
- 1、server端代码
- 2、client端代码
- 3、`socket()` 函数
- 3.1、函数原型
- 3.2、参数解析
- 3.2.1、协议族(domain参数)
- 3.2.2、套接字类型(type参数)
- 3.2.3、最终使用的协议(protocol参数)
- 4、`struct sockaddr_in` IPv4地址结构体
- 4.1、结构体原型
- 4.2、结构体成员分析
- 4.3、`struct sockaddr` 结构体
- 5、字节序转换
- 6、`bind()` 函数
- 6.1、字符串IP与网络字节序互相转换
- 6.1.1、`inet_addr()`
- 6.1.2、 `inet_aton()`
- 6.1.3、`inet_ntoa()`
- 6.2 向套接字分配网络地址bind()函数
- 6.2.1、函数原型:
- 6.2.2、参数解析:
- 7、`listen()` 函数 - 进入等待连接请求状态
- 7.1 函数原型:
- 7.2 参数解析:
- 8、`accept()` 函数 - 受理客户端连接请求
- 8.1 函数原型:
- 8.2 参数解析
- 9、`connect()`函数 - 向服务端发送连接请求
- 9.1 函数原型:
- 9.2 参数解析
- 9.3 客户端地址信息在哪里
- 10、基于TCP的服务端、客户端实现字符串转换
- 10.1 客户端代码实现:
- 10.2 服务端代码实现:
- 二、基于UDP的服务端/客户端
- 1、server端代码实现
- 2、client端代码实现
- 3、`sendto()` 函数 - 填写地址并传输数据的I/O函数
- 3.1 函数原型
- 3.2 参数解析
- 3.3 UDP客户端地址分配
- 4、`recvfrom()` 函数 - 接收数据
- 4.1 函数原型
- 4.2 参数解析
- 5、存在数据边界的UDP套接字
- 6、创建已连接UDP套接字
- 6.1 已连接UDP client端代码实现
一、基于TCP的服务端/客户端
- 先给出server端和client端代码,client向server发送请求,server接受请求并回复client消息,这里简单回复"hello world!",后面详细说明函数作用
1、server端代码
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <unistd.h>
#include <string.h>int main(void){int serv_sock = socket(PF_INET, SOCK_STREAM, 0); if(serv_sock == -1)std::cout<<"socket error\n";struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(8080);if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)std::cout<<"bind error\n";if(listen(serv_sock, 3) == -1)std::cout<<"listen error\n";struct sockaddr_in clie_addr;memset(&clie_addr, 0, sizeof(clie_addr));socklen_t clie_addr_size = 0;int clie_sock = accept(serv_sock, (struct sockaddr*) &clie_addr, &clie_addr_size);if(clie_sock == -1)std::cout<<"accept error\n";std::string message = "hello world!";send(clie_sock, message.c_str(), message.size(), 0);close(clie_sock);close(serv_sock);return 0;
}
2、client端代码
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <unistd.h>
#include <string.h>int main(void){int clie_sock = socket(PF_INET, SOCK_STREAM, 0);if(clie_sock == -1)std::cout<<"socket error\n";std::string server_ip = "127.0.0.1";struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());serv_addr.sin_port = htons(8080);if(connect(clie_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)std::cout<<"connect error\n";char message[30] = {0};if(recv(clie_sock, message,30, 0) == -1)std::cout<<"read error\n";std::string str_message(message);std::cout<<"message : "<<str_message<<"\n";close(clie_sock);return 0;
}
- 可以大概看看,不理解没关系,接下来会解释每一个函数的作用
3、socket()
函数
3.1、函数原型
socket(int domain, int type, int protocol); // 成功会返回文件描述符,失败返回-1
3.2、参数解析
- domain-协议族:套接字中使用的协议族信息。
- type-套接字类型:套接字数据传输类型信息。
- protocol-最终使用的协议:计算机间通信中使用的协议信息。
3.2.1、协议族(domain参数)
- 协议族就是协议的分类信息,在<sys/socket.h>中声明的协议分类信息如下表:
名称 | 协议族 |
---|---|
PF_INET | IPv4互联网协议族 |
PF_INET6 | IPv6互联网协议族 |
PF_LOCAL | 本地通信的UNIX协议族 |
PF_PACKET | 底层套接字的协议族 |
PF_IPX | IPX Novell协议族 |
3.2.2、套接字类型(type参数)
- 套接字类型指的是套接字数据传输方式,这里介绍两种类型,面向连接的套接字(TCP) 、面向消息的套接字(UDP)
- 1、
SOCK_STREAM
:表示使用面向连接的套接字,提供可靠的、按序传递的、基于自己的服务。 - 2、
SOCK_DGRAM
:表示使用面向消息的套接字,提供不可靠的、不按序传递的、以数据高速传输为目的的服务。
3.2.3、最终使用的协议(protocol参数)
- 最终使用的协议指的是同一协议族中存在多个数据传输方式相同的协议,在同一协议族中数据传输方式相同,但使用的协议不同,一般该参数传递0即可。
4、struct sockaddr_in
IPv4地址结构体
4.1、结构体原型
struct sockaddr_in
{sa_family_t sin_family; // 地址族uint16_t sin_port; // 16位TCP/UDP端口号struct in_addr sin_addr; // 32位IP地址char sin_zero[8]; // 不使用
}
// struct in_addr原型
struct in_addr
{In_addr_t s_addr; // 32位IP地址
}
4.2、结构体成员分析
sin_family
:每种协议使用的地址族均不同,比如IPv4使用4字节地址族,IPv6使用16字节地址族。常用的地址族如下表:
地址族 | 含义 |
---|---|
AF_INET | IPv4网络中使用的地址族 |
AF_INET6 | IPv6网络中使用的地址族 |
AF_LOCAL | 本地通信中采用的UNIX协议的地址族 |
sin_port
:保存16位端口号sin_addr
:保存32位IP地址信息sin_zero
:没有特殊含义,为了使得sockaddr_in与sockaddr大小保持一致而插入的成员,(sockaddr后面介绍),填充位0即可
4.3、struct sockaddr
结构体
- 在调用bind()函数时是将sockaddr_in转换为sockaddr进行传入的,因为bind()函数要求的参数类型是sockaddr,但为什么不直接定义sockaddr进行传入呢,而是定义sockaddr_in转换为sockaddr呢?如下sockaddr结构体原型:
struct sockaddr
{sa_family_t sin_family; // 地址族char sa_data[14]; //地址信息
}
- 因为最终IP地址信息和端口号会保存到sa_data成员中,直接想sa_data中填写信息比较麻烦,所以就有了sockaddr_in,使用起了更方便。
5、字节序转换
- 由于CPU存储数据分为大端序和小端序,但是网络数据传输中同一使用大端序,所以下面介绍字节序转换函数
- (1)
uint32_t ntohl (uint32_t __netlong)
:将uint32_t类型数据从网络字节序转换为主机字节序 - (2)
uint16_t ntohs (uint16_t __netshort)
:将uint16_t类型数据从网络字节序转换为主机字节序 - (3)
uint32_t htonl (uint32_t __hostlong)
:将uint32_t类型数据从主机字节序转换为网络字节序 - (4)
uint16_t htons (uint16_t __hostshort)
:将uint16_t类型数据从主机字节序转换为网络字节序 - 只需要在想sockaddr_in结构体填充数据时需要进行转换,其它情况不需要转换字节序,这都是自动完成的。当然sockaddr_in中的sin_family也不需要进行字节序转换,因为sin_family 并不会发送到网络上(详细信息自行了解)。
6、bind()
函数
6.1、字符串IP与网络字节序互相转换
6.1.1、inet_addr()
- 作用:将字符串类型IP转换为转换为整数并返回,示例如下:
#include <arpa/inet.h>
#include <string>
int main(void)
{std::string str_ip = "127.0.0.1";uint32_t u_ip = inet_addr(str_ip.c_str()); // 返回的结果u_ip可以直接赋值给sockaddr_in中的sin_addr.s_addrif(u_ip == INADDR_NONE){std::cout<<"无效ip"<<std::endl;}return 0;
}
6.1.2、 inet_aton()
- 作用:将字符串类型IP转换为转换为整数同时将结果直接写入sockaddr_in中的sin_addr,调用成功返回true(1),失败返回false(0),示例如下:
#include <arpa/inet.h>
#include <string>
int main(void)
{std::string str_ip = "127.0.0.1";struct sockaddr_in addr;int ret = inet_aton(str_ip.c_str(), &addr.sin_addr); if(ret == 0){std::cout<<"无效ip"<<std::endl;}return 0;
}
6.1.3、inet_ntoa()
- 作用:将网络字节序的IP转换为字符串,成功是返回字符串IP,失败是返回-1,示例如下:
#include <arpa/inet.h>
#include <string>
int main(void)
{struct sockaddr_in addr;addr.sin_addr.s_addr = htonl(0x1020304);std::string str_ip = inet_ntoa(addr.sin_addr); std::cout<<str_ip<<std::endl;return 0;
}
6.2 向套接字分配网络地址bind()函数
6.2.1、函数原型:
int bind (int __fd, const struct sockaddr * __addr, socklen_t __len);
// 成功返回0,失败返回-1
6.2.2、参数解析:
参数 | 参数说明 |
---|---|
__fd | 要分配地址信息的套接字文件描述符,socket()函数返回的文件描述符 |
__addr | struct sockaddr_in IPv4地址结构信息 |
__len | 参数2的长度 |
7、listen()
函数 - 进入等待连接请求状态
7.1 函数原型:
int listen(int sock, int backlog);// 成功时返回0, 失败时返回-1
7.2 参数解析:
参数 | 参数说明 |
---|---|
sock | 套接字文件描述符 |
backlog | 连接请求等待队列的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列 |
- 所谓的等待连接请求状态是指客户端请求连接时,在受理连接前一直使请求处于等待连接状态
8、accept()
函数 - 受理客户端连接请求
8.1 函数原型:
int accept(int sock, struct sockaddr* addr, socklen_t* addrlen);// 成功时返回套接字文件描述符,失败时返回-1
8.2 参数解析
参数 | 参数说明 |
---|---|
sock | 套接字文件描述符 |
addr | 保存发起连接请求的客户端地址信息 |
addrlen | 保存第二个参数的地址长度 |
9、connect()
函数 - 向服务端发送连接请求
9.1 函数原型:
int connect(int sock, struct sockaddr* servaddr, socklen_t* addrlen);// 成功时返回0,失败时返回-1
9.2 参数解析
参数 | 参数说明 |
---|---|
sock | 客户端套接字文件描述符 |
servaddr | 目标服务器端地址信息 |
addrlen | 第二个参数的长度 |
9.3 客户端地址信息在哪里
- 客户端调用 connect() 函数函数后,发生一下两种情况才会完成函数调用:
(1)、服务端接收到连接请求
(2)、发生断网等异常情况而中断连接请求 - 需要注意的是,连接请求并不意味着服务端调用 accept() 函数,其实是服务端把连接请求信息记录在等待队列中,因此 connect() 函数返回后并不立即进行数据交换。
- 实现服务端必经过程之一就是给套接字分配IP和端口号,但在客户端实现过程中并没有为套接字分配IP和端口号,而是在创建套接字后就立即调用了connect()函数。难道客户端套接字不需要IP和端口号吗?当然不是,学过计算机网络都知道网络中数据交换必须要IP和端口号,既然如此,那客户端套接字何时?何地?如何分配地址呢?
(1)、何时:在调用connect()函数时
(2)、何地:操作系统内核中自动完成
(3)、如何:IP使用主机的IP,端口随机分配 - 因此,客户端IP和端口号是在调用connect()函数时自动分配,无需使用bind()函数进行分配。
10、基于TCP的服务端、客户端实现字符串转换
- 我们已经学完了基础函数,前面我们给出的示例是很简单的调用流程,即客户端发送请求,服务端接受请求并发送了“hello world”的信息,然后各自关闭了连接。那服务端如何循环的接收并处理客户端的请求呢?客户端如何不断的向服务端发送请求呢?接下里我们实现一个简单的字符串转换功能,即客户端向服务端发送字符串,服务端接收到字符串后将字符串中大写字符转换为小写并返回给客户端。
10.1 客户端代码实现:
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <unistd.h>
#include <string.h>
const int BUFFER_SIZE = 128;int main(void){int clie_sock = socket(PF_INET, SOCK_STREAM, 0);if(clie_sock == -1)std::cout<<"socket error\n";std::string server_ip = "127.0.0.1";struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());serv_addr.sin_port = htons(8080);if(connect(clie_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)std::cout<<"connect error\n";while(true){std::string str_input = "";std::cout<<"please input:";std::cin>>str_input;if(str_input == "q")break;send(clie_sock, str_input.c_str(), str_input.size(), 0);char message[BUFFER_SIZE] = {0};int recv_size = 0;if((recv_size = recv(clie_sock, message, BUFFER_SIZE - 1, 0)) == -1)std::cout<<"read error\n";message[recv_size] = '\0';std::string str_message(message, recv_size);std::cout<<"before:"<<str_input<<"\n";std::cout<<"later:"<<str_message<<"\n";}close(clie_sock);return 0;
}
10.2 服务端代码实现:
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <unistd.h>
#include <string.h>
#include <regex>
#include <cctype>
const int BUFFER_SIZE = 128;void to_lower(const std::string& str_input, std::string& str_output){std::regex pattern("^[a-zA-Z]+$");bool is_letters = std::regex_match(str_input, pattern);if(is_letters){str_output.resize(str_input.size());std::transform(str_input.begin(), str_input.end(), str_output.begin(), ::tolower);}else{str_output = "包含其它字符,转换失败!";}return;
}int main(void){int serv_sock = socket(PF_INET, SOCK_STREAM, 0); if(serv_sock == -1)std::cout<<"socket error\n";struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(8080);if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)std::cout<<"bind error\n";if(listen(serv_sock, 3) == -1)std::cout<<"listen error\n";struct sockaddr_in clie_addr;memset(&clie_addr, 0, sizeof(clie_addr));socklen_t clie_addr_size = 0;int clie_sock = 0;clie_sock = accept(serv_sock, (struct sockaddr*) &clie_addr, &clie_addr_size);if(clie_sock == -1)std::cout<<"accept error\n";while(true){char mess[BUFFER_SIZE] = {0};int recv_size = 0;if((recv_size = recv(clie_sock, mess, BUFFER_SIZE - 1, 0)) == -1) {std::cout<<"recv error\n";}mess[recv_size] = '\0';std::string str_message(mess, recv_size);std::string str_result = "";to_lower(str_message, str_result);send(clie_sock, str_result.c_str(), str_result.size(), 0);}close(clie_sock);close(serv_sock);return 0;
}
二、基于UDP的服务端/客户端
- UDP服务端/客户端不像TCP那样在连接状态下交换数据,因此与TCP不同,无需经过连接过程。也就是说不必调用TCP连接过程中的listen()函数和accept()函数,UPD只有 创建套接字 的过程和 数据交换 的过程
- UPD不同于TCP,不存在请求连接和受理过程,因此在某种意义上无法明确区分服务端和客户端,只能因提供服务的一方称为服务端。
- 下面使用UDP方式实现 字符串转换 的例子
1、server端代码实现
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <unistd.h>
#include <string.h>
#include <regex>
#include <cctype>
const int BUFFER_SIZE = 128;void to_lower(const std::string& str_input, std::string& str_output){std::regex pattern("^[a-zA-Z]+$");bool is_letters = std::regex_match(str_input, pattern);if(is_letters){str_output.resize(str_input.size());std::transform(str_input.begin(), str_input.end(), str_output.begin(), ::tolower);}else{str_output = "包含其它字符,转换失败!";}return;
}int main(void){int serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if(serv_sock == -1)std::cout<<"socket error\n";struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(8080);if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)std::cout<<"bind error\n";struct sockaddr_in clie_addr;memset(&clie_addr, 0, sizeof(clie_addr));socklen_t clie_addr_size = 0;int clie_sock = 0;while(true){char message[BUFFER_SIZE] = {0};int mess_len = recvfrom(serv_sock, message, BUFFER_SIZE, 0, (struct sockaddr*)&clie_addr, &clie_addr_size);message[mess_len] = '\0';std::string str_input(message, mess_len);std::string str_output = "";to_lower(str_input, str_output);sendto(serv_sock, str_output.c_str(), str_output.size(), 0, (struct sockaddr*)&clie_addr, clie_addr_size);}close(serv_sock);return 0;
}
2、client端代码实现
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <unistd.h>
#include <string.h>
const int BUFFER_SIZE = 128;int main(void){int clie_sock = socket(PF_INET, SOCK_DGRAM, 0);if(clie_sock == -1)std::cout<<"socket error\n";std::string server_ip = "127.0.0.1";struct sockaddr_in serv_addr, from_addr;socklen_t from_addr_size = 0;memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());serv_addr.sin_port = htons(8080);while(true){std::string str_input = "";std::cout<<"please input:";std::cin>>str_input;if(str_input == "q")break;sendto(clie_sock, str_input.c_str(), str_input.size(), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));char message[BUFFER_SIZE] = {0};int mess_len = recvfrom(clie_sock, message, BUFFER_SIZE, 0, (struct sockaddr*)&from_addr, &from_addr_size);message[mess_len] = '\0';std::string str_message(message, mess_len);std::cout<<"before:"<<str_input<<"\n";std::cout<<"later:"<<str_message<<"\n";}close(clie_sock);return 0;
}
3、sendto()
函数 - 填写地址并传输数据的I/O函数
3.1 函数原型
ssize_t sendto(int sock, void* buff, size_t nbytes, int flags, struct sockaddr* to, socklen_t addrlen);// 成功返回传输的字节数,失败返回-1
3.2 参数解析
参数 | 参数解析 |
---|---|
sock | 用于传输数据的UDP套接字文件描述符 |
buff | 将要传输数据的地址 |
nbytes | 将要传输数据的长度,以字节为单位 |
flags | 可选项,没有传递0即可 |
to | 存有目标地址信息的sockaddr结构体变量 |
addrlen | to参数的长度 |
3.3 UDP客户端地址分配
- 在TCP中客户端是通过connect()函数字符为客户端分配地址的,那UDP中如何分配地址呢?是在 sendto() 函数中进行地址分配的,如果调用sendto()函数时发现没有分配地址信息,则在首次调用时给相应套接字自动分配IP和端口号,IP是主机IP,端口号随机分配。而且此时分配的地址信息回一直保留到程序结束,因此也可以与其它UDP进行数据交换。
4、recvfrom()
函数 - 接收数据
4.1 函数原型
ssize_t recvfrom(int sock, void* buff, size_t nbytes, int flags, sockaddr* from, socklen_t* addrlen);// 成功时返回接收的字节数,失败返回-1
4.2 参数解析
参数 | 参数解析 |
---|---|
sock | 用于接收数据的UDP套接字文件描述符 |
buff | 接受到数据的缓冲地址 |
nbytes | 可接收的最大字节数,不能超过buff所指的缓冲大小 |
flags | 可选项,没有传递0即可 |
from | 保存发送端的地址信息 |
addrlen | 保存from参数长度的变量的地址值 |
5、存在数据边界的UDP套接字
- UDP是具有数据边界的协议,在传输过程中调用I/O函数的次数非常重要。因此,输入函数和输出函数的调用次数应该完全一致,这样才能保证接收全部已发送数据。比如客户端调用了3次sendto()函数,那么服务端也必须调用3次recvfrom()函数才可以接收客户端3次发送的信息。这和TCP不同,TCP客户端调用3次send()函数,那么服务端可以只调用1次recv()函数就可以接收3次发送的信息。
6、创建已连接UDP套接字
- TCP套接字中需要注册待传输数据的目标IP和端口号,而UDP中则无需注册,因此,通过sendto()函数传输数据的过程大致分为3个阶段:
(1)、第一阶段:向UDP套接字注册目标IP和端口号
(2)、第二阶段:传输数据
(3)、第三阶段:删除UDP套接字中注册的目标地址信息 - 每次调用sendto函数时重复上述过程。每次都变更目标地址,因此可以重复利用同一UDP套接字向不同目标传输数据。这种未注册目标地址信息的套接字称为未连接套接字,反之,注册了目标地址的套接字称为连接connected套接字。显然,UDP套接字默认属于未连接套接字。但UDP套接字在下述情况下显得不太合理: “IP为211.210.147.82的主机82号端口共准备了3个数据,调用3次sendto函数进行传输。”此时需重复3次上述三阶段。因此,要与同一主机进行长时间通信时,将UDP套接字变成已连接套接字会提高效率。上述三个阶段中,第一个和第三个阶段占整个通信过程近1/3的时间,缩短这部分时间将大大提高整体性能。
- 创建已连接UDP套接字的过程格外简单,只需针对UDP套接字调用connect()函数即可,当然,针对UDP套接字调用connect()函数并不意味着要与对方套接字连接,这只是向UDP套接字注册目标IP和端口号。之后每次调用sendto()函数只需要传输数据,因为已经指定了收发对象,所以此时不仅可以使用sendto()、recvfrom()函数,还可以使用send()和recv()函数。
- 下面给出已连接UDP套接字client端代码实现,服务端没有变化。
6.1 已连接UDP client端代码实现
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <unistd.h>
#include <string.h>
const int BUFFER_SIZE = 128;int main(void){int clie_sock = socket(PF_INET, SOCK_DGRAM, 0);if(clie_sock == -1)std::cout<<"socket error\n";std::string server_ip = "127.0.0.1";struct sockaddr_in serv_addr, from_addr;socklen_t from_addr_size = 0;memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());serv_addr.sin_port = htons(8080);if(connect(clie_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)std::cout<<"connect error\n";while(true){std::string str_input = "";std::cout<<"please input:";std::cin>>str_input;if(str_input == "q")break;// sendto(clie_sock, str_input.c_str(), str_input.size(), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));send(clie_sock, str_input.c_str(), str_input.size(), 0);char message[BUFFER_SIZE] = {0};// int mess_len = recvfrom(clie_sock, message, BUFFER_SIZE, 0, (struct sockaddr*)&from_addr, &from_addr_size);int mess_len = recv(clie_sock, message, BUFFER_SIZE - 1, 0);message[mess_len] = '\0';std::string str_message(message, mess_len);std::cout<<"before:"<<str_input<<"\n";std::cout<<"later:"<<str_message<<"\n";}close(clie_sock);return 0;
}
- 已连接UDP客户端比未连接UDP客户端只多了connect()函数调用,而且已连接UDP客户端中还可以使用send()和recv()函数进行数据交换,其它部分并没有什么不同。
相关文章:
TCP/IP网络编程-C++(上)
TCP/IP网络编程-C (上) 一、基于TCP的服务端/客户端1、server端代码2、client端代码3、socket() 函数3.1、函数原型3.2、参数解析3.2.1、协议族(domain参数)3.2.2、套接字类型(type参数)3.2.3、最终使用的协…...
在线绘制Nature Communication同款双色、四色火山图,突出感兴趣的基因
导读:火山图通常使用三种颜色分别表示显著上调,显著下调和不显著。通过为特定的数据点添加另一种颜色,可以创建双色或四色火山图,从而更直观地突出感兴趣的数据点。 《Nature Communication》文章“Molecular and functional land…...
ctfshow pwn wp
文章目录 Test_your_ncpwn-0pwn1pwn2pwn3pwn4 前置基础pwn5pwn6pwn7pwn8pwn9pwn10pwn11pwn12pwn13:gcc编译执行C语言代码pwn14:gcc编译执行C语言代码pwn15:nasm编译asm,ld链接为可执行文件pwn16:gcc编译汇编文件.s为可…...
【数据结构实战篇】用C语言实现你的私有队列
🏝️专栏:【数据结构实战篇】 🌅主页:f狐o狸x 在前面的文章中我们用C语言实现了栈的数据结构,本期内容我们将实现队列的数据结构 一、队列的概念 队列:只允许在一端进行插入数据操作,在另一端…...
数据结构 (11)串的基本概念
一、串的定义 1.串是由一个或者多个字符组成的有限序列,一般记为:sa1a2…an(n≥0)。其中,s是串的名称,用单括号括起来的字符序列是串的值;ai(1≤i≤n)可以是字母、数字或…...
快速高效求素数|质数的方法—Java(模板)
判断素数|质数方法时间效率:线性筛法>埃氏筛法>试除法 在写算法题的时候,各种各样跟素数有关的题目非常常见,本文列出了三种常见的判断素数的方法 三种求素数方法的优缺点 一、试除法 试除法的基本思想是:判断一个数 x 是否为素数&…...
探秘嵌入式位运算:基础与高级技巧
目录 一、位运算基础知识 1.1. 位运算符 1.1.1. 与运算(&) 1.1.2. 或运算(|) 1.1.3. 异或运算(^) 1.1.4. 取反运算(~) 1.1.5. 双重按位取反运算符(~~…...
iOS 17.4 Not Installed
0x00 系统警告 没有安装 17.4 的模拟器,任何操作都无法进行! 点击 OK 去下载,完成之后,依旧是原样! 0x01 解决办法 1、先去官网下载对应的模拟器: https://developer.apple.com/download/all/?q17.4 …...
RestTemplate 使用教程
RestTemplate 是 Spring 框架提供的一种用于执行HTTP请求的同步客户端。它简化了与HTTP服务器的交互,并支持RESTful Web服务。 1. 添加依赖 首先,确保你的项目中包含了Spring Web的支持。如果你使用的是Maven,在pom.xml文件中添加如下依赖&…...
windows下安装wsl的ubuntu,同时配置深度学习环境
写在前面,本次文章只是个人学习记录,不具备教程的作用。个别信息是网上的,我会标注,个人是gpt生成的 安装wsl 直接看这个就行;可以不用备份软件源。 https://blog.csdn.net/weixin_44301630/article/details/1223900…...
【贪心算法第五弹——300.最长递增子序列】
目录 1.题目解析 题目来源 测试用例 2.算法原理 3.实战代码 代码解析 注意本题还有一种动态规划的解决方法,贪心的方法就是从动态规划的方法总结而来,各位可以移步博主的另一篇博客先了解一下:动态规划-子序列问题——300.长递增子序列…...
【数据分析】基于GEE解析2000-2020年武汉市FVC时空变化特征
武汉市FVC时空变化特征解析 1. 写在前面2. 2000~2020年武汉市FVC时空变化特征解析2.1. 数据获取与预处理2.2. 辐射定标和大气校正2.3. 云层和云影去除2.4. FVC计算2.5. 时空分析2.6. 代码1. 写在前面 🌍✨在应对全球气候变化和环境监测的挑战中,植被盖度(Fraction Vegetati…...
springboot实战(19)(条件分页查询、PageHelper、MYBATIS动态SQL、mapper映射配置文件、自定义类封装分页查询数据集)
引言: 该类博客的学习是基于b站黑马视频springbootvue视频学习!具体围绕项目——"大事件"进行实战学习。 目录 一、功能介绍(需求)。 1、文章列表功能基本介绍。 2、条件分页查询功能与注意。 3、前端页面效果。&#x…...
Mongo数据库 --- Mongo Pipeline
Mongo数据库 --- Mongo Pipeline 什么是Mongo PipelineMongo Pipeline常用的几个StageExplanation with example:MongoDB $matchMongoDB $projectMongoDB $groupMongoDB $unwindMongoDB $countMongoDB $addFields Some Query Examples在C#中使用Aggreagtion Pipeline**方法一: …...
Docker部署mysql:8.0.31+dbsyncer
Docker部署mysql8.0.31 创建本地mysql配置文件 mkdir -p /opt/mysql/log mkdir -p /opt/mysql/data mkdir -p /opt/mysql/conf cd /opt/mysql/conf touch my.config [mysql] #设置mysql客户端默认字符集 default-character-setUTF8MB4 [mysqld] #设置3306端口 port33…...
银河麒麟桌面系统——桌面鼠标变成x,窗口无关闭按钮的解决办法
银河麒麟桌面系统——桌面鼠标变成x,窗口无关闭按钮的解决办法 1、支持环境2、详细操作说明步骤1:用root账户登录电脑步骤2:导航到kylin-wm-chooser目录步骤3:编辑default.conf文件步骤4:重启电脑 3、结语 Ὁ…...
【微服务】 Eureka和Ribbon
一、Eureka 服务调用出现的问题:在远程调用另一个服务时,我们采用的解决办法是发送一次http请求,每次环境的变更会产生新的地址,所以采用硬编码会出现很多麻烦,并且为了应对并发问题,采用分布式部署&#…...
C++设计模式(工厂模式)
一、介绍 1.动机 在软件系统中,经常面临着创建对象的工作,这些对象有可能是一系列相互依赖的对象;由于需求的变化,需要创建的对象的具体类型经常变化,同时也可能会有更多系列的对象需要被创建。 如何应对这种变化&a…...
nodejs第三方库sharp对图片的操作生成新图片、压缩、添加文字水印及图片水印等
Sharp是一个基于libvips的高性能Node.js图像处理库,它提供了广泛的功能,包括调整大小、裁剪、旋转、格式转换等。Sharp可以处理多种图像格式,并且能够高效地转换图像格式。 相关说明及用法看:https://sharp.nodejs.cn/ 安装&#…...
uniapp-vue2引用了vue-inset-loader插件编译小程序报错
报错信息 Error: Vue packages version mismatch: - vue3.2.45 (D:\qjy-myApp\admin-app\node_modules\vue\index.js) - vue-template-compiler2.7.16 (D:\qjy-myApp\admin-app\node_modules\vue-template-compiler\package.json) This may cause things to work incorrectly.…...
计算机的错误计算(一百六十七)
摘要 将计算机的错误计算(一百六十六)中算式的分母有理化,然后再在 MATLAB 讨论其计算精度。本节说明,MATLAB 的输出与(一百六十六)的输出几乎一致,有效数字的错误率也相同。 例1. 探讨 …...
交叉编译openSSH
升级原系统中的SSHd服务 由于原系统自带的SSH版本过低存在漏洞风险,所以需要升级新版本的SSH 交叉编译前准备 需要下面三个库,都是开源的,去对应的网站下载即可 openssh-9.6p1.tar.gz openssl-1.1.1w.tar.gz zlib-1.3.1.tar.gz 这里没有…...
从零开始学GeoServer源码(二)添加支持arcgis切片功能
文章目录 参考文章环境背景1、配置打包好的程序1.1、下载GeoServer的war包1.2、下载GeoWebCache1.3、拷贝jar包1.4、修改配置文件1.4.1、拷贝geowebcache-arcgiscache-context.xml1.4.2、修改geowebcache-core-context.xml1.4.3、修改geowebcache-servlet.xml 1.5、配置切片信息…...
Android OTA 更新面试题及参考答案
什么是 OTA 更新? OTA 更新即空中下载技术(Over-the-Air Technology)更新,是一种通过无线网络对移动设备的系统软件或应用程序进行远程更新的技术手段 。 其原理是设备通过移动网络或 Wi-Fi 连接到服务器,服务器检测设…...
搜维尔科技:通过Touch力反馈主手实时通讯机械臂,进行远程操作
通过Touch力反馈主手实时通讯机械臂,进行远程操作 搜维尔科技:通过Touch力反馈主手实时通讯机械臂,进行远程操作...
Java ArrayList 与顺序表:在编程海洋中把握数据结构的关键之锚
我的个人主页 我的专栏:Java-数据结构,希望能帮助到大家!!!点赞❤ 收藏❤ 前言:在 Java编程的广袤世界里,数据结构犹如精巧的建筑蓝图,决定着程序在数据处理与存储时的效率、灵活性以…...
React中事件处理和合成事件:理解与使用
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...
在Vue3项目中引入省市区联动插件
1. 打开HBuilder X 图1 2. 新建一个空项目 文件->新建->项目->uni-app 填写项目名称:vue3demo 选择项目存放目录:D:/HBuilderProjects 一定要注意vue的版本,当前选择的版本为vue3 图2 点击“创建”之后进入项目界面 图3 其中各文件…...
动态内存管理(c语言)
我们通常开辟空间的方式 int val 20; //大小为4个字节 char arr[10] {0} //开辟出一块连续的空间且大小为10 但是上面开辟空间方式的特点 1.空间开辟大小是固定的 2.数组在声明得时候,必须指定数组得长度,它所需要得内存在编译时分配 但是以上的方式不能…...
SLF4J日志快速上手
tags: SpringBoot 日志 快速上手 SLF4J日志快速上手 第一步:添加日志相关配置 logging:level:root: INFO # 设置全局日志级别为 INFO,只显示重要的日志信息(略过 DEBUG 和 TRACE 级别)# org.hibernate.SQL: DEBUG # 对于 Hiber…...
【PyTorch】(基础三)---- 图像读取和展示
图像读取和展示 pytorch本身并不提供图像的读取和展示功能,利用pytorch执行计算机视觉任务的时候,通常是利用opencv等工具先进行图像处理,然后将结果转化成tensor类型传递给pytorch,在pytorch执行之后,也可以将tensor…...
【纪念365天】我的创作纪念日
过去的一年 没有注意加入csdn已经有一年了。 这几天翻看小猴儿的通知才发现时间来到了一年的纪念日。稍稍思索想要将这一段时间的学习到的知识以及偶然遇到的机遇做一下总结。 上一次写纪念日是来到csdn128天的时候, 200天前我的学习状态是非常疯狂的。 只记得我当时…...
flink学习(7)——window
概述 窗口的长度(大小): 决定了要计算最近多长时间的数据 窗口的间隔: 决定了每隔多久计算一次 举例:每隔10min,计算最近24h的热搜词,24小时是长度,每隔10分钟是间隔。 窗口的分类 1、根据window前是否调用keyBy分为键控窗口和非键控窗口…...
第十六届蓝桥杯模拟赛第二期题解—Java
第十六届蓝桥杯模拟赛/校赛第二期个人题解,有错误的地方欢迎各位大佬指正 问题一(填空题) 【问题描述】 如果一个数 p 是个质数,同时又是整数 a 的约数,则 p 称为 a 的一个质因数。 请问, 2024 的最大的质因数是多少? …...
数据库-MySQL-Dynamic-Datasource源码解析
文章目录 前言一、简介二、整体流程三、核心解析四、总结 前言 多数据源的应用在日常项目中也是很常见的场景。 dynamic-datasource的功能,用起来的确很方便,只需要一个DS注解,加上一些简单的配置即可完成多数据源的切换。究竟是怎么做到的…...
uniapp+vue2+uview2.0导航栏组件二次封装
样式 代码 <template><view class"navBar"><u-navbar :title"title" :titleColor"titleColor" :bgColor"bgColor" :safeAreaInsetTop"safeAreaInsetTop":autoBack"true" leftClick"leftClic…...
【Git 工具】用 IntelliJ IDEA 玩转 Git 分支与版本管理
文章目录 一、使用 IDEA 配置和操作 Git1.1 查看 Idea 中的 Git 配置1.2 克隆 Github 项目到本地 二、版本管理2.1 提交并推送修改2.2 拉取远程仓库2.3 查看历史2.4 版本回退 三、分支管理3.1 新建分支3.2 切换分支3.2 合并分支3.4 Cherry-Pick 参考资料 一、使用 IDEA 配置和操…...
异或-java-leetcode
1486.数组异或操作 给你两个整数,n 和 start 。 数组 nums 定义为:nums[i] start 2*i(下标从 0 开始)且 n nums.length 。 请返回 nums 中所有元素按位异或(XOR)后得到的结果。 示例 1: 输入…...
Jmeter中的测试片段和非测试原件
1)测试片段 1--测试片段 功能特点 重用性:将常用的测试元素组合成一个测试片段,便于在多个线程组中重用。模块化:提高测试计划的模块化程度,使测试计划更易于管理和维护。灵活性:可以通过模块控制器灵活地…...
NeurIPS 2024 有效投稿达 15,671 篇,数据集版块内容丰富
NeurIPS,全称 Neural Information Processing Systems Conference,是神经信息处理系统的年度学术会议。该会议始于 1987 年,当时名为 NIPS。随着人工智能领域的快速发展,其影响力逐渐扩大,被越来越多的研究者和企业关注…...
力扣101. 对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称。 提示: 树中节点数目在范围 [1, 1000] 内-100 < Node.val < 100 进阶:你可以运用递归和迭代两种方法解决这个问题吗? 代码: /*** Definition for a binary …...
Ubuntu 环境下的 C/C++ 编译与调试配置
详细教学文档:Ubuntu 环境下的 C/C 编译与调试配置 本文档将手把手引导你完成在 Ubuntu 环境中设置 VS Code 的 tasks.json 和 launch.json,实现以下目标: 统一管理输出文件:将所有编译生成的可执行文件统一存放到项目的 build …...
『VUE』36. Vue的应用概念(分析流程)
目录 创建应用挂载应用可执行文件公共资源总结 欢迎关注 『VUE』 专栏,持续更新中 欢迎关注 『VUE』 专栏,持续更新中 创建应用 首先引入createApp ,然后借助createApp 创造一个app对象 main.js import { createApp } from "vue"; import A…...
《基于FPGA的便携式PWM方波信号发生器》论文分析(三)——数码管稳定显示与系统调试
一、论文概述 基于FPGA的便携式PWM方波信号发生器是一篇由任青颖、庹忠曜、黄洵桢、李智禺和张贤宇 等人发表的一篇期刊论文。该论文主要研究了一种新型的信号发生器,旨在解决传统PWM信号发生器在移动设备信号调控中存在的精准度低和便携性差的问题 。其基于现场可编…...
mac上的建议xftp 工具
mac上的建议xftp 工具 最近使用mac比较频繁了,但是第一次重度使用mac里面有很多的工具都是新的,有的window版本的工具无法使用。 xftp 的平替 Cyberduck 从它的官网上下载是免费的,但是如果使用 Apple store 要花费198呢。这不就剩下一大笔…...
Hive | Hive 表如何查看所有分区
文章目录 概述使用 SHOW PARTITIONS 命令查看特定分区的信息获取详细的分区信息总结 概述 Apache Hive 是一个构建在 Hadoop 之上的数据仓库工具,它提供了 SQL 类似的查询语言(称为 HiveQL),使得用户能够更容易地进行大数据处理和…...
MySQL系列之数据类型(Numeric)
导览 前言一、数值类型综述二、数值类型详解1. NUMERIC1.1 UNSIGNED或SIGNED1.2 数据类型划分 2. Integer类型取值和存储要求3. Fixed-Point类型取值和存储要求4. Floating-Point类型取值和存储要求 结语精彩回放 前言 MySQL系列最近三篇均关注了和我们日常工作或学习密切相关…...
4.6 JMeter HTTP信息头管理器
欢迎大家订阅【软件测试】 专栏,开启你的软件测试学习之旅! 文章目录 前言1 HTTP信息头管理器的位置2 常见的HTTP请求头3 添加 HTTP 信息头管理器4 应用场景 前言 在 JMeter 中,HTTP信息头管理器(HTTP Header Manager)…...
fpga 综合与优化设计
目录 Quartus Prime优化设置 1. 分析与综合设置 2. 物理综合优化 3. 适配设置 描述方法对综合的影响 1. 操作符的应用差异 2. 条件语句和分支语句的应用差异 3. 描述方式对综合电路的影响 优化设计方法 1. FPGA设计的基本原则 2. 常用优化设计方法 优化主要包括 4 个…...
计算机毕业设计Hadoop+Spark音乐推荐系统 音乐预测系统 音乐可视化大屏 音乐爬虫 HDFS hive数据仓库 机器学习 深度学习 大数据毕业设计
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...