当前位置: 首页 > news >正文

从入门到精通:网络编程套接字(万字详解,小白友好,建议收藏)

一、预备知识

1.1 理解源IP地址和目的IP地址

在网络编程中,IP地址(Internet Protocol Address)是每个连接到互联网的设备的唯一标识符。IP地址可以分为IPv4和IPv6两种类型。IPv4地址是由32位二进制数表示,通常分为四个八位组,以十进制数表示,例如192.168.0.1。IPv6地址是由128位二进制数表示,以16进制数表示,例如2001:0db8:85a3:0000:0000:8a2e:0370:7334。

在IP数据包的头部,有两个IP地址,分别是源IP地址和目的IP地址。源IP地址表示发送数据包的设备地址,而目的IP地址则是接收数据包的设备地址。IP地址的存在使得我们能够在庞大的网络中找到特定的设备,就像我们在现实世界中通过邮寄地址找到特定的人或公司一样。

然而,光有IP地址并不能完成通信。想象一下发送QQ消息的场景,有了IP地址,我们可以将消息发送到对方的设备上,但这还不足以确定消息应该由哪个程序来解析。这时我们就需要端口号的帮助。端口号和IP地址结合使用,可以唯一标识网络中的某个进程,从而确保数据包能够正确送达目标应用程序。

在现代网络通信中,IP地址的管理和分配由互联网名称与数字地址分配机构(ICANN)进行。IP地址的分配遵循一定的规则,以确保全球唯一性和合理使用。例如,私有IP地址(如192.168.x.x)仅用于局域网内部,不能在互联网上使用。公网IP地址则由ISP(Internet Service Provider)分配,用于互联网上的设备通信。

理解IP地址的基础知识是网络编程的第一步。在实际开发中,我们经常需要处理IP地址的转换、验证和解析等操作。Python提供了丰富的库函数来帮助我们完成这些任务,例如socket库和ipaddress库等。通过这些工具,我们可以方便地进行IP地址的处理和操作。

1.2 认识端口号

端口号是网络编程中另一个重要的概念。端口号是传输层协议的一部分,用于标识网络上的进程。它是一个16位的整数,范围从0到65535,每个端口号都唯一对应一个进程,从而告诉操作系统当前数据应交给哪个进程处理。

端口号的作用类似于现实生活中的房间号。如果IP地址是一个公寓楼的地址,那么端口号就是具体的房间号。通过IP地址我们可以找到公寓楼,而通过端口号我们可以找到具体的房间,进而找到住在这个房间的人(即进程)。

端口号分为三类:公认端口(0-1023)、注册端口(1024-49151)和动态/私有端口(49152-65535)。公认端口由IANA(Internet Assigned Numbers Authority)管理,通常用于系统服务和知名应用程序,例如HTTP使用端口80,HTTPS使用端口443。注册端口用于用户应用程序和服务,可以由用户自行申请和使用。动态/私有端口则用于临时或私有应用程序,不需要注册。

在网络编程中,我们常常需要绑定端口号,以便服务器能够接收客户端的请求。例如,在创建一个TCP服务器时,我们需要调用bind函数将套接字绑定到一个特定的IP地址和端口号上。这样,当客户端发送请求到这个IP地址和端口号时,服务器就能够接收到请求并进行处理。

需要注意的是,端口号的使用需要遵循一些基本的原则。首先,避免使用已知的公认端口,以免与系统服务发生冲突。其次,确保端口号唯一性,一个端口号只能被一个进程占用。最后,注意端口号的范围,避免使用保留端口和特殊用途端口。

1.3 理解源端口号和目的端口号

在传输层协议(TCP和UDP)的数据段中,有两个端口号,分别是源端口号和目的端口号。源端口号表示发送数据的进程的端口号,而目的端口号则表示接收数据的进程的端口号。这两个端口号描述了“数据是谁发的,要发给谁”。

例如,我们在发送快递时,会标明寄件人和收件人的信息。寄件人的信息相当于源端口号,而收件人的信息相当于目的端口号。通过源端口号和目的端口号的结合,数据包可以在网络中准确地传输到目标进程。

在TCP协议中,连接的建立和断开都涉及到源端口号和目的端口号。在三次握手过程中,客户端通过源端口号向服务器的目的端口号发送SYN请求,服务器通过目的端口号向客户端的源端口号发送SYN-ACK应答,客户端再通过源端口号向服务器的目的端口号发送ACK确认,从而建立连接。在四次挥手过程中,客户端和服务器通过源端口号和目的端口号的交互来完成连接的断开。

在UDP协议中,源端口号和目的端口号用于标识数据报的发送方和接收方。由于UDP是无连接协议,每个数据报都是独立的,源端口号和目的端口号在每个数据报中都是独立存在的,不同的数据报可以有不同的端口号。

源端口号和目的端口号的存在,使得网络通信更加灵活和高效。通过合理使用端口号,我们可以实现多种多样的网络应用和服务。例如,在一个网络应用中,我们可以使用多个端口号来处理不同类型的请求,如HTTP请求、文件传输请求和即时通讯请求等。

1.4 认识TCP协议

TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议,提供可靠的数据传输。它通过三次握手建立连接,保证数据的完整性和顺序性,适用于对传输可靠性要求高的场景。

TCP协议的特点包括:

  • 面向连接:在传输数据之前,必须先建立连接。
  • 可靠传输:通过序号、确认、重传和超时等机制,保证数据的可靠传输。
  • 流量控制:通过滑动窗口机制,控制发送方发送数据的速度,避免接收方接收不过来。
  • 拥塞控制:通过拥塞窗口和慢启动机制,控制网络中的数据流量,避免网络拥塞。

在TCP协议中,数据被分成若干个数据段(Segment)进行传输。每个数据段都有一个序号,用于标识数据在整个数据流中的位置。接收方在接收到数据段后,会发送一个确认报文(ACK)给发送方,确认已接收到的数据段。发送方在接收到确认报文后,会继续发送后续的数据段。如果发送方在一定时间内没有收到确认报文,会进行数据重传,直到接收到确认报文为止。

TCP协议的连接建立过程称为三次握手:

  1. SYN:客户端向服务器发送一个SYN报文,表示请求建立连接。
  2. SYN-ACK:服务器收到SYN报文后,回应一个SYN-ACK报文,表示同意建立连接。
  3. ACK:客户端收到SYN-ACK报文后,回应一个ACK报文,表示连接建立完成。

TCP协议的连接断开过程称为四次挥手:

  1. FIN:客户端向服务器发送一个FIN报文,表示请求断开连接。
  2. ACK:服务器收到FIN报文后,回应一个ACK报文,表示同意断开连接。
  3. FIN:服务器向客户端发送一个FIN报文,表示准备断开连接。
  4. ACK:客户端收到FIN报文后,回应一个ACK报文,表示断开连接完成。

TCP协议在实际应用中有着广泛的应用,例如HTTP、FTP、SMTP等常见的网络协议都基于TCP协议实现。通过理解和掌握TCP协议,我们可以更好地进行网络编程,开发出高效、可靠的网络应用。

1.5 认识UDP协议

UDP(User Datagram Protocol,用户数据报协议)是无连接的协议,不保证数据的可靠传输。它以数据报的形式发送数据,每个数据报都是独立的,适用于实时性要求高但对可靠性要求低的场景,如视频直播、在线游戏等。

UDP协议的特点包括:

  • 无连接:在传输数据之前,不需要建立连接,每个数据报都是独立的。
  • 不可靠传输:UDP协议不保证数据报的传输成功,也不保证数据报的顺序,数据可能丢失、重复或乱序。
  • 面向数据报:UDP协议以数据报的形式发送和接收数据,每个数据报都有独立的报头和数据部分。

UDP协议的应用场景非常广泛,尤其在实时性要求高的场景中,UDP协议的无连接和低开销特点使得其非常适用。例如,在线游戏、视频会议和语音通话等应用通常采用UDP协议,以确保数据能够快速传输,尽可能减少延迟。

在UDP协议中,每个数据报都包含源端口号、目的端口号、长度和校验和等信息。源端口号和目的端口号用于标识数据报的发送方和接收方,长度表示数据报的长度,校验和用于检测数据报在传输过程中是否出现错误。由于UDP协议不提供重传和确认机制,数据报在传输过程中可能会丢失或出现错误,因此在应用层需要额外的处理逻辑来保证数据的完整性和可靠性。

1.6 网络字节序

在网络中,多字节数据的传输顺序有两种:大端字节序(Big-endian)和小端字节序(Little-endian)。大端字节序是指数据的高字节存储在内存的低地址,而小端字节序则是指数据的低字节存储在内存的低地址。不同的计算机体系结构可能采用不同的字节序,因此在进行网络传输时,需要进行字节序的转换,以保证数据在不同体系结构的计算机之间能够正确传输和解析。

TCP/IP协议规定网络数据流采用大端字节序,即低地址存储高字节。为了保证不同架构计算机之间的兼容性,我们需要进行字节序转换。C语言标准库提供了以下函数来进行字节序的转换:

  • htonl(Host to Network Long):将32位长整数从主机字节序转换为网络字节序。
  • htons(Host to Network Short):将16位短整数从主机字节序转换为网络字节序。
  • ntohl(Network to Host Long):将32位长整数从网络字节序转换为主机字节序。
  • ntohs(Network to Host Short):将16位短整数从网络字节序转换为主机字节序。

这些函数名中的h表示host(主机),n表示network(网络),l表示32位长整数,s表示16位短整数。例如,htonl表示将32位的长整数从主机字节序转换为网络字节序,用于在发送数据前对IP地址等数据进行转换。

通过字节序转换函数,我们可以确保网络程序具有良好的可移植性,使得同样的代码在大端和小端机器上都能正确运行。在实际开发中,正确处理字节序是保证网络通信成功的关键步骤。

二、socket编程接口

2.1 socket 常见API

socket API是网络编程的基础接口,适用于各种底层网络协议,如IPv4、IPv6等。以下是常见的socket API函数:

  • int socket(int domain, int type, int protocol);:创建一个新的套接字。domain参数指定协议族(如AF_INET用于IPv4),type参数指定套接字类型(如SOCK_STREAM用于TCP),protocol参数指定协议(通常为0,由系统自动选择)。
  • int bind(int socket, const struct sockaddr *address, socklen_t address_len);:将套接字绑定到本地地址。address参数指定本地地址和端口号,address_len参数指定地址的长度。
  • int listen(int socket, int backlog);:将套接字设置为监听模式,等待客户端连接。backlog参数指定等待连接队列的最大长度。
  • int accept(int socket, struct sockaddr *address, socklen_t *address_len);:接受客户端连接请求。address参数用于存储客户端的地址,address_len参数用于存储地址的长度。
  • int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);:与服务器建立连接。addr参数指定服务器的地址和端口号,addrlen参数指定地址的长度。

这些API函数构成了网络编程的基础,通过它们我们可以实现各种网络通信功能。例如,使用socket函数创建一个套接字,使用bind函数将其绑定到本地地址和端口号,使用listen函数将其设置为监听模式,使用accept函数接受客户端连接请求,使用connect函数与服务器建立连接。

在实际开发中,我们经常需要结合使用这些API函数来实现复杂的网络通信功能。例如,在实现一个TCP服务器时,我们可以使用socket函数创建一个服务器套接字,使用bind函数将其绑定到本地地址和端口号,使用listen函数将其设置为监听模式,然后使用accept函数接受客户端连接请求。在每个连接请求到来时,我们可以创建一个新的套接字用于与客户端通信,通过这个新的套接字接收和发送数据。

2.2 sockaddr结构

sockaddr结构是网络编程中通用的地址结构,不同的网络协议对应不同的地址结构,如IPv4地址用sockaddr_in表示,IPv6地址用sockaddr_in6表示。通过将特定类型的地址结构转换为sockaddr,可以实现代码的通用性。

sockaddr结构的定义如下:

struct sockaddr {sa_family_t sa_family;  // 地址族(如AF_INET用于IPv4)char sa_data[14];       // 地址数据(实际长度由地址族决定)
};

对于IPv4地址,通常使用sockaddr_in结构,该结构的定义如下:

struct sockaddr_in {sa_family_t sin_family;  // 地址族(AF_INET用于IPv4)in_port_t sin_port;      // 端口号(使用网络字节序)struct in_addr sin_addr; // IP地址char sin_zero[8];        // 保留字段,必须设置为0
};

对于IPv6地址,通常使用sockaddr_in6结构,该结构的定义如下:

struct sockaddr_in6 {sa_family_t sin6_family;   // 地址族(AF_INET6用于IPv6)in_port_t sin6_port;       // 端口号(使用网络字节序)uint32_t sin6_flowinfo;    // 流量信息struct in6_addr sin6_addr; // IP地址uint32_t sin6_scope_id;    // 作用域ID
};

在实际编程中,我们通常将特定类型的地址结构转换为sockaddr结构,并在使用时强制转换回原始类型。例如,在调用bind函数时,我们可以将sockaddr_in结构转换为sockaddr结构:

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));

通过这种方式,我们可以实现代码的通用性,处理不同类型的地址结构,同时保证网络通信的正确性和可靠性。

2.3 in_addr结构

in_addr结构用于表示一个IPv4的IP地址,其实质是一个32位的整数。in_addr结构的定义如下:

struct in_addr {uint32_t s_addr;  // IP地址(使用网络字节序)
};

在网络编程中,我们通常使用点分十进制的字符串表示IP地址,例如"192.168.0.1"。为了在程序中使用这些字符串表示的IP地址,我们需要进行字符串和in_addr结构之间的转换。标准库提供了以下函数来进行这些转换:

  • inet_pton:将点分十进制的字符串转换为in_addr结构。
  • inet_ntop:将in_addr结构转换为点分十进制的字符串。

以下是这些函数的使用示例:

struct in_addr addr;
inet_pton(AF_INET, "192.168.0.1", &addr);char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN);

通过这些函数,我们可以方便地在程序中处理IP地址,进行IP地址的转换和解析,确保网络通信的正确性。

三、简单的UDP网络程序

3.1 封装 UdpSocket

以下是一个简单的UdpSocket类的实现,它封装了UDP套接字的常见操作:

#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cassert>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;class UdpSocket {
public:UdpSocket() : fd_(-1) {}bool Socket() {fd_ = socket(AF_INET, SOCK_DGRAM, 0);if (fd_ < 0) {perror("socket");return false;}return true;}bool Close() {close(fd_);return true;}bool Bind(const std::string& ip, uint16_t port) {sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(ip.c_str());addr.sin_port = htons(port);int ret = bind(fd_, (sockaddr*)&addr, sizeof(addr));if (ret < 0) {perror("bind");return false;}return true;}bool RecvFrom(std::string* buf, std::string* ip = NULL, uint16_t* port = NULL) {char tmp[1024 * 10] = {0};sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t read_size = recvfrom(fd_, tmp, sizeof(tmp) - 1, 0, (sockaddr*)&peer, &len);if (read_size < 0) {perror("recvfrom");return false;}buf->assign(tmp, read_size);if (ip != NULL) {*ip = inet_ntoa(peer.sin_addr);}if (port != NULL) {*port = ntohs(peer.sin_port);}return true;}bool SendTo(const std::string& buf, const std::string& ip, uint16_t port) {sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(ip.c_str());addr.sin_port = htons(port);ssize_t write_size = sendto(fd_, buf.data(), buf.size(), 0, (sockaddr*)&addr, sizeof(addr));if (write_size < 0) {perror("sendto");return false;}return true;}private:int fd_;
};
3.2 UDP通用服务器

以下是一个通用的UDP服务器类UdpServer的实现:

#pragma once
#include "udp_socket.hpp"
#include <functional>typedef std::function<void (const std::string&, std::string* resp)> Handler;class UdpServer {
public:UdpServer() {assert(sock_.Socket());}~UdpServer() {sock_.Close();}bool Start(const std::string& ip, uint16_t port, Handler handler) {bool ret = sock_.Bind(ip, port);if (!ret) {return false;}for (;;) {std::string req;std::string remote_ip;uint16_t remote_port = 0;bool ret = sock_.RecvFrom(&req, &remote_ip, &remote_port);if (!ret) {continue;}std::string resp;handler(req, &resp);sock_.SendTo(resp, remote_ip, remote_port);printf("[%s:%d] req: %s, resp: %s\n", remote_ip.c_str(), remote_port, req.c_str(), resp.c_str());}sock_.Close();return true;}private:UdpSocket sock_;
};

基于以上封装,我们可以实现一个简单的英译汉服务器:

#include "udp_server.hpp"
#include <unordered_map>
#include <iostream>std::unordered_map<std::string, std::string> g_dict;void Translate(const std::string& req, std::string* resp) {auto it = g_dict.find(req);if (it == g_dict.end()) {*resp = "未查到!";return;}*resp = it->second;
}int main(int argc, char* argv[]) {if (argc != 3) {printf("Usage ./dict_server [ip] [port]\n");return 1;}g_dict.insert(std::make_pair("hello", "你好"));g_dict.insert(std::make_pair("world", "世界"));g_dict.insert(std::make_pair("c++", "最好的编程语言"));g_dict.insert(std::make_pair("bit", "特别NB"));UdpServer server;server.Start(argv[1], atoi(argv[2]), Translate);return 0;
}
3.3 UDP通用客户端

以下是一个通用的UDP客户端类UdpClient的实现:

#pragma once
#include "udp_socket.hpp"class UdpClient {
public:UdpClient(const std::string& ip, uint16_t port) : ip_(ip), port_(port) {assert(sock_.Socket());}~UdpClient() {sock_.Close();}bool RecvFrom(std::string* buf) {return sock_.RecvFrom(buf);}bool SendTo(const std::string& buf) {return sock_.SendTo(buf, ip_, port_);}private:UdpSocket sock_;std::string ip_;uint16_t port_;
};

基于以上封装,我们可以实现一个简单的英译汉客户端:

#include "udp_client.hpp"
#include <iostream>int main(int argc, char* argv[]) {if (argc != 3) {printf("Usage ./dict_client [ip] [port]\n");return 1;}UdpClient client(argv[1], atoi(argv[2]));for (;;) {std::string word;std::cout << "请输入您要查的单词: ";std::cin >> word;if (!std::cin) {std::cout << "Good Bye" << std::endl;break;}client.SendTo(word);std::string result;client.RecvFrom(&result);std::cout << word << " 意思是 " << result << std::endl;}return 0;
}

四、简单的TCP网络程序

4.1 TCP socket API 详解

TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。以下是TCP socket API的详细介绍:

  • int socket(int domain, int type, int protocol);:创建一个新的TCP套接字。domain参数指定协议族,IPv4使用AF_INETtype参数指定套接字类型,TCP使用SOCK_STREAM
  • int bind(int socket, const struct sockaddr *address, socklen_t address_len);:将套接字绑定到本地地址和端口号。
  • int listen(int socket, int backlog);:将套接字设置为监听模式,backlog参数指定等待连接队列的最大长度。
  • int accept(int socket, struct sockaddr *address, socklen_t *address_len);:接受客户端连接请求,返回一个新的套接字用于与客户端通信。
  • int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);:与服务器建立连接。

这些API函数构成了TCP网络编程的基础,通过它们我们可以实现各种网络通信功能。例如,使用socket函数创建一个套接字,使用bind函数将其绑定到本地地址和端口号,使用listen函数将其设置为监听模式,使用accept函数接受客户端连接请求,使用connect函数与服务器建立连接。

在实际开发中,我们经常需要结合使用这些API函数来实现复杂的网络通信功能。例如,在实现一个TCP服务器时,我们可以使用socket函数创建一个服务器套接字,使用bind函数将其绑定到本地地址和端口号,使用listen函数将其设置为监听模式,然后使用accept函数接受客户端连接请求。在每个连接请求到来时,我们可以创建一个新的套接字用于与客户端通信,通过这个新的套接字接收和发送数据。

4.2 封装 TCP socket

以下是一个简单的TCP套接字封装类TcpSocket的实现:

#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <cassert>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>#include <arpa/inet.h>
#include <fcntl.h>typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;#define CHECK_RET(exp) if (!(exp)) { return false; }class TcpSocket {
public:TcpSocket() : fd_(-1) {}TcpSocket(int fd) : fd_(fd) {}bool Socket() {fd_ = socket(AF_INET, SOCK_STREAM, 0);if (fd_ < 0) {perror("socket");return false;}printf("open fd = %d\n", fd_);return true;}bool Close() const {close(fd_);printf("close fd = %d\n", fd_);return true;}bool Bind(const std::string& ip, uint16_t port) const {sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(ip.c_str());addr.sin_port = htons(port);int ret = bind(fd_, (sockaddr*)&addr, sizeof(addr));if (ret < 0) {perror("bind");return false;}return true;}bool Listen(int num) const {int ret = listen(fd_, num);if (ret < 0) {perror("listen");return false;}return true;}bool Accept(TcpSocket* peer, std::string* ip = NULL, uint16_t* port = NULL) const {sockaddr_in peer_addr;socklen_t len = sizeof(peer_addr);int new_sock = accept(fd_, (sockaddr*)&peer_addr, &len);if (new_sock < 0) {perror("accept");return false;}printf("accept fd = %d\n", new_sock);peer->fd_ = new_sock;if (ip != NULL) {*ip = inet_ntoa(peer_addr.sin_addr);}if (port != NULL) {*port = ntohs(peer_addr.sin_port);}return true;}bool Recv(std::string* buf) const {buf->clear();char tmp[1024 * 10] = {0};ssize_t read_size = recv(fd_, tmp, sizeof(tmp), 0);if (read_size < 0) {perror("recv");return false;}if (read_size == 0) {return false;}buf->assign(tmp, read_size);return true;}bool Send(const std::string& buf) const {ssize_t write_size = send(fd_, buf.data(), buf.size(), 0);if (write_size < 0) {perror("send");return false;}return true;}bool Connect(const std::string& ip, uint16_t port) const {sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(ip.c_str());addr.sin_port = htons(port);int ret = connect(fd_, (sockaddr*)&addr, sizeof(addr));if (ret < 0) {perror("connect");return false;}return true;}int GetFd() const {return fd_;}private:int fd_;
};
4.3 TCP通用服务器

以下是一个通用的TCP服务器类TcpServer的实现:

#pragma once
#include <functional>
#include "tcp_socket.hpp"typedef std::function<void (const std::string& req, std::string* resp)> Handler;class TcpServer {
public:TcpServer(const std::string& ip, uint16_t port) : ip_(ip), port_(port) {}bool Start(Handler handler) {CHECK_RET(listen_sock_.Socket());CHECK_RET(listen_sock_.Bind(ip_, port_));CHECK_RET(listen_sock_.Listen(5));for (;;) {TcpSocket new_sock;std::string ip;uint16_t port = 0;if (!listen_sock_.Accept(&new_sock, &ip, &port)) {continue;}printf("[client %s:%d] connect!\n", ip.c_str(), port);for (;;) {std::string req;bool ret = new_sock.Recv(&req);if (!ret) {printf("[client %s:%d] disconnect!\n", ip.c_str(), port);new_sock.Close();break;}std::string resp;handler(req, &resp);new_sock.Send(resp);printf("[%s:%d] req: %s, resp: %s\n", ip.c_str(), port, req.c_str(), resp.c_str());}}return true;}private:TcpSocket listen_sock_;std::string ip_;uint64_t port_;
};
4.4 英译汉服务器

基于以上封装,我们可以实现一个简单的英译汉服务器:

#include <unordered_map>
#include "tcp_server.hpp"std::unordered_map<std::string, std::string> g_dict;void Translate(const std::string& req, std::string* resp) {auto it = g_dict.find(req);if (it == g_dict.end()) {*resp = "未找到";return;}*resp = it->second;
}int main(int argc, char* argv[]) {if (argc != 3) {printf("Usage ./dict_server [ip] [port]\n");return 1;}g_dict.insert(std::make_pair("hello", "你好"));g_dict.insert(std::make_pair("world", "世界"));g_dict.insert(std::make_pair("bit", "贼NB"));TcpServer server(argv[1], atoi(argv[2]));server.Start(Translate);return 0;
}

五、简单的TCP网络程序(多进程版本)

通过每个请求创建子进程的方式来支持多连接,我们可以实现一个多进程版本的TCP服务器。以下是TcpProcessServer的实现:

#pragma once
#include <functional>
#include <signal.h>
#include "tcp_socket.hpp"typedef std::function<void (const std::string& req, std::string* resp)> Handler;class TcpProcessServer {
public:TcpProcessServer(const std::string& ip, uint16_t port) : ip_(ip), port_(port) {signal(SIGCHLD, SIG_IGN);  // 忽略子进程的退出信号,避免产生僵尸进程}void ProcessConnect(const TcpSocket& new_sock, const std::string& ip, uint16_t port, Handler handler) {int ret = fork();  // 创建子进程if (ret > 0) {// 父进程new_sock.Close();return;} else if (ret == 0) {// 子进程for (;;) {std::string req;bool ret = new_sock.Recv(&req);if (!ret) {printf("[client %s:%d] disconnected!\n", ip.c_str(), port);exit(0);}std::string resp;handler(req, &resp);new_sock.Send(resp);printf("[client %s:%d] req: %s, resp: %s\n", ip.c_str(), port, req.c_str(), resp.c_str());}} else {perror("fork");}}bool Start(Handler handler) {CHECK_RET(listen_sock_.Socket());CHECK_RET(listen_sock_.Bind(ip_, port_));CHECK_RET(listen_sock_.Listen(5));for (;;) {TcpSocket new_sock;std::string ip;uint16_t port = 0;if (!listen_sock_.Accept(&new_sock, &ip, &port)) {continue;}printf("[client %s:%d] connect!\n", ip.c_str(), port);ProcessConnect(new_sock, ip, port, handler);}return true;}private:TcpSocket listen_sock_;std::string ip_;uint64_t port_;
};
5.1 多进程版本的实现分析

在上述实现中,我们通过fork()系统调用为每个新的客户端连接创建一个子进程。父进程负责监听和接受新的连接请求,而子进程则负责处理与客户端的具体通信。这样,通过多进程的方式,我们可以支持多个客户端同时连接和通信。

优点
  • 并发处理能力强:每个连接由独立的进程处理,可以充分利用多核CPU的性能。
  • 进程间隔离性强:进程之间相互独立,一个进程的崩溃不会影响到其他进程,增加了系统的稳定性。
缺点
  • 资源开销大:每创建一个子进程,操作系统都需要分配独立的内存和资源,资源开销较大。
  • 上下文切换开销大:进程之间的上下文切换比线程之间的上下文切换开销大,影响性能。

在实际开发中,使用多进程还是多线程需要根据具体场景进行选择。如果系统资源充足且需要高隔离性,可以选择多进程模式;如果需要高并发且资源受限,可以选择多线程模式。

六、简单的TCP网络程序(多线程版本)

通过每个请求创建一个线程的方式来支持多连接,我们可以实现一个多线程版本的TCP服务器。以下是TcpThreadServer的实现:

#pragma once
#include <functional>
#include <pthread.h>
#include "tcp_socket.hpp"typedef std::function<void (const std::string&, std::string*)> Handler;struct ThreadArg {TcpSocket new_sock;std::string ip;uint16_t port;Handler handler;
};class TcpThreadServer {
public:TcpThreadServer(const std::string& ip, uint16_t port) : ip_(ip), port_(port) {}bool Start(Handler handler) {CHECK_RET(listen_sock_.Socket());CHECK_RET(listen_sock_.Bind(ip_, port_));CHECK_RET(listen_sock_.Listen(5));for (;;) {ThreadArg* arg = new ThreadArg();arg->handler = handler;bool ret = listen_sock_.Accept(&arg->new_sock, &arg->ip, &arg->port);if (!ret) {continue;}printf("[client %s:%d] connect\n", arg->ip.c_str(), arg->port);pthread_t tid;pthread_create(&tid, NULL, ThreadEntry, arg);pthread_detach(tid);  // 分离线程,自动回收资源}return true;}static void* ThreadEntry(void* arg) {ThreadArg* p = reinterpret_cast<ThreadArg*>(arg);ProcessConnect(p);p->new_sock.Close();delete p;return NULL;}static void ProcessConnect(ThreadArg* arg) {for (;;) {std::string req;bool ret = arg->new_sock.Recv(&req);if (!ret) {printf("[client %s:%d] disconnected!\n", arg->ip.c_str(), arg->port);break;}std::string resp;arg->handler(req, &resp);arg->new_sock.Send(resp);printf("[client %s:%d] req: %s, resp: %s\n", arg->ip.c_str(), arg->port, req.c_str(), resp.c_str());}}private:TcpSocket listen_sock_;std::string ip_;uint16_t port_;
};
6.1 多线程版本的实现分析

在上述实现中,我们通过pthread_create()系统调用为每个新的客户端连接创建一个线程。主线程负责监听和接受新的连接请求,而新创建的线程则负责处理与客户端的具体通信。这样,通过多线程的方式,我们可以支持多个客户端同时连接和通信。

优点
  • 并发处理能力强:每个连接由独立的线程处理,可以充分利用多核CPU的性能。
  • 资源开销小:线程共享进程的内存和资源,相比多进程模式,资源开销较小。
  • 上下文切换开销小:线程之间的上下文切换比进程之间的上下文切换开销小,性能更高。
缺点
  • 线程安全问题:多个线程共享进程的内存和资源,需要注意线程安全问题,可能导致数据竞争和死锁等问题。
  • 线程数量有限:系统对线程的数量有一定限制,过多的线程可能导致资源耗尽,影响系统稳定性。

在实际开发中,使用多线程模式可以在一定程度上提高并发处理能力,但需要注意线程安全问题,合理控制线程数量,避免资源耗尽。

七、TCP协议通讯流程

7.1 建立连接的过程

TCP协议的连接建立过程称为三次握手:

  1. SYN:客户端向服务器发送一个SYN报文,表示请求建立连接。
  2. SYN-ACK:服务器收到SYN报文后,回应一个SYN-ACK报文,表示同意建立连接。
  3. ACK:客户端收到SYN-ACK报文后,回应一个ACK报文,表示连接建立完成。

在三次握手过程中,双方需要交换序列号和确认号,以确保连接的可靠性和数据的有序性。具体过程如下:

  • 客户端发送SYN报文,设置序列号为x
  • 服务器收到SYN报文,发送SYN-ACK报文,设置序列号为y,确认号为x+1
  • 客户端收到SYN-ACK报文,发送ACK报文,确认号为y+1

通过三次握手,客户端和服务器建立了可靠的连接,后续可以进行数据传输。

7.2 数据传输的过程

TCP协议提供全双工通信服务,通信双方可以同时发送和接收数据。服务器从accept()返回后,可以调用read()读取客户端发送的数据,同时客户端可以调用write()发送请求。

在数据传输过程中,TCP协议通过序列号和确认号确保数据的有序性和完整性。具体过程如下:

  • 发送方将数据分成若干数据段,每个数据段都有一个序列号。
  • 接收方收到数据段后,发送一个确认报文(ACK),确认号为已接收到的数据段的序列号加1。
  • 发送方在接收到确认报文后,继续发送后续的数据段。
  • 如果发送方在一定时间内没有收到确认报文,会进行数据重传,直到接收到确认报文为止。

通过这种方式,TCP协议保证了数据的可靠传输,避免数据丢失和重复。

7.3 断开连接的过程

TCP协议的连接断开过程称为

四次挥手:

  1. FIN:客户端向服务器发送一个FIN报文,表示请求断开连接。
  2. ACK:服务器收到FIN报文后,回应一个ACK报文,表示同意断开连接。
  3. FIN:服务器向客户端发送一个FIN报文,表示准备断开连接。
  4. ACK:客户端收到FIN报文后,回应一个ACK报文,表示断开连接完成。

在四次挥手过程中,双方需要交换序列号和确认号,以确保连接的可靠断开。具体过程如下:

  • 客户端发送FIN报文,设置序列号为x
  • 服务器收到FIN报文,发送ACK报文,确认号为x+1
  • 服务器发送FIN报文,设置序列号为y
  • 客户端收到FIN报文,发送ACK报文,确认号为y+1

通过四次挥手,客户端和服务器断开了连接,释放了相关资源。

八、TCP 和 UDP 对比

8.1 可靠传输 vs 不可靠传输

TCP提供可靠的数据传输,通过确认和重传机制保证数据的完整性和顺序性;UDP则不保证数据的传输可靠性,数据可能丢失、重复或乱序。

TCP通过序列号、确认号和重传机制,确保数据在传输过程中不丢失、不重复和按顺序到达接收方。这使得TCP适用于对传输可靠性要求高的场景,如文件传输、电子邮件等。

UDP则不提供这些保证,数据报在传输过程中可能会丢失或乱序。因此,UDP适用于对实时性要求高但对传输可靠性要求低的场景,如视频直播、在线游戏等。

8.2 有连接 vs 无连接

TCP是面向连接的协议,在传输数据前需要建立连接;UDP是无连接的协议,每个数据报都是独立的,不需要建立连接。

TCP在传输数据前,通过三次握手建立连接,确保双方可以进行可靠的数据传输。建立连接后,双方可以通过连接进行全双工通信,传输数据和确认报文。

UDP则不需要建立连接,每个数据报都是独立的,可以直接发送和接收。由于不需要建立连接,UDP的传输开销较小,适用于对实时性要求高的场景。

8.3 字节流 vs 数据报

TCP是面向字节流的协议,数据被视为连续的字节流进行传输;UDP是面向数据报的协议,每个数据报都是一个独立的消息。

TCP在传输数据时,将数据分成若干数据段,每个数据段都有一个序列号。接收方在接收到数据段后,将其按序号排列成连续的字节流,确保数据的有序性和完整性。

UDP则将数据分成若干数据报,每个数据报都是一个独立的消息。接收方在接收到数据报后,可以直接处理每个数据报,而不需要按顺序排列。这使得UDP适用于对实时性要求高但对有序性和完整性要求低的场景。

九、总结

通过本文,我们详细介绍了网络编程套接字的基础知识,包括IP地址、端口号、TCP和UDP协议、网络字节序等内容。随后,我们通过封装的方式实现了简单的UDP和TCP网络程序,并探讨了多进程和多线程版本的实现方式。最后,我们详细讲解了TCP协议的连接建立、数据传输和连接断开过程,以及TCP和UDP协议的对比。

在实际开发中,选择使用TCP还是UDP取决于具体的应用场景和需求。如果需要可靠的数据传输和连接控制,可以选择TCP协议;如果需要低延迟和高实时性,可以选择UDP协议。通过理解和掌握这些基础知识和编程技巧,我们可以更好地进行网络编程,开发出高效、可靠的网络应用。

嗯,就是这样啦,文章到这里就结束啦,真心感谢你花时间来读。
觉得有点收获的话,不妨给我点个吧!
如果发现文章有啥漏洞或错误的地方,欢迎私信我或者在评论里提醒一声~

相关文章:

从入门到精通:网络编程套接字(万字详解,小白友好,建议收藏)

一、预备知识 1.1 理解源IP地址和目的IP地址 在网络编程中&#xff0c;IP地址&#xff08;Internet Protocol Address&#xff09;是每个连接到互联网的设备的唯一标识符。IP地址可以分为IPv4和IPv6两种类型。IPv4地址是由32位二进制数表示&#xff0c;通常分为四个八位组&am…...

dledger原理源码分析系列(一)架构,核心组件和rpc组件

简介 dledger是openmessaging的一个组件&#xff0c; raft算法实现&#xff0c;用于分布式日志&#xff0c;本系列分析dledger如何实现raft概念&#xff0c;以及dledger在rocketmq的应用 本系列使用dledger v0.40 本文分析dledger的架构&#xff0c;核心组件&#xff1b;rpc组…...

第七节:如何浅显易懂地理解Spring Boot中的依赖注入(自学Spring boot 3.x的第二天)

大家好&#xff0c;我是网创有方&#xff0c;今天我开始学习spring boot的第一天&#xff0c;一口气写了这么多。 这节通过一个非常浅显易懂的列子来讲解依赖注入。 在Spring Boot 3.x中&#xff0c;依赖注入&#xff08;Dependency Injection, DI&#xff09;是一个核心概念…...

Postman自动化测试实战:使用脚本提升测试效率

在软件开发过程中&#xff0c;接口测试是确保后端服务稳定性和可靠性的关键步骤。Postman作为一个流行的API开发工具&#xff0c;提供了强大的脚本功能来实现自动化测试。通过在Postman中使用脚本&#xff0c;测试人员可以编写测试逻辑&#xff0c;实现测试用例的自动化执行&am…...

CSMA/CA并不是“公平”的

CSMA/CA会造成过于公平,对于最需要流量的节点,是最不友好的,而对于最不需要流量的节点,则是最友好的。 CSMA/CA是优先公平来工作的。 CSMA/CA首先各节点使用DIFS界定air idle,在此期间大家都等待 其次,为了同时发送引起碰撞,在DIFS之后随机从CWmin和CWmax之间选择一个时…...

【漏洞复现】I doc view——任意文件读取

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 I doc view 在线文档预览是一个用于查看、编辑、管理文档的工具…...

图数据库 vs 向量数据库

最近大模型出来之后&#xff0c;向量数据库重新翻红&#xff0c;业界和市场上有不少声音认为向量数据库会极大的影响图数据库&#xff0c;图数据库市场会萎缩甚至消失&#xff0c;今天就从技术原理角度来讨论下图数据库和向量数据库到底差别在哪里&#xff0c;适合什么场景&…...

企业品牌出海第一站 维基百科词条创建

维基百科是一部内容开放、自由的网络百科全书,旨在创造一个涵盖所有领域知识,服务所有互联网用户的知识性百科全书。其在国外应用非常广泛且认可度很高&#xff0c;国内品牌出海或国际品牌都很有必要创建企业自己的维基百科页面&#xff0c;以及企业高管的个人维基百科页面。 如…...

Windows下activemq集群配置(broker-network)

1.activemq版本信息 activemq&#xff1a;apache-activemq-5.18.4 2.activemq架构 3.activemq集群配置 activemq集群配置基于Networks of Brokers 这种HA方案的优点&#xff1a;是占用的节点数更少(只需要2个节点),而且2个broker都可以响应消息的接收与发送。不足&#xff…...

心理辅导平台系统

摘 要 中文本论文基于Java Web技术设计与实现了一个心理辅导平台。通过对国内外心理辅导平台发展现状的调研&#xff0c;本文分析了心理辅导平台的背景与意义&#xff0c;并提出了论文研究内容与创新点。在相关技术介绍部分&#xff0c;对Java Web、SpringBoot、B/S架构、MVC模…...

代理IP对SEO影响分析:提升网站排名的关键策略

你是否曾经为网站排名难以提升而苦恼&#xff1f;代理服务器或许就是你忽略的关键因素。在竞争激烈的互联网环境中&#xff0c;了解代理服务器对SEO的影响&#xff0c;有助于你采取更有效的策略&#xff0c;提高网站的搜索引擎排名。本文将为你详细分析代理服务器在SEO优化中的…...

【leetcode--三数之和】

这道题记得之前做过&#xff0c;但是想不起来了。。总结一下&#xff1a; 函数的主要步骤和关键点&#xff1a; 排序&#xff1a;对输入的整数数组nums进行排序。这是非常重要的&#xff0c;因为它允许我们使用双指针技巧来高效地找到满足条件的三元组。初始化&#xff1a;定…...

解决Java中的ClassCastException问题

解决Java中的ClassCastException问题 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java编程中&#xff0c;ClassCastException是一个常见的运行时异常&am…...

【TensorFlow深度学习】混合生成模型:结合AR与AE的创新尝试

混合生成模型&#xff1a;结合AR与AE的创新尝试 引言自回归模型与自动编码器的简述混合模型的创新尝试组合AR与AE&#xff1a;MADE混合模型在图学习中的应用 结论与展望 在自我监督学习的广阔天地里&#xff0c;混合生成模型以其独特的魅力&#xff0c;跨越了自回归&#xff08…...

Spring:Spring中分布式事务解决方案

一、前言 在Spring中&#xff0c;分布式事务是指涉及多个数据库或系统的事务处理&#xff0c;其中事务的参与者、支持事务的服务器、资源管理器以及事务管理器位于分布式系统的不同节点上。这样的架构使得两个或多个网络计算机上的数据能够被访问并更新&#xff0c;同时将这些操…...

音视频开发32 FFmpeg 编码- 视频编码 h264 参数相关

1. ffmpeg -h 这个命令总不会忘记&#xff0c;用这个先将ffmpeg所有的help信息都list出来 C:\Users\Administrator>ffmpeg -h ffmpeg version 6.0-full_build-www.gyan.dev Copyright (c) 2000-2023 the FFmpeg developersbuilt with gcc 12.2.0 (Rev10, Built by MSYS2 pro…...

标准版小程序订单中心path审核不通过处理教程

首先看自己小程序是不是已经审核通过并上线状态才在站内信里面提醒的&#xff1f; 如果没有提交过审核&#xff0c;请在提交的时候填写。path地址为&#xff1a;pages/goods/order_list/index 如果是已经上线的小程序&#xff0c;当时没要求填这个&#xff0c;但新的政策要求填…...

移植对话框MFC

VC版 MFC程序对话框资源移植 以下均拷贝自上面&#xff0c;仅用来记录 &#xff08;部分有删除&#xff09; 法1&#xff1a; Eg&#xff1a;将B工程调试好的对话框移植到A工程中 1.资源移植 1.1 在2017打开B工程,在工作区Resource标签页中选中Dialog文件夹下的资源文件,按…...

【开源的字典项目】【macOS】:在macOS上能打开mdd and mdx 的github开源项目

【开源的字典项目】【macOS】 在macOS上能打开mdd and mdx 的github开源项目 Here are some GitHub repositories that provide code for opening and reading mdd and mdx files in macOS: 1. MdxEdit: Repository: https://github.com/mdx-editorDescription: A free and …...

已解决javax.security.auth.login.LoginException:登录失败的正确解决方法,亲测有效!!!

已解决javax.security.auth.login.LoginException&#xff1a;登录失败的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 出现问题的场景 报错原因 解决思路 解决方法 1. 检查用户名和密码 用户名和密码验证 2. 验证配置文件 …...

2741. 特别的排列 Medium

给你一个下标从 0 开始的整数数组 nums &#xff0c;它包含 n 个 互不相同 的正整数。如果 nums 的一个排列满足以下条件&#xff0c;我们称它是一个特别的排列&#xff1a; 对于 0 < i < n - 1 的下标 i &#xff0c;要么 nums[i] % nums[i1] 0 &#xff0c;要么 nums[…...

读AI新生:破解人机共存密码笔记15辅助博弈

1. 辅助博弈 1.1. assistance game 1.2. 逆强化学习如今已经是构建有效的人工智能系统的重要工具&#xff0c;但它做了一些简化的假设 1.2.1. 机器人一旦通过观察人类学会了奖励函数&#xff0c;它就会采用奖励函数&#xff0c;这样它就可以执行相同的任务 1.2.1.1. 解决这…...

C++ 因项目需求,需要将0~2的32次方这个区间的数字保存到内存当中(内存大小为4G),并且可以实现对任意一个数字的增删。(先叙述设计思路,再写岀代码)

问题&#xff1a; C 因项目需求&#xff0c;需要将0~2的32次方这个区间的数字保存到内存当中(内存大小为4G),并且可以实现对任意一个数字的增删。(先叙述设计思路&#xff0c;再写岀代码) 解答 设计思路代码实现说明 为了在有限的内存&#xff08;4GB&#xff09;中存储和操作 …...

Linux 下的性能监控与分析技巧

在日常的服务器管理和问题诊断过程中&#xff0c;Linux 命令行工具提供了强大的支持。本文通过几个常用的示例&#xff0c;介绍如何快速定位问题、监控服务器性能。 无论你是编程新手还是有一定经验的开发者&#xff0c;理解和掌握这些命令&#xff0c;都将在你的工作中大放异…...

不可复制网站上的文字——2种方法

禁用javascript或Console控制台代码 &#xff08;1&#xff09;F12键——设置——勾选禁用javascript &#xff08;2&#xff09;Console控制台敲如下代码&#xff1a; var allowPaste function(e){ e.stopImmediatePropagation(); return true; }; document.addEventListe…...

Ubuntu 22.04上编译安装c++ spdlog library

Very fast, header-only/compiled, C logging library. 请以root身份或sudo执行。 1. 安装必需的依赖项&#xff1a; sudo apt-get update sudo apt-get install git g cmake 2. 克隆 spdlog 仓库&#xff1a; cd /opt git clone https://github.com/gabime/spdlog.git …...

ESP32代码开发入门

ESP-IDF ESP-ADF开发 开发概要 编译环境及SDK搭建 整个开发流程是:下载ESP-IDF, ESP-ADF(按需下载),并安装, 编写hello world工程,编译并烧录到主板验证 可参照ESP32 esp-idf esp-adf环境安装及.a库创建与编译api大部分可以用glibc的接口 做了封装,时间time(NULL), 创建线程p…...

“势”是“态”的偶然性减少

“态势感知”中的“势”指的是一种趋势或倾向性&#xff0c;而“态”则表示状态或局势。这个术语常用于描述在一段时间内系统或事件显示出来的方向性变化或发展趋势。因此&#xff0c;可以将“态势”理解为系统或事件状态变化的趋势&#xff0c;这种变化通常反映出偶然性减少的…...

人脑计算机技术与Neuroplatform:未来计算的革命性进展

引言 想象一下&#xff0c;你在某个清晨醒来&#xff0c;准备开始一天的工作&#xff0c;而实际上你的大脑正作为一台生物计算机的核心&#xff0c;处理着大量复杂的信息。这并非科幻电影的情节&#xff0c;而是人脑计算机技术即将带来的现实。本文将深入探讨FinalSpark公司的…...

新版周易测算系统源码 去授权完美运行

已经去掉授权可以完美运行 更新了三个模板市面上都是几千几千的卖 更新了三套首页新ui 自己后台切换就行 源码大小&#xff1a;338M 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89447857 更多资源下载&#xff1a;关注我....

【PYTHON】力扣刷题笔记 -- 0053. 最大子数组和【中等】

题目描述&#xff1a;给你一个整数数组 array: nums &#xff0c;请你找出一个具有最大和的连续子数组 sub-array&#xff0c;返回其最大和 子数组&#xff08;最少包含一个元素&#xff09;: 是数组中的一个连续部分 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1…...

Linux启动elasticsearch,提示权限不够

Linux启动elasticsearch&#xff0c;提示权限不够&#xff0c;如下图所示&#xff1a; 解决办法&#xff1a; 设置文件所有者&#xff0c;即使用户由权限访问文件 sudo chown -R 用户名[:新组] ./elasticsearch-8.10.4 //切换到elasticsearch-8.10.4目录同级 chown详细格式…...

css 布局出现无法去除的空白

案件介绍&#xff1a;在没有设置任何的css样式的情况下 文字顶部出现无法去除的空白 源代码 <div click"onClick" ><div class"tableTextButton--container"></div><Icon v-if"loading || thisLoading" type"ios-lo…...

使用SpringBoot整合filter

SpringBoot整合filter&#xff0c;和整合servlet类似&#xff0c;也有两种玩儿法 1、创建一个SpringBoot工程&#xff0c;在工程中创建一个filter过滤器&#xff0c;然后用注解WebFilter配置拦截的映射 2、启动类还是使用ServletComponentScan注解来扫描拦截器注解WebFilter 另…...

Python酷库之旅-第三方库openpyxl(15)

目录 一、 openpyxl库的由来 1、背景 2、起源 3、发展 4、特点 4-1、支持.xlsx格式 4-2、读写Excel文件 4-3、操作单元格 4-4、创建和修改工作表 4-5、样式设置 4-6、图表和公式 4-7、支持数字和日期格式 二、openpyxl库的优缺点 1、优点 1-1、支持现代Excel格式…...

葡萄串目标检测YoloV8——从Pytorch模型训练到C++部署

文章目录 软硬件准备数据准备数据处理脚本模型训练模型部署数据分享软硬件准备 训练端 PytorchultralyticsNvidia 3080Ti部署端 fastdeployonnxruntime数据准备 用labelimg进行数据标注 数据处理脚本 xml2yolo import os import glob import xml.etree.ElementTree as ETxm…...

OpenAI推出自我改进AI- CriticGPT

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

springboot系列七: Lombok注解,Spring Initializr,yaml语法

老韩学生 LombokLombok介绍Lombok常用注解Lombok应用实例代码实现idea安装lombok插件 Spring InitializrSpring Initializr介绍Spring Initializr使用演示需求说明方式1: IDEA创建方式2: start.spring.io创建 注意事项和说明 yaml语法yaml介绍使用文档yaml基本语法数据类型字面…...

专访ATFX首席战略官Drew Niv:以科技创新引领企业高速发展

在金融科技创新的浪潮中&#xff0c;人才是推动企业高速发展的核心驱动力&#xff0c;优质服务是引领企业急速前行的灯塔。作为差价合约领域的知名品牌&#xff0c;ATFX高度重视人才引进工作&#xff0c;秉持“聚天下英才而用之”的理念&#xff0c;在全球范围内广揽科技精英&a…...

关于FPGA对 DDR4 (MT40A256M16)的读写控制 4

关于FPGA对 DDR4 &#xff08;MT40A256M16&#xff09;的读写控制 4 语言 &#xff1a;Verilg HDL 、VHDL EDA工具&#xff1a;ISE、Vivado、Quartus II 关于FPGA对 DDR4 &#xff08;MT40A256M16&#xff09;的读写控制 4一、引言二、DDR4 SDRAM设备中模式寄存器重要的模式寄存…...

android——Livedata、StateFlow、ShareFlow和Channel的介绍和使用

目录 一、LiveData介绍 二、StateFlow介绍 三、ShareFlow介绍 四、Channel介绍 小结 一、LiveData介绍 LiveData是一种在Android开发中用于观察数据变化的组件。它可以被观察者注册并在数据变化时通知观察者&#xff0c;从而实现数据的实时更新。LiveData具有生命周期感知能力&…...

Debezium 同步 MySQL 实时数据并解决数据重复消费问题

我们使用 Debezium 实时同步一个 MySQL 的数据到另一个 MySQL&#xff0c;代码网上基本都有&#xff0c;都是在引入 debezium-api&#xff0c;debezium-embedded 后写 Java 代码&#xff0c;做好了基本配置后启动程序&#xff0c;Debezium 会自动读取 MySQL 的实时 binlog&…...

【图像处理】1、使用OpenCV库图像轮廓的检测和绘制

OpenCV (Open Source Computer Vision Library) 是一个用于计算机视觉和图像处理的开源库。它提供了数百种用于图像和视频分析的算法&#xff0c;并被广泛应用于研究和商业领域。OpenCV 支持多种编程语言&#xff0c;包括 C、Python、Java 等&#xff0c;具有跨平台的特性&…...

【AI编译器】triton学习:矩阵乘优化

Matrix Multiplication 主要内容&#xff1a; 块级矩阵乘法 多维指针算术 重新编排程序以提升L2缓存命 自动性能调整 Motivations 矩阵乘法是当今高性能计算系统的一个关键组件&#xff0c;在大多数情况下被用于构建硬件。由于该操作特别复杂&#xff0c;因此通常由软件提…...

动静分离网络

动静分离网络的主要目的是分别处理视频帧中的静止区域和运动区域&#xff0c;以便对不同区域采用不同的去噪策略。这里提供一个实现思路&#xff0c;通过两个分支网络分别处理静止区域和运动区域&#xff0c;然后将两者的输出融合起来。 实现步骤 帧差图生成&#xff1a;计算…...

Python商务数据分析知识专栏(三)——Python数据分析的应用①Matplotlib数据可视化基础

Python商务数据分析知识专栏&#xff08;三&#xff09;——Python数据分析的应用①Matplotlib数据可视化基础 Matplotlib数据可视化基础1.掌握绘图基本语法与常用绘图2.分析特征间关系3.分析特征内部数据分布与分散情况 Matplotlib数据可视化基础 1.掌握绘图基本语法与常用绘…...

DataV大屏组件库

DataV官方文档 DataV组件库基于Vue &#xff08;React版 (opens new window)&#xff09; &#xff0c;主要用于构建大屏&#xff08;全屏&#xff09;数据展示页面即数据可视化&#xff0c;具有多种类型组件可供使用&#xff1a; 源码下载...

paraview跨节点并行渲染

参考&#xff1a; https://cloud.tencent.com/developer/ask/sof/101483588 ParaView 支持使用其内置的网络拓扑来进行跨节点的并行渲染。以下是一个简单的步骤来设置和运行跨节点的并行渲染&#xff1a; 确保你的计算环境支持多节点计算&#xff0c;比如通过SSH、MPI或其他集…...

Java中相等比较详解

本文对Java中的相等判断进行详细解释&#xff0c;包括&#xff0c;equals和compareTo等。 一、 运算符 1. 用途 基本数据类型&#xff1a;用于比较两个基本数据类型的值是否相等。 引用类型&#xff1a;用于比较两个对象引用是否指向同一个对象。 2. 示例 // 基本数据类型比…...

HBuilder X 小白日记01

1.创建项目 2.右击项目&#xff0c;可创建html文件 3.保存CtrlS&#xff0c;运行一下 我们写的内容&#xff0c;一般是写在body里面 注释的快捷键&#xff1a;Ctrl/ h标签 <h1> 定义重要等级最高的(最大)的标题。<h6> 定义最小的标题。 H标签起侧重、强调的作用…...

Python和MATLAB粘性力接触力动态模型半隐式欧拉算法

&#x1f3af;要点 &#x1f3af;运动力模型计算制作过程&#xff1a;&#x1f58a;相机捕捉网球运动图&#xff0c;制定运动数学模型&#xff0c;数值微分运动方程 | &#x1f58a;计算运动&#xff0c;欧拉算法离散积分运动&#xff0c;欧拉-克罗默算法微分运动方程 &#…...

Node.js_流Stream

Node.js 中的流&#xff08;Streams&#xff09;是一种处理读取和写入数据的抽象接口&#xff0c;它允许你以非阻塞的方式处理数据&#xff0c;这对于处理大文件或网络请求等 I/O 密集型任务非常有用。流主要分为四种类型&#xff1a;可读流&#xff08;Readable&#xff09;、…...

认识String类

文章目录 String类字符串的遍历字符串的比较字符串的替换字符串的转换字符串的切割字符串的切片字符串的查找 总结 String类 在C语言中已经涉及到字符串了&#xff0c;但是在C语言中要表示字符串只能使用字符数组或者字符指针&#xff0c;可以使用标准库提 供的字符串系列函数完…...

深度学习——卷积神经网络(convolutional neural network)CNN详解(一)——概述. 步骤清晰0基础可看

在CNN的学习过程中我会提供相应的手算例子帮助理解训练过程。 其他关于神经网络的学习链接如下&#xff1a; 一、了解卷积神经网络 卷积神经网络的作用 总的来说&#xff0c;卷积神经网络的第一个主要作用是对图像进行特征提取&#xff0c;所谓特征提取&#xff0c;就是明白…...

c++初级-2-引用

文章目录 引用一、引用的定义二、引用做函数参数三、引用作为返回对象四、引用的本质五、常量引用 引用 即给一个变量起别名。 一、引用的定义 int a 10;//引用int& b a;cout << "a " << a << endl;cout << "b " <&l…...

论文阅读KVQ: Kwai Video Quality Assessment for Short-form Videos

背景 短视频格式、内容与长视频不同,需要引入新的质量评估方法。作者构建了一个新的用于质量评估的数据集,提出了新的质量评估方法。 如下图所示,短视频有不同的格式、有模糊、噪声、编码等各种畸变。 KVQ 数据集 通过快手平台选择多样化的短视频样本,提取多个质量特征…...

阿维塔:车端出海idps能力建设实践方案探讨

在当前国内外法规环境下,特别是随着欧标已强制实施和国标即将跟进,推行IDPS显得至关重要。2024年6月27日,在2024第三届中国车联网安全大会上,阿维塔车型出海副总师韩建伟围绕阿维塔IDPS技术方案展开了详细介绍。车端IDPS逻辑上分为应用层、系统层、网络层、外部通信层,并包…...

湖北鹤峰:高山萝卜陆续上市田间一片好“丰”景

2日,笔者走进湖北省恩施州鹤峰县太平镇奇峰关村的萝卜种植基地,放眼望去,一棵棵萝卜整齐排列,圆润饱满,一股清甜气味扑面而来。农户们在忙着采摘白萝卜,骡驮装车,一派热火朝天的丰收景象。“现在正是蔬菜上市的季节,每天都有10多名工人在田里摘萝卜,采收量约为30吨左右…...

新火科技控股01611公布中期业绩公司拥有人应占溢利9981.3万港元同

公布中期业绩 公司拥有人应占溢利9981.3万港元 同比扭亏为盈)智通财经APP讯,新火科技控股公布截至2024年3月31日止六个月的中期业绩,收益约6.56亿港元,同比减少72.6%;毛利3214.1万港元,2023年同期毛损1438.8万港元;公司拥有人应占溢利9981.3万港元,2023年同期应占亏损约2.…...

Rosetta PyRosetta 源码包 安装包 下载

--- pyrosetta_src.zip包含以下包&#xff1a; | --- PyRosetta4.Debug.python27.ubuntu.release-185.tar.bz2 | --- PyRosetta4.Release.python27.linux.release-215.tar.bz2 | --- PyRosetta4.Release.python38.ubuntu.release-349.tar.bz2 --- pyrosetta_whl.zip包含…...

什么是React?

01 Why React? What is React? I think the one-line description of React on its home page (https://react.dev/) is concise and accurate: “A JavaScript library for building user interfaces.” 我认为React主页(https://react.dev/)上的一行描述既简洁又准确: …...

最长递增子序列,交错字符串

第一题&#xff1a; 代码如下&#xff1a; int lengthOfLIS(vector<int>& nums) {//dp[i]表示以第i个元素为结尾的最长子序列的长度int n nums.size();int res 1;vector<int> dp(n, 1);for (int i 1; i < n; i){for (int j 0; j < i; j){if (nums[i]…...