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

Linux(socket网络编程)TCP连接

Linux(socket网络编程)TCP连接

  • 基础
    • 文件目录
    • 函数
      • 系统进程控制函数
        • fork()
        • exec系列函数
        • void abort(void)
        • void assert(int expression)
        • void exit(int status)
        • void _exit(int status)
        • int atexit(void (*func)(void))
        • int on_exit(void (*function)(int,void*),void *arg)
        • int setjmp(jmp_buf environment)
        • void longjmp(jmp_buf environment,int value)
        • void siglongjmp(sigjmp_buf env,int val)
        • int sigsetjmp(sigjmp_buf env,int savemask)
        • pid_t getpgid(pid_t pid)
        • pid_t getpgrp(void)
        • pid_t getpid(void)
        • pid_t getppid(void)
        • int getpriority(int which,int who)
        • int setpgid(pid_t pid,pid_t pgid)
        • int setpgrp(void)
        • int setpriority(int which,int who,int prio)
        • int nice(int inc)
        • system
        • int wait(int *status)
        • pid_t waitpid(pid_t pid,int* status,int options)
    • Socket编程
    • TCP和UDP
  • 建立一个简单的TCP连接
    • 服务端
      • 创建socket
      • 绑定socket到地址和端口
      • 监听连接
      • 接受连接
      • 发送消息
      • 关闭套接字
      • 服务端完整代码
    • 客户端
    • 运行
    • socket编程TCP连接:实验一 回声服务器
    • socket编程TCP连接:实验二

基础

文件目录

Bin 命令文件
Boot 启动加载器
Dev 设备文件
Etc 配置文件
Home 普通用户家目录
Media 用于挂载可移动设备的目录

函数

字符串函数
数据转换函数
输入输出函数
权限控制函数
IO函数
系统进程控制函数
文件和目录函数

系统进程控制函数

进程是操作系统调度的最小单位

fork 用于创建一个新的子进程,子进程是父进程的副本。
exec 用于在当前进程的上下文中执行一个新的程序,替换当前进程的内存镜像。

fork()

》头文件#include <unistd.h>
》fork函数用于创建一个新的进程,也就是子进程,子进程是父进程的副本,父进程就是调用了fork的进程。
》子进程几乎拥有父进程的所有资源(包括内存、文件描述符等)
》子进程和父进程各自拥有独立的地址空间和进程ID
特点
(1)返回两次fork在父进程中返回子进程的PID,在子进程中返回0。如果创建失败,则返回-1。
(2)共享与独立:子进程和父进程共享打开的文件描述符、文件偏移量等。但它们有独立的地址空间和数据段。
(3)资源开销:fork会复制父进程的地址空间,这是一个相对昂贵的操作,尤其是在父进程占用大量内存时。不过,现代操作系统采用了写时复制机制(Copy-On-Write,COW)来优化这一过程。

exec系列函数

》头文件#include <unistd.h>
》exec系列函数用于在当前进程的上下文中执行一个新的程序,从而替换当前进程的镜像。
特点:
(1)不创建新的进程:exec不创建新的进程,而是用新的程序替换当前进程的内存空间
(2)参数传递:exec通常需要传递新程序的路径和参数列表
(3)无返回值:exec成功,无返回;失败返回-1。
常用函数:

execl(const char *path, const char *arg, ...)//使用路径和参数列表执行程序。
execle(const char *path, const char *arg, ..., char * const envp[])// 类似于 execl,但允许指定环境变量。
execlp(const char *file, const char *arg, ...)//使用文件名(在PATH中查找)和参数列表执行程序。
execv(const char *path, char *const argv[])// 使用路径和参数数组执行程序。
execve(const char *path, char *const argv[], char *const envp[])// 类似于 execv,但允许指定环境变量。
execvp(const char *file, char *const argv[])// 使用文件名(在PATH中查找)和参数数组执行程序。

l 进程执行的参数,以可变参数的形式给出的,这些参数以NULL作为最后一个参数结尾。
p 进程函数会将当前的PATH作为一个参考环境变量
e 进程函数会需要用户来设置这个环境变量
v 进程函数会用参数数组来传递argv,数组的最后一个必须是NULL
示例:

int main(int argc, char* argv[])
{execl("/bin/ls", "ls", "-l", NULL);
}

运行结果:
total 48
-rwxr-xr-x 1 root root 39008 Feb 10 16:46 ConsoleApplication5.out

void abort(void)

通常用于检测到不可恢复的错误时,比如内存分配失败
头文件#include<stdlib.h>

void assert(int expression)

用于在调试期间捕捉编程错误。它检查给定的表达式是否为真,如果为假,则输出错误信息并终止进程。
头文件#include<assert.h>

void exit(int status)

用于正常终止进程。它首先执行所有通过atexit()或on_exit()注册的函数,然后关闭所有打开的文件描述符,最后终止进程。
头文件#include<stdlib.h>

void _exit(int status)

终止进程,但不执行任何清理操作,也不刷新标准I/O缓存区。
头文件#include<unistd.h>

int atexit(void (*func)(void))

注册一个或多个函数。
头文件#include<stdlib.h>

int on_exit(void (function)(int,void),void *arg)

注册一个或多个函数,允许传递一个参数给注册的函数。
头文件#include<stdlib.h>

int setjmp(jmp_buf environment)

保存目前堆栈环境

void longjmp(jmp_buf environment,int value)

跳转到原先setjmp保存的堆栈环境

void siglongjmp(sigjmp_buf env,int val)

改变进程优先顺序,跳转到原先sigsetjmp保存的堆栈环境

int sigsetjmp(sigjmp_buf env,int savemask)

保存目前堆栈环境

pid_t getpgid(pid_t pid)

取得进程组识别码

pid_t getpgrp(void)

取得进程组识别码

pid_t getpid(void)

取得进程识别码

pid_t getppid(void)

取得父进程的进程识别码

int getpriority(int which,int who)

取得程序进程执行优先权

int setpgid(pid_t pid,pid_t pgid)

设置进程组识别码

int setpgrp(void)

设置进程组识别码

int setpriority(int which,int who,int prio)

设置程序进程执行优先权

int nice(int inc)

改变进程优先级

system

执行shell命令

int wait(int *status)

等待子进程中断或结束

pid_t waitpid(pid_t pid,int* status,int options)

等待子进程中断或结束

Socket编程

建立TCP连接
服务端:
Socket 封装底层逻辑,为应用程序提供便捷的通信接口
创建时需要指定:传输层协议和地址簇(IPv4/IPv6)
Bind 为socket绑定IP地址和端口号
Listen 设置为监听模式,设置最大连接数
Accept 接收连接,返回一个用于通信的新socket
Read/write 数据交换
Close 断开连接
客户端:
Socket
Connect
Read/write
Close

