全景网站怎么做/外包seo服务口碑好
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、最终使用的协…...

React Hooks中use的细节
文档 useState useState如果是以函数作为参数,那要求是一个纯函数,不接受任何参数,同时需要一个任意类型的返回值作为初始值。 useState可以传入任何类型的参数作为初始值,当以一个函数作为参数进行传入的时候需要注意ÿ…...

通信网络安全分层及关键技术解决
要实现信息化,就必须重视信息网络安全。信息网络安全绝不仅是IT行业的问题,而是一个社会问题,是一个包括多学科的系统安全工程问题,并直接关系到国家安全。因此,知名安全专家沈昌祥院士呼吁,要像重视两弹一…...

C++ 面向对象包含哪些设计原则
设计模式是由设计原则迭代出来的 开闭原则:一个类应该对扩展开放,对修改关闭 稳定的部分稳定住,变化的部分扩展 扩展可以通过继承和组合 相关原则:单一职责原则、里氏替换原则、接口隔离原则 单一职责原则:一个类应该…...

微信小程序首页搜索框的实现教程
微信小程序首页搜索框的实现教程 前言 在现代移动应用中,搜索功能是用户获取信息的主要方式之一。对于购物小程序而言,提供一个美观且高效的搜索框,可以显著提升用户体验,帮助用户快速找到他们想要的商品。本文将详细介绍如何在微信小程序中实现一个样式优美的搜索框,包…...

android集成FFmpeg步骤以及常用命令,踩坑经历
1、入坑第一步:首先集成的库必须正确。最好是有ndk的,FFmpeg有许多个版本,我才开始接触的时候随便选了一个,一般的 方法没有问题。但是涉及到需要使用libx264等条件进行编码时,老是报错,网上搜索资料也没有…...

Go错误与日志处理—推荐实践
错误的分类 在 Go 语言中,错误是通过实现 error 接口的类型表示的,但不同场景下的错误可以按性质和用途进行分类。以下是 Go 语言错误的常见分类,以及每类错误的解释和示例: 标准错误类型 标准库中定义了许多常见的错误类型&…...

Android 13 Aosp Settings Android Studio版本
Android 13 Aosp Settings Android Studio版本 Settings相关源码 Settings https://android.googlesource.com/platform/packages/apps/Settings/+/refs/heads/android13-release SettingsIntelligence https://android.googlesource.com/platform/packages/apps/SettingsIn…...

Jedis存储一个以byte[]的形式的对象到Redis
1.1 准备一个User实体类 import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.io.Serializable; import java.util.Date;Data NoArgsConstructor AllArgsConstructor public class User implements Serializable {private In…...

updatexml报错注入原理分析
《网络安全自学教程》 SQL注入时,经常利用updatexml()的报错特性来脱库。 updatexml报错原理 1、updatexml语法参数2、报错原理分析3、使用updatexml()脱库4、分割显示结果 updatexml() 的作用是修改xml文件的内容。 1、updatexml语法参数 updatexml(参数1&#x…...

蓝桥杯c++算法秒杀【6】之动态规划【上】(数字三角形、砝码称重(背包问题)、括号序列、组合数问题:::非常典型的必刷例题!!!)
下将以括号序列、组合数问题超级吧难的题为例子讲解动态规划 别忘了请点个赞收藏关注支持一下博主喵!!!! ! ! ! ! 关注博主,更多蓝桥杯nice题目静待更新:) 动态规划 一、数字三角形 【问题描述】 上图给出了…...

【Qt】重写QComboBox下拉展示多列数据
需求 点击QComboBox时,下拉列表以多行多列的表格展示出来。 实现 直接上代码: #include <QComboBox> #include <QTableWidget> #include <QVBoxLayout> #include <QWidget> #include <QEvent> #include <QMouseEve…...

【mac】终端左边太长处理,自定义显示名称(terminal路径显示特别长)
1、打开终端 2、步骤 (1)修改~/.zshrc文件 nano ~/.zshrc(2)添加或修改PS1,我是自定义了名字为“macminiPro” export PS1"macminiPro$ "(3)使用 nano: Ctrl o (字母…...

基于Springboot的流浪宠物管理系统
基于javaweb的流浪宠物管理系统 介绍 基于javaweb的流浪宠物管理系统的设计与实现,后端框架使用Springbootmybatis,前端框架使用Vuehrml,数据库使用mysql,使用B/S架构实现前台用户系统和后台管理员系统,和不同权限级别…...

web博客系统的自动化测试
目录 前言测试用例编写自动化脚本测试准备博客登录页相关测试用例登陆成功登录失败 博客首页相关测试用例登陆成功登录失败 博客详情页相关测试用例登录成功登录失败 博客编辑页相关测试用例登陆成功登录失败 编写测试文档测试类型内容 前言 本次测试是运用个人写的一个博客系…...

【论文阅读】Multi-level Semantic Feature Augmentation for One-shot Learning
用于单样本学习的多层语义特征增强 引用:Chen, Zitian, et al. “Multi-level semantic feature augmentation for one-shot learning.” IEEE Transactions on Image Processing 28.9 (2019): 4594-4605. 论文地址:下载地址 论文代码:https:…...

网络知识面试
1、http状态码 101: 切换请求协议 200:(请求成功)。服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。 301 : (永久移动,永久性重定向,会缓存) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。…...

图片预览 图片上传到服务器
首先要明白 理解 multipart/form-data:multipart/form-data是一种在HTTP请求中使用的MIME类型,主要用于在客户端和服务器之间传输包含文件或二进制数据的表单数据。它通过一个边界(boundary)来分隔不同的表单字段和文件数据。…...

前端:base64的作用
背景 项目中发现,img标签中写src,读取一个png图片,只有16kb,速度特别慢。 解决办法,将图片转为base64,然后读取,速度特别快17ms就解决。 定义:base64是一种基于64个可打印字符(A-…...

Django在fitler过滤不等于的条件
提问 django 在API接口fitler的时候如何过滤 category 不等于6的 解答 为了在AoYuStudentFilter中设置过滤category不等于6的条件,需要使用django_filters库中的exclude方法。不过直接在FilterSet中使用exclude可能不那么直观,因为FilterSet主要设计用…...

Spring Boot英语知识分享网站:技术与实践
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...

京准电钟:NTP网络校时服务器从入门到精准
京准电钟:NTP网络校时服务器从入门到精准 京准电钟:NTP网络校时服务器从入门到精准 1.前言 由计算机网络系统组成的分布式系统,若想协调一致进行:IT行业的“整点开拍”、“秒杀”、“Leader选举”,通信行业的“同步…...

C++趣味编程玩转物联网:用树莓派Pico控制四位数码管
数码管是一种常用的数字显示器件,广泛应用于电子时钟、记分牌和智能设备显示界面。在本项目中,我们将通过树莓派Pico板控制一个四位数码管模块,展示从 0000 到 9999 的数字动态显示。这不仅是一次硬件和软件结合的实践,还可以帮助…...

DRM(数字权限管理技术)防截屏录屏----视频转hls流加密、web解密播放
提示:视频转hls流加密、web解密播放 需求:研究视频截屏时,播放器变黑,所以先研究的视频转hls流加密 文章目录 [TOC](文章目录) 前言一、工具ffmpeg、openssl二、后端nodeexpress三、web播放四、文档总结 前言 HLS流媒体协议&a…...

实验三 z变换及离散时间LTI系统的z域分析
实验原理 有理函数z 变换的部分分式展开 【实例2-1】试用Matlab 命令对函数 X ( z ) 18 18 3 − 1 − 4 z − 2 − z − 3 X\left(z\right)\frac{18}{183^{-1} -4z^{-2} -z^{-3} } X(z)183−1−4z−2−z−318 进行部分分式展开,并求出其z 反变换。 B[18]; A…...

Python中的DrissionPage详解
文章目录 Python中的DrissionPage详解一、引言二、DrissionPage的基本使用1、安装与启动2、元素定位与操作 三、高级功能1、截图功能2、数据提取3、与其他库的集成 四、具体使用示例五、总结 Python中的DrissionPage详解 一、引言 DrissionPage是一个强大的Python库ÿ…...

python除了熟悉的pandas,openpyxl库也很方便的支持编辑Excel表
excel表格是大家经常用到的文件格式,各行各业都会跟它打交道。之前文章我们介绍了使用openpyxl和xlrd库读取excel表数据,使用xlwt库创建和编辑excel表,在办公自动化方面可以方便我们快速处理数据,帮助我们提升效率。 python之open…...

go语言怎么实现bash cmd里的mv功能?
在Go语言中实现类似于Bash命令行中的mv命令的功能,主要是通过文件系统的操作来完成的。mv命令可以用来移动文件或目录,也可以用来重命名文件或目录。在Go语言中,可以使用标准库中的os和io/ioutil包来实现这些功能。 以下是一个简单的例子&…...

Vue前端面试进阶(五)
使用Element UI开发的实际项目 在实际项目中,我使用Element UI来快速构建用户界面。Element UI是一套为开发者、设计师和产品经理准备的基于Vue 2.0的桌面端组件库,它提供了丰富的UI组件,极大地提高了开发效率。然而,在使用过程中…...

面试手撕题积累
1、实现滑动窗口限流,允许每分钟最多有100个请求 阿里一面题。 核心: 时间窗口管理:滑动窗口会根据时间流逝不断更新,需要记录请求的时间戳,并根据当前时间计算窗口内的请求数量。 限流判断:每次请求到来…...