迭代服务器 一种服务器处理模式,特点是一次只处理一个请求,与之对应的是并发服务器。
就是把服务端上的accept,read/write,close等放到一个循环中,以便能多次接收客户端的请求。
回声服务器 把收到的数据原封不动的回复。用于测试
TCP套接字的 I/O缓冲 TCP协议在数据传输过程中,用来临时存放数据的内存区域,分发送缓冲区,和接收缓冲区。

TCP和UDP

TCP协议三次握手,四次挥手

UDP适用于实时音视频传输,因为更看重实时性,即便有丢包也只会造成短暂的画面抖动或杂音。
TCP能保证数据的完整性。适合用来传输重要的压缩文件。

TCP通常比UDP慢,有两个原因:
1.收发数据前后进行的连接设置及清理过程
2.收发数据过程中卫保证可靠性而添加的流控制
尤其是收发的数据量小但需要频繁连接时,UDP比TCP更高效

UDP中的服务器端和客户端没有连接。只有创建套接字的过程和数据交换过程

TCP中,服务端与每一个客户端通信都需要一个单独的套接字。而UDP中,无论与多少个客户端通信,服务端都只需要一个套接字。

对于UDP,调用sendto函数时自动分配IP和端口号。也就是说,UDP客户端中通常无需额外的地址分配过程。

TCP:服务端和客户端建立连接
服务端:
建立socket
bind给socket绑定IP和端口号
Listen开始监听
accept接收连接,三次握手在这里,返回一个新的用于通信的socket

客户端:
建立socket
connect 主动连接
数据交换:
Read、write

建立一个简单的TCP连接

初学阶段,如果搞两台主机来建立通信,先不说通信上的各种问题,但是运行调试就很麻烦。
所以为了更易于学习,在一个程序的不同进程中来实现服务端和客户端。

服务端

创建socket

struct sockaddr_in seraddr,cliaddr;//创建地址结构体
socklen_t cliaddrlen = sizeof(cliaddr);//客户端地址长度,socklen_t通常是一个无符号整型
// 创建socket
int server,client;//创建套接字
server = socket(PF_INET, SOCK_STREAM, 0);

不出意外的话,这里就得到了套接字,而要是server<0说明创建套接字失败了。

if (server < 0) {std::cout << "create socket failed!" << std::endl;}

socket函数:int socket(int domain, int type, int protocol);
域为PF_INET表示IPv4
类型为SOCK_STREAM表示TCP
protocol通常为0

struct sockaddr_in 是一个用来描述Internet地址的结构体
linux系统中的定义(c语言):

struct sockaddr_in {sa_family_t    sin_family;  // 地址族,通常为 AF_INET(IPv4)uint16_t       sin_port;    // 端口号,网络字节序(大端模式)struct in_addr sin_addr;    // IPv4 地址,网络字节序char           sin_zero[8]; // 填充字节,必须全为0(用于与 sockaddr 兼容)
};

绑定socket到地址和端口

memset(&seraddr, 0, sizeof(seraddr)); // 初始化地址结构体
seraddr.sin_family = AF_INET; // IPv4地址
seraddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 监听所有可用接口
seraddr.sin_port = htons(8888); // 端口号
int ret = bind(server, (struct sockaddr*)&seraddr, sizeof(seraddr));
if (ret == -1) {std::cout << "bind failed!" << std::endl;close(server);return;
}

sockaddr是一个通用的套接字地址结构体,定义在头文件sys/socket.h中,它包含了一些必要的字段,但字段并不具体:

struct sockaddr {sa_family_t sa_family;    // 地址族(例如 AF_INET, AF_INET6)char        sa_data[14];  // 地址数据,具体含义依赖于地址族
};

sockaddr_in 是专门用于IPv4的套接字地址结构体。 定义在头文件netinet/in.h

struct sockaddr_in {sa_family_t    sin_family;  // 地址族,对于IPv4地址,通常是 AF_INETuint16_t       sin_port;    // 端口号(网络字节序)struct in_addr sin_addr;    // IPv4地址char           sin_zero[8]; // 填充字节,为了保持与 struct sockaddr 结构的大小一致
};

监听连接

ret = listen(server, 3); // 最多允许3个待处理连接
if (ret == -1) {std::cout << "listen failed!" << std::endl;close(server);return;
}

接受连接

client = accept(server, (struct sockaddr*)&cliaddr, &cliaddrlen);
if (client == -1) {std::cout << "accept failed!" << std::endl;close(server);return;
}

发送消息

//向客户端发送消息
const char message[] = "Hello World!"; //要发送的消息
ssize_t len = write(client, message, strlen(message));
if (len != (ssize_t)strlen(message)) {std::cout << "write failed!" << std::endl;close(server);return;
}

关闭套接字

close(client);
close(server);

服务端完整代码

//头文件
#include <iostream> // 包含标准输入输出流库
#include <cstring>  // 包含memset等字符串处理函数
#include <unistd.h> // 包含close函数
#include <arpa/inet.h> // 包含inet_addr, htons等网络地址转换函数
#include <sys/types.h> // 包含数据类型定义
#include <sys/socket.h> // 包含socket编程相关函数和结构体
#include <netinet/in.h> // 包含sockaddr_in结构体定义
void lession_ser()
{// 创建用于服务器端的socketint server; // 服务器socket描述符int client; // 客户端socket描述符(由accept返回)struct sockaddr_in seraddr, cliaddr; // 服务器端和客户端的地址结构体socklen_t cliaddrlen = sizeof(cliaddr); // 客户端地址长度// 创建socketserver = socket(PF_INET, SOCK_STREAM, 0);if (server < 0) {std::cout << "create socket failed!" << std::endl;return; // 创建失败,退出函数}// 绑定socket到指定地址和端口memset(&seraddr, 0, sizeof(seraddr)); // 清零结构体seraddr.sin_family = AF_INET; // 设置地址族为IPv4seraddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 绑定到所有可用接口seraddr.sin_port = htons(9527); // 设置端口号为9527(网络字节序)int ret = bind(server, (struct sockaddr*)&seraddr, sizeof(seraddr));if (ret == -1) {std::cout << "bind failed!" << std::endl;close(server); // 绑定失败,关闭socketreturn;}// 开始监听连接请求ret = listen(server, 3); // 监听队列长度为3printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);if (ret == -1) {std::cout << "listen failed!" << std::endl;close(server); // 监听失败,关闭socketreturn;}// 接受一个客户端连接printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__); // 打印当前文件名、行号和函数名(调试用)client = accept(server, (struct sockaddr*)&cliaddr, &cliaddrlen);if (client == -1) {std::cout << "accept failed!" << std::endl;close(server); // 接受失败,关闭服务器socketreturn;}// 向客户端发送数据printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__); // 打印当前文件名、行号和函数名(调试用)const char message[] = "Hello World!"; // 要发送的消息ssize_t len = write(client, message, strlen(message)); // 发送消息if (len != (ssize_t)strlen(message)) {std::cout << "write failed!" << std::endl;close(server); // 发送失败,关闭服务器socket(这里应该也关闭client,但示例中未做)return;}// 关闭socketclose(client); // 关闭客户端socketclose(server); // 关闭服务器socket// 注释:在实际应用中,通常服务器不会立即关闭,而是会继续监听新的连接。// 此处关闭是为了示例简洁。
}

客户端

// 客户端运行函数
void run_client()
{// 创建一个套接字int client = socket(PF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr; // 服务器地址结构体memset(&servaddr, 0, sizeof(servaddr)); // 将结构体清零servaddr.sin_family = AF_INET; // 设置地址族为IPv4servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置服务器IP地址为127.0.0.1(本地回环地址)servaddr.sin_port = htons(8888); // 设置服务器端口号为9527(网络字节序)// 连接到服务器int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));if (ret == 0) { // 连接成功printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__); // 打印当前文件名、行号和函数名char buffer[256] = ""; // 创建接收数据的缓冲区read(client, buffer, sizeof(buffer)); // 从服务器读取数据到缓冲区std::cout << buffer; // 输出接收到的数据}else { // 连接失败printf("%s(%d):%s %d\n", __FILE__, __LINE__, __FUNCTION__, ret); // 打印错误信息}close(client); // 关闭套接字std::cout << "client done!" << std::endl; // 打印客户端完成信息
}// 示例函数:演示父子进程间的通信
void lession()
{pid_t pid = fork(); // 创建子进程std::cout << pid << std::endl;if (pid == 0) { // 如果是子进程// 等待一秒以确保服务器进程先启动sleep(1);run_client(); // 运行客户端}else if (pid > 0) { // 如果是父进程printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__); // 打印当前文件名、行号和函数名lession_ser(); // 运行服务器int status = 0; // 用于存储子进程退出状态的变量wait(&status); // 等待子进程结束}else { // fork失败std::cout << "fork failed!" << pid << std::endl; // 打印错误信息}
}

运行

int main(int argc, char* argv[])
{lession();
}

结果

3311
/root/projects/ConsoleApplication5/main.cpp(103):lession
/root/projects/ConsoleApplication5/main.cpp(39):lession_ser
/root/projects/ConsoleApplication5/main.cpp(46):lession_ser
0
/root/projects/ConsoleApplication5/main.cpp(98):lession
/root/projects/ConsoleApplication5/main.cpp(79):run_client
/root/projects/ConsoleApplication5/main.cpp(54):lession_ser
Hello World!client done!

注,我在调试过程中发现accept失败的情况,原因是我的客户端地址长度没有初始化:
socklen_t cliaddrlen;// =sizeof(cliaddr); // 客户端地址长度

socket编程TCP连接:实验一 回声服务器

//与上述实现相比,这里用了迭代服务器,建立了两次连接。每次连接进行5次通信。

#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <iostream>
#include <chrono>//void error_handling(char* message);void lession_ser()
{//创建socketint server;int client;struct sockaddr_in addr, cliaddr;socklen_t cliaddrlen = sizeof(cliaddr); // 客户端地址长度server = socket(PF_INET, SOCK_STREAM, 0);if (server < 0) {std::cout << "create socket failed!" << std::endl;return;}//bindmemset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("0.0.0.0");addr.sin_port = htons(8888);int ret = bind(server, (struct sockaddr*)&addr, sizeof(addr));if (ret == -1) {std::cout << "bind failed!" << std::endl;close(server);return;}//listenret = listen(server, 3);printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);if (ret == -1) {std::cout << "listen failed!" << std::endl;close(server);return;}char buffer[1024]{};for (int i=0;i<2;i++) {//acceptprintf("准备第%d次连接\n", i);client = accept(server, (struct sockaddr*)&cliaddr, &cliaddrlen);if (client == -1) {std::cout << "accept failed!" << std::endl;close(server);return;}//返回客户端发送的信息ssize_t len = 0;while (len = read(client, buffer, sizeof(buffer))) {len = write(client, buffer, len);if (len < 0) {std::cout << "write failed!" << std::endl;goto keep1;}memset(buffer, 0, len);}if (len <= 0) {std::cout << "read failed!" << std::endl;goto keep1;}keep1://close//可以不执行,因为服务端关闭的时候,客户端会自动关闭printf("socket\"client\"关闭!");close(client);}close(server);}void run_client()
{int client = socket(PF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");servaddr.sin_port = htons(8888);int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));int i{5};while (ret == 0 && i--) {printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);auto now = std::chrono::system_clock::now();std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);char* buffer = std::ctime(&now_time_t);write(client, buffer, sizeof(buffer));memset(buffer, 0, sizeof(buffer));read(client, buffer, sizeof(buffer));std::cout << buffer;}printf("red=%d\n", ret);close(client);std::cout << "client done!" << std::endl;
}#include <sys/wait.h>
#include "main.h"
void lession()
{pid_t pid = fork();std::cout << pid << std::endl;if (pid == 0) {//开启客户端printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);sleep(1);run_client();run_client();}else if (pid > 0) {printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);lession_ser();int status = 0;std::cout << "子进程\"" << wait(&status) << "\"结束!" << std::endl;}else {std::cout << "fork failed!" << pid << std::endl;}
}int main(int argc, char* argv[])
{lession();
}

运行结果
在这里插入图片描述
上述代码存在的问题:
(1)char* buffer,对buffer求长度时,用sizeof(buffer)得到的是指针类型的长度4/8,用sizeof(buffer)得到的是1。正确求法是用strlen(buffer);
用char
buffer是为了接收获取到的时间信息,但继续用buffer作为接收缓冲区,其缓冲区就很小了(我这里只有25)。
所以:read和write时,要注意缓冲区的大小、count参数等信息。避免数据丢失。

socket编程TCP连接:实验二

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>//sockaddr_in、htons()
#include <string.h>
#include <arpa/inet.h>//inet_addr()、netinet/in.h
#include <unistd.h> // close()int compute(int count, int oprand[], char op) {int result = 0;switch (op) {case'+':for (int i = 0; i < count; i++)result += oprand[i];break;case'-':for (int i = 0; i < count; i++)result -= oprand[i];break;case'*':result = 1;for (int i = 0; i < count; i++)result *= oprand[i];break;default:break;}return result;
}void tcp_server() {//创建socketint server = socket(PF_INET, SOCK_STREAM, 0);if(server < 0)return;struct sockaddr_in addr;//绑定IP、端口memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("0.0.0.0");addr.sin_port = htons(8888);int ret = bind(server, (struct sockaddr*)&addr, sizeof(addr));if (ret == -1) {close(server);return;}//listenret = listen(server, 3);if (ret == -1) {close(server);return;}//acceptstruct sockaddr_in cliaddr;socklen_t cliaddrlen = sizeof(cliaddr);char buffer[1024]{};while (1) {memset(buffer, 0, sizeof(buffer));int client = accept(server, (struct sockaddr*)&cliaddr, &cliaddrlen);if (client == -1) {close(server);return;}//readsize_t len = 0;len = read(client, buffer, 1);int result = 0;if (len > 0) {//加&0xFF的原因:当buffer[0]大于128时,其最高位为1,强制转换过程的右移会加1,对其结果进行&0xFF可以将高位多出的1变为0;for (int i = 0; i < ((unsigned)buffer[0] & 0xFF); i++)read(client, buffer + 1 + i * 4, 4);read(client, buffer + 1 + ((unsigned)buffer[0] & 0xFF)*4,1);result = compute(((unsigned)buffer[0]&0xFF), (int*)(buffer + 1), buffer[((unsigned)buffer[0] & 0xFF) * 4 + 1]);write(client, &result, 4);}close(client);}close(server);
}void tcp_client() {//创建socketint client = socket(PF_INET, SOCK_STREAM, 0);//connectstruct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("127.0.0.1");addr.sin_port = htons(8888);int ret = connect(client, (struct sockaddr*) & addr, sizeof(addr));char buffer[1024];while (ret == 0) {//memset(buffer,0,sizeof(buffer));fputs("Operand count:", stdout);int opnd_cnt = 0;scanf("%d", &opnd_cnt);if (opnd_cnt < 2 && opnd_cnt>255) {fputs("Error:opnd_cnt too small or opnd_cnt too big!\n", stdout);close(client);printf("client done!");return;}buffer[0] = (char)opnd_cnt;//服务器需要将buffer[0]解释为无符号类型for (int i = 0; i < opnd_cnt; i++)scanf("%d", buffer + 1 + i * 4);fgetc(stdin);fputs("Operator:", stdout);buffer[1 + opnd_cnt * 4] = fgetc(stdin);size_t len = opnd_cnt * 4 + 2;//strlen(buffer);size_t send_len = 0;printf("len = %d\n",len);while (send_len < len) {ssize_t ret = write(client, buffer + send_len, len - send_len);if (ret <= 0) {fputs("write failed!\n", stdout);close(client);printf("client done!\n");return;}send_len += (size_t)ret;}memset(buffer, 0, strlen(buffer));//准备接收服务器运算的结果if (read(client, buffer, sizeof(buffer)) <= 0) {printf("read failed!\n");close(client);return;}printf("from server:%d\n", *(int*)buffer);}close(client);printf("client done!\n");
}#include <sys/wait.h>
void test() {pid_t pid = fork();printf("---pid = %d---\n", pid);if (pid == 0) {//开启客户端sleep(1);tcp_client();tcp_client();}else if (pid > 0) {tcp_server();int status{};printf("子进程\"%d\"结束!", wait(&status));}else {printf("fork failed!");}
};int main() {test();
}

在这里插入图片描述
有问题:频繁出现read failed!
找原因:
客户端接收数据部分,只read一次,并且没有收到就结束,这样是有问题的。
因为服务端需要计算结果后发送给客户端。一旦客户端在服务端结果发送出来之前read,必然收不到数据(read函数返回0)。

修改后代码:

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>//sockaddr_in、htons()
#include <string.h>
#include <arpa/inet.h>//inet_addr()、netinet/in.h
#include <unistd.h> // close()int compute(int count, int oprand[], char op) {int result = 0;switch (op) {case'+':for (int i = 0; i < count; i++)result += oprand[i];break;case'-':for (int i = 0; i < count; i++)result -= oprand[i];break;case'*':result = 1;for (int i = 0; i < count; i++)result *= oprand[i];break;default:break;}return result;
}void tcp_server() {//创建socketint server = socket(PF_INET, SOCK_STREAM, 0);if(server < 0)return;struct sockaddr_in addr;//绑定IP、端口memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("0.0.0.0");addr.sin_port = htons(8888);int ret = bind(server, (struct sockaddr*)&addr, sizeof(addr));if (ret == -1) {close(server);return;}//listenret = listen(server, 3);if (ret == -1) {close(server);return;}//acceptstruct sockaddr_in cliaddr;socklen_t cliaddrlen = sizeof(cliaddr);char buffer[1024]{};while (1) {memset(buffer, 0, sizeof(buffer));int client = accept(server, (struct sockaddr*)&cliaddr, &cliaddrlen);if (client == -1) {close(server);return;}//readsize_t len = 0;len = read(client, buffer, 1);int result = 0;if (len > 0) {//加&0xFF的原因:当buffer[0]大于128时,其最高位为1,强制转换过程的右移会加1,对其结果进行&0xFF可以将高位多出的1变为0;for (int i = 0; i < ((unsigned)buffer[0] & 0xFF); i++)read(client, buffer + 1 + i * 4, 4);read(client, buffer + 1 + ((unsigned)buffer[0] & 0xFF)*4,1);result = compute(((unsigned)buffer[0]&0xFF), (int*)(buffer + 1), buffer[((unsigned)buffer[0] & 0xFF) * 4 + 1]);write(client, &result, 4);}close(client);}close(server);
}void tcp_client() {//创建socketint client = socket(PF_INET, SOCK_STREAM, 0);//connectstruct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("127.0.0.1");addr.sin_port = htons(8888);int ret = connect(client, (struct sockaddr*) & addr, sizeof(addr));char buffer[1024];while (ret == 0) {//memset(buffer,0,sizeof(buffer));fputs("Operand count:", stdout);int opnd_cnt = 0;scanf("%d", &opnd_cnt);if (opnd_cnt < 2 && opnd_cnt>255) {fputs("Error:opnd_cnt too small or opnd_cnt too big!\n", stdout);close(client);printf("client done!");return;}buffer[0] = (char)opnd_cnt;//服务器需要将buffer[0]解释为无符号类型for (int i = 0; i < opnd_cnt; i++)scanf("%d", buffer + 1 + i * 4);fgetc(stdin);fputs("Operator:", stdout);buffer[1 + opnd_cnt * 4] = fgetc(stdin);size_t len = opnd_cnt * 4 + 2;//strlen(buffer);printf("len = %d\n", len);size_t send_len = 0;while (send_len < len) {ssize_t ret = write(client, buffer + send_len, len - send_len);if (ret <= 0) {fputs("write failed!\n", stdout);close(client);printf("client done!\n");return;}send_len += (size_t)ret;}memset(buffer, 0, strlen(buffer));//准备接收服务器运算的结果size_t read_len = 0;len = 4;while (read_len < 4){size_t ret = read(client, buffer + read_len, len - read_len);if (ret <= 0) {fputs("read failed!\n", stdout);close(client);std::cout << "client done!" << std::endl;return;}read_len += (size_t)ret;}printf("from server:%d\n", *(int*)buffer);}close(client);printf("client done!\n");
}#include <sys/wait.h>
void test() {pid_t pid = fork();printf("---pid = %d---\n", pid);if (pid == 0) {//开启客户端sleep(1);tcp_client();tcp_client();}else if (pid > 0) {tcp_server();int status{};printf("子进程\"%d\"结束!", wait(&status));}else {printf("fork failed!");}
};int main() {test();
}

在这里插入图片描述
还是有问题:每次运行,第二次计算都会出现read failed!
找原因:
客户端用了while(red==0),并且如果发送接收正常,while循环没有终止。而服务端一旦操作完成(计算并发送),就会close通信的socket。
此时,客户端write能够正常发出,但read就会返回-1。
修改后代码:

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>//sockaddr_in、htons()
#include <string.h>
#include <arpa/inet.h>//inet_addr()、netinet/in.h
#include <unistd.h> // close()int compute(int count, int oprand[], char op) {int result = 0;switch (op) {case'+':for (int i = 0; i < count; i++)result += oprand[i];break;case'-':for (int i = 0; i < count; i++)result -= oprand[i];break;case'*':result = 1;for (int i = 0; i < count; i++)result *= oprand[i];break;default:break;}std::cout << __LINE__ << ":result=" << result << std::endl;return result;
}void tcp_server() {//创建socketint server = socket(PF_INET, SOCK_STREAM, 0);if(server < 0)return;struct sockaddr_in addr;//绑定IP、端口memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("0.0.0.0");addr.sin_port = htons(8888);int ret = bind(server, (struct sockaddr*)&addr, sizeof(addr));if (ret == -1) {close(server);return;}//listenret = listen(server, 3);if (ret == -1) {close(server);return;}//acceptstruct sockaddr_in cliaddr;socklen_t cliaddrlen = sizeof(cliaddr);char buffer[1024]{};while (1) {memset(buffer, 0, sizeof(buffer));int client = accept(server, (struct sockaddr*)&cliaddr, &cliaddrlen);if (client == -1) {close(server);std::cout << "accept failed!---server done!!!" << std::endl;return;}//readsize_t len = 0;len = read(client, buffer, 1);std::cout << __LINE__ <<":buffer[0] = "<<buffer[0]<< std::endl;int result = 0;if (len > 0) {//加&0xFF的原因:当buffer[0]大于128时,其最高位为1,强制转换过程的右移会加1,对其结果进行&0xFF可以将高位多出的1变为0;for (int i = 0; i < ((unsigned)buffer[0] & 0xFF); i++)read(client, buffer + 1 + i * 4, 4);read(client, buffer + 1 + ((unsigned)buffer[0] & 0xFF)*4,1);std::cout << __LINE__ << std::endl;result = compute(((unsigned)buffer[0]&0xFF), (int*)(buffer + 1), buffer[((unsigned)buffer[0] & 0xFF) * 4 + 1]);write(client, &result, 4);}std::cout << __LINE__ << "服务端已计算完成并发送!!!\n准备结束当前通信的socket,进入下一次循环,重新建立新的连接。" << std::endl;close(client);}close(server);
}void tcp_client() {//创建socketint client = socket(PF_INET, SOCK_STREAM, 0);//connectstruct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("127.0.0.1");addr.sin_port = htons(8888);int ret = connect(client, (struct sockaddr*) & addr, sizeof(addr));char buffer[1024];if (ret == 0) {//memset(buffer,0,sizeof(buffer));fputs("Operand count:", stdout);int opnd_cnt = 0;scanf("%d", &opnd_cnt);if (opnd_cnt < 2 && opnd_cnt>255) {fputs("Error:opnd_cnt too small or opnd_cnt too big!\n", stdout);close(client);printf("client done!");return;}buffer[0] = (char)opnd_cnt;//服务器需要将buffer[0]解释为无符号类型for (int i = 0; i < opnd_cnt; i++)scanf("%d", buffer + 1 + i * 4);fgetc(stdin);fputs("Operator:", stdout);buffer[1 + opnd_cnt * 4] = fgetc(stdin);size_t len = opnd_cnt * 4 + 2;//strlen(buffer);printf("len = %d\n", len);size_t send_len = 0;while (send_len < len) {ssize_t ret = write(client, buffer + send_len, len - send_len);if (ret <= 0) {fputs("write failed!\n", stdout);close(client);printf("client done!\n");return;}send_len += (size_t)ret;}std::cout << "Client sent successfully!!!" << std::endl;memset(buffer, 0, strlen(buffer));//准备接收服务器运算的结果size_t read_len = 0;len = 4;while (read_len < 4){size_t ret = read(client, buffer + read_len, len - read_len);if (ret <= 0) {fputs("read failed!\n", stdout);close(client);std::cout << "client done!" << std::endl;return;}read_len += (size_t)ret;}printf("from server:%d\n", *(int*)buffer);}close(client);printf("client done!\n");
}#include <sys/wait.h>
void test() {pid_t pid = fork();printf("---pid = %d---\n", pid);if (pid == 0) {//开启客户端sleep(1);tcp_client();tcp_client();}else if (pid > 0) {tcp_server();int status{};printf("子进程\"%d\"结束!", wait(&status));}else {printf("fork failed!");}
};int main() {test();
}

在这里插入图片描述

相关文章:

Linux(socket网络编程)TCP连接

Linux&#xff08;socket网络编程&#xff09;TCP连接 基础文件目录函数系统进程控制函数fork()exec系列函数void abort(void)void assert(int expression)void exit(int status)void _exit(int status)int atexit(void (*func)(void))int on_exit(void (*function)(int,void*)…...

深入 JVM 虚拟机:字符串常量池演变与 intern() 方法工作原理解析

🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template 🌺 仓库主页: GitCode︱ Gitee ︱ Github 💖 欢迎点赞 👍 收藏 ⭐评论 📝 如有错误敬请纠正! 前言 在 Java 开发中,字符串常量池(String Constant…...

从零开始学习人工智能

从零开始学习人工智能可以按照以下步骤进行&#xff1a; 一、了解人工智能的基本概念 学习内容&#xff1a;了解人工智能的定义、发展历程、主要研究方向&#xff08;如机器学习、深度学习、自然语言处理、计算机视觉等&#xff09;、常见应用&#xff08;如语音识别、图像识别…...

解锁电商数据宝藏:淘宝商品详情API实战指南

在电商蓬勃发展的今天&#xff0c;数据已成为驱动业务增长的核心引擎。对于商家、开发者以及数据分析师而言&#xff0c;获取精准、实时的商品数据至关重要。而淘宝&#xff0c;作为国内最大的电商平台&#xff0c;其海量商品数据更是蕴含着巨大的价值。 本文将带你深入探索淘…...

Gui-Guider1.8.1 数字时钟控件找不到定义,无法编译

我们在Gui-Guider中使用的一些控件&#xff0c;生成后会发现在LVGL源码中找不到该控件的定义&#xff0c;这时因为Gui-Guider中的一些控件是其自己编写的而不是LVGL提供的&#xff0c;那么我们该如何应用呢&#xff1f;这里拿Digital Clock数字时钟控件举例&#xff1a; 这里我…...

多模态模型详解

多模态模型是什么 多模态模型是一种能够处理和理解多种数据类型&#xff08;如文本、图像、音频、视频等&#xff09;的机器学习模型&#xff0c;通过融合不同模态的信息来提升任务的性能。其核心在于利用不同模态之间的互补性&#xff0c;增强模型的鲁棒性和准确性。 如何融合…...

Unity3D实现显示模型线框(shader)

系列文章目录 unity工具 文章目录 系列文章目录👉前言👉一、效果展示👉二、第一种方式👉二、第二种方式👉壁纸分享👉总结👉前言 在 Unity 中显示物体线框主要基于图形渲染管线和特定的渲染模式。 要显示物体的线框,通常有两种常见的方法:一种是利用内置的渲染…...

【实测】用全志A733平板搭建一个端侧Deepseek算力平台

随着DeepSeek 的蒸馏技术的横空出世&#xff0c;端侧 SoC 芯片上运行大模型成为可能。那么端侧芯片跑大模型的效果如何呢&#xff1f;本文将在全志 A733 芯片平台上部署一个 DeepSeek-R1:1.5B 模型&#xff0c;并进行实测效果展示。 端侧平台环境 设备&#xff1a;全志A733平板…...

新数据结构(7)——Object

Object类是所有类的父类&#xff0c;在 Java 中&#xff0c;每个类都直接或间接地继承自Object类&#xff0c;也就是说所有类都是object类的子类可以使用Object里的方法。 equals()和hashCode()是Java中Object类所包含的两个关键方法&#xff0c;下面将介绍两个方法。 和equa…...

数据结构-栈和队列的应用

目录 前言一、栈的应用&#xff08;迷宫问题&#xff09;1.1 问题描述1.2 算法选择1.3 算法精化1.4 算法实现1.5 问题结果 二、队列的应用&#xff08;农夫过河问题&#xff09;2.1 问题描述2.2 算法选择2.3 算法精化2.4 算法实现2.5 问题结果 总结 前言 本篇文章使用两个例子…...

【JavaScript】异步编程汇总

异步编程解决方案&#xff1a; 回调函数PromiseGeneratorawait / async 回调函数 回调函数是早期处理异步编程的主要方式&#xff0c;虽然它本身存在很多的缺陷&#xff0c;比如那个时候对于复杂的异步处理常常会出现回调地狱。 但是因为 JavaScript 中当时并没有很好的API来帮…...

【AI系列】从零开始学习大模型GPT (2)- Build a Large Language Model (From Scratch)

前序文章 【AI系列】从零开始学习大模型GPT (1)- Build a Large Language Model (From Scratch) Build a Large Language Model 背景第1章:理解大型语言模型第2章:处理文本数据第3章:编码Attention机制什么是Attention机制?Attention机制的基本原理数学表示应用总结为什么要…...

动态规划——路径问题②

文章目录 931. 下降路径最小和算法原理代码实现 64. 最小路径和算法原理代码实现 174. 地下城游戏算法原理代码实现 931. 下降路径最小和 题目链接&#xff1a;931. 下降路径最小和 算法原理 状态表示&#xff1a; 经验题目要求&#xff1a;dp[i][j]表示到达[i,j]位置时&…...

【每日关注】科技圈重要动态

时代新动态 2025 年 2 月 12 日科技圈重要动态总结全球 AI 治理新进展巴黎 AI 宣言签署&#xff0c;美英缺席 科技巨头合作与竞争苹果联姻阿里开发中国版AI功能DeepSeek生态持续扩展OpenAI拒绝马斯克收购&#xff0c;矛盾公开化 汽车行业动态小米汽车销量跃居新势力第二比亚迪智…...

Postgresql的三种备份方式_postgresql备份

这种方式可以在数据库正在使用的时候进行完整一致的备份&#xff0c;并不阻塞其它用户对数据库的访问。它会产生一个脚本文件&#xff0c;里面包含备份开始时&#xff0c;已创建的各种数据库对象的SQL语句和每个表中的数据。可以使用数据库提供的工具pg_dumpall和pg_dump来进行…...

Linux 配置 MySQL 定时自动备份到另一台服务器

Linux 配置 MySQL 定时自动备份到另一台服务器这里写自定义目录标题 前言1、配置服务器通信1.1&#xff1a;配置过程 2、编写自动备份sh脚本文件3&#xff1a;设置定时自动执行 前言 此方案可使一台服务器上的 MySQL 中的所有数据库每天 0 点自动转储为 .sql 文件&#xff0c;…...

CCF-GESP 等级考试 2024年6月认证C++二级真题解析

2024年6月真题 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 正确答案&#xff1a;C 考察知识点&#xff1a;计算机基础与编程环境 解析&#xff1a;CCF 组织的 GESP 认证考试第 1 级可选择的认证语言有 Scratch、Python、C &#xff0c;共 3 种。答案为C。 …...

vm虚拟机的一些操作命令

PowerShell命令 // 获取虚拟机列表&#xff1a; get-vm // 创建虚拟机&#xff1a; new-vm -Name "BrioDev75" -MemoryStartupBytes 16GB -Path "D:\Hyper-V" // 删除虚拟机&#xff1a; remove-vm -Name "BrioDev75" -Force (-Force参数是…...

sql难点

一、 假设你有一个查询&#xff0c;需要根据 id 是否为 null 来动态生成 SQL 条件&#xff1a; xml复制 <select id"getResources" resultType"Resource">SELECT * FROM resources<where><if test"id ! null">and id <!…...

【多模态大模型】系列1:Transformer Encoder——ViLT、ALBEF、VLMO

目录 1 ViLT2 ALBEF3 VLMO 1 ViLT ViLT: Vision-and-Language Transformer Without Convolution or Region Supervision 图文多模态任务&#xff0c;关键是提取视觉特征和文本特征&#xff0c;然后对齐。在之前的多模态研究工作中&#xff0c;视觉侧通常需要一个目标检测器来…...

2.4 测试数据与初始化

测试数据与初始化 在 Spring Test 中&#xff0c;合理管理测试数据的初始化和清理是保证测试可靠性的关键。本章将介绍多种数据准备方式&#xff0c;涵盖 SQL 脚本执行、编程式初始化 和 动态数据生成&#xff0c;并提供最佳实践示例。 1. 使用 Sql 执行 SQL 脚本 作用 在测…...

DataBase【MySQL基础夯实使用说明(中)】

MySQL数据库 &#x1f3c6;当领导问你忙不忙&#xff0c;您怎么回复&#xff1f; &#x1f514;要让领导知道你很忙&#xff0c;但是你的事情紧急&#xff0c;我可以优先处理&#xff01; 文章目录 MySQL数据库前言一、SQL&#xff08;Structured Query Language&#xff09;1…...

Unity3D Shader 简析:变体与缓存详解

引言 在 Unity3D 中&#xff0c;Shader 是渲染管线的核心部分&#xff0c;负责控制物体的外观和材质表现。Shader 的变体&#xff08;Variants&#xff09;和缓存机制是优化渲染性能的关键。本文将深入探讨 Unity3D 中 Shader 变体的概念、缓存机制以及如何通过代码实现和管理…...

vuex基础介绍

/store/index.js import Vue from vue import Vuex from vuexVue.use(Vuex)/*** 创建并导出一个 Vuex 仓库实例* 仓库是一个存储应用所有状态的容器&#xff0c;并且提供了修改和获取状态的方法*/ export default new Vuex.Store({// state 是一个对象&#xff0c;用于存储应…...

OpenWRT中常说的LuCI是什么——LuCI介绍(一)

我相信每个玩openwrt的小伙伴都或多或少看到过luci这个东西&#xff0c;但luci到底是什么东西&#xff0c;可能还不够清楚&#xff0c;今天就趁机来介绍下&#xff0c;openwrt中的luci&#xff0c;到底是个什么东西。 什么是LuCI&#xff1f; 首先&#xff0c;LuCI是OpenWRT中…...

singleTaskAndroid的Activity启动模式知识点总结

一. 前提知识 1.1. 任务栈知识 二. Activity启动模式的学习 2.1 standard 2.2 singleTop 2.3.singleTask 2.4.singleInstance 引言&#xff1a; Activity作为四大组件之一&#xff0c;也可以说Activity是其中最重要的一个组件&#xff0c;其负责调节APP的视图&#xff…...

DeepSeek-V3 技术报告

1.摘要 为了减少开源模型与闭源模型的能力差距&#xff0c;我们提出了DeepSeek-V3&#xff0c;一个大的混合专家模型&#xff08;Mixture-of-Experts (MoE) &#xff09;&#xff0c;有6710亿参数&#xff0c;每个token会激活370亿参数。 DeepSeek-V3采用多头隐注意力&#xf…...

Vue 3 30天精进之旅:Day 22 - 构建和部署

欢迎回来&#xff01;在我们的Vue 3学习旅程的第22天&#xff0c;我们将探讨应用的构建和部署。在完成了我们的应用开发后&#xff0c;下一步就是如何将其部署到服务器&#xff0c;使得用户可以访问。 1. 构建Vue应用 构建Vue应用是将我们在本地开发的代码打包成生产环境可用…...

Ansible中Playbook的逻辑控制语句-when

playbook的逻辑控制语句 when 条件判断语句&#xff0c;类似if loop 循环语句&#xff0c;类似loop block 将几个任务组成一个代码块&#xff0c;便于针对一组操作的异常进行处理 when的基本用法 when的运算符操作 when关键字可以配合各种运算符进行操作&#xff0c;如下&…...

制造业物联网的十大用例

预计到 2026 年&#xff0c;物联网制造市场价值将达到 4000 亿美元。实时收集和分析来自联网物联网设备与传感器的数据&#xff0c;这一能力为制造商提供了对生产流程前所未有的深入洞察。物联网&#xff08;IoT&#xff09;有潜力彻底改变制造业&#xff0c;使工厂能够更高效地…...

InfiniBand与IP over InfiniBand(IPOIB):实现高性能网络通信的底层机制

在现代高性能计算(HPC)和数据中心环境中,网络通信的效率和性能至关重要。InfiniBand(IB)作为一种高性能的串行计算机总线架构,以其低延迟、高带宽和高可靠性而广泛应用于集群计算和数据中心。IP over InfiniBand(IPOIB)则是在InfiniBand网络上实现IP协议的一种方式,它…...

【通俗易懂说模型】一篇弄懂几个经典CNN图像模型(AlexNet、VGGNet、ResNet)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;深度学习_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …...

机器学习 | scikit-learn中分块拟合vs一次性拟合所有数据

Scikit-learn是一个广泛使用的机器学习Python库&#xff0c;提供了一系列分类、回归、聚类等算法。机器学习的关键挑战之一是处理无法一次性放入内存的大型数据集。本文探讨了使用scikit-learn将数据分块拟合与一次性拟合的策略&#xff0c;讨论了每种方法的优点和局限性。 大…...

两个同一对象targetList和 sourceList 去重

我现在需要解决的问题是从一个Java的源列表`sourceList`中移除所有在目标列表`targetList`中存在的数据,并且还要去除`targetList`中的重复数据。让我先理清楚这两个问题的思路。 首先,如何快速从`sourceList`中移除含有`targetList`的数据。这里的“含有”应该是指两个列表中…...

小游戏源码开发之可跨app软件对接是如何设计和开发的

专业小游戏开发的团队往往会面临跨领域和不同平台客户需要追加同一款游戏的需求&#xff0c;所以就要设计和开发一款可任意对接不同 App 软件的小游戏&#xff0c;那么针对这类需求小游戏开发团队早已有了成熟的解决方案&#xff0c;针对设计和开发可跨平台游戏对接大概流程简单…...

掌握正则表达式_模式匹配的艺术

当然,以下是《掌握正则表达式:模式匹配的艺术》文章内容,使用 Java 正则表达式,并包含丰富的代码示例: 1. 引言 1.1 正则表达式的定义与历史 正则表达式(Regular Expression,简称 regex 或 regexp)是一种用于描述文本模式的强大工具。它最初由数学家 Stephen Kleene…...

FacePoke详细使用指南:如何利用开源AI工具优化照片人物表情

文章目录 前言1. 本地使用FacePoke1.1 整合包方式安装1.2 Docker方式部署 2. FacePoke功能演示3. 公网使用FacePoke3.1 创建远程连接公网地址 4. 固定远程访问公网地址 前言 在数字创意的世界里&#xff0c;一款名为FacePoke的工具正以其风趣而强大的功能吸引着无数创作者的目…...

本地部署【LLM-deepseek】大模型 ollama+deepseek/conda(python)+openwebui/docker+openwebui

通过ollama本地部署deepseek 总共两步 1.模型部署 2.[web页面] 参考官网 ollama:模型部署 https://ollama.com/ open-webui:web页面 https://github.com/open-webui/open-webui 设备参考 Mac M 芯片 windows未知 蒸馏模型版本:deepseek-r1:14b 运行情况macminim2 24256 本地…...

分发饼干(力扣455)

从这道题开始我们就进入贪心算法的学习了。这个算法没有固定的套路&#xff0c;甚至题目之间的联系也很少&#xff0c;基本上每一道题都要当新题来写。我们能做的只有见多识广&#xff0c;这样才有机会在考试中根据以往经验解决贪心的题目。贪心的本质上就是找到局部最优解&…...

信息收集-主机服务器系统识别IP资产反查技术端口扫描协议探针角色定性

知识点&#xff1a; 1、信息收集-服务器系统-操作系统&IP资产 2、信息收集-服务器系统-端口扫描&服务定性 一、演示案例-应用服务器-操作系统&IP资产 操作系统 1、Web大小写(windows不区分大小写&#xff0c;linux区分大小写) 2、端口服务特征(22就是linux上的服…...

建筑兔零基础自学python记录18|实战人脸识别项目——视频检测07

本次要学视频检测&#xff0c;我们先回顾一下图片的人脸检测建筑兔零基础自学python记录16|实战人脸识别项目——人脸检测05-CSDN博客 我们先把上文中代码复制出来&#xff0c;保留红框的部分。 ​ 然后我们来看一下源代码&#xff1a; import cv2 as cvdef face_detect_demo(…...

vue-点击生成动态值,动态渲染回显输入框

1.前言 动态点击生成数值&#xff0c;回显输入框&#xff0c;并绑定。 2.实现 <template><div style"display:flex;align-items: center;flex-direction:row"><a-input:key"inputKey"v-model"uploadData[peo.field]"placehold…...

Idea 插件 Quickly-Code-Toolkit

使用说明 &#xff08;一&#xff09;全局设置 Paging Wrapper Setting&#xff08;分页设置&#xff09; 功能&#xff1a;主要用于在方法写入时&#xff0c;为返回参数提供分页包装类。设置方式&#xff1a;需准确填写分页包装类的全限定名&#xff0c;例如&#xff1a;com…...

fun-transformer学习笔记-Task1——Transformer、Seq2Seq、Encoder-Decoder、Attention之间的关系

Transformer、Seq2Seq、Encoder-Decoder、Attention由这四者之间的关系可以从模型架构的发展脉络来理解&#xff1a; Seq2Seq 与 Encoder–Decoder 模型 “Seq2Seq”&#xff08;sequence‐to‐sequence&#xff09;是一类用于将一个变长序列映射为另一个变长序列的任务&#x…...

使用瑞芯微RK3588的NPU进行模型转换和推理

使用边缘设备进行算法落地时&#xff0c;通常要考虑模型推理速度&#xff0c;NVIDA系列平台可以使用TensorRT和CUDA加速&#xff0c;瑞芯微RK3588的板子上都是Arm的手机GPU&#xff0c;虽然没有类似CUDA的加速计算方式&#xff0c;但是提供了NPU进行加速推理&#xff0c;本文说…...

mysql读写分离与proxysql的结合

上一篇文章介绍了mysql如何设置成主从复制模式&#xff0c;而主从复制的目的&#xff0c;是为了读写分离。 读写分离&#xff0c;拿spring boot项目来说&#xff0c;可以有2种方式&#xff1a; 1&#xff09;设置2个数据源&#xff0c;读和写分开使用 2&#xff09;使用中间件…...

Vue笔记(九)

一、文章分类架子--PageContainer 学习PageContainer组件的封装&#xff0c;这一组件用于搭建页面基础结构&#xff0c;为后续内容展示提供统一布局。它可能包含通用的页面样式、导航栏、侧边栏等基础元素的结构搭建。 在Vue组件中&#xff0c; <template> 标签内定义基础…...

YOLO11框架使用

YOLO11 1. Frame Understanding2. What can YOLO11 do?3.如何训练自己数据集?3.1 配置环境3.2 制作自己数据集3.3 配置文件3.3.1 数据集配置文件3.3.2 网络模块配置文件4.修改训练参数配置文件5. 训练脚本编写6.结果展示1. Frame Understanding 2. What can YOLO11 do? Ult…...

RK3588视觉控制器与AI 算法:开启工业视觉检测新境界

在实际应用中&#xff0c;工业相机拍摄产品的图像&#xff0c;RK3588 迅速接收并进行预处理。AI 算法随即对图像进行深入分析&#xff0c;提取特征并与预设的标准进行对比&#xff0c;从而准确判断是否存在缺陷。 例如&#xff0c;在电子元件生产线上&#xff0c;RK3588 和 AI…...

C语言基础入门:2.5基础输入输出

【C语言基础】输入输出完全指南&#xff1a;从printf到缓冲区安全 文章目录 【C语言基础】输入输出完全指南&#xff1a;从printf到缓冲区安全一、格式化输出艺术&#xff1a;printf函数详解二、scanf输入安全与缓冲区处理三、字符级交互&#xff1a;getchar与putchar实战程序员…...