根本
文件目录
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_INET
- uint16_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()
- {
- // 创建用于服务器端的socket
- int server; // 服务器socket描述符
- int client; // 客户端socket描述符(由accept返回)
- struct sockaddr_in seraddr, cliaddr; // 服务器端和客户端的地址结构体
- socklen_t cliaddrlen = sizeof(cliaddr); // 客户端地址长度
- // 创建socket
- server = 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; // 设置地址族为IPv4
- seraddr.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); // 绑定失败,关闭socket
- return;
- }
- // 开始监听连接请求
- ret = listen(server, 3); // 监听队列长度为3
- printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);
- if (ret == -1) {
- std::cout << "listen failed!" << std::endl;
- close(server); // 监听失败,关闭socket
- return;
- }
- // 接受一个客户端连接
- 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); // 接受失败,关闭服务器socket
- return;
- }
- // 向客户端发送数据
- 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;
- }
- // 关闭socket
- close(client); // 关闭客户端socket
- close(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; // 设置地址族为IPv4
- servaddr.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(){ //创建socket int 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; } //bind 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) { std::cout << "bind failed!" << std::endl; close(server); return; } //listen ret = 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++) { //accept printf("预备第%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() {
- //创建socket
- int 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;
- }
- //listen
- ret = listen(server, 3);
- if (ret == -1) {
- close(server);
- return;
- }
- //accept
- struct 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;
- }
- //read
- size_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() {
- //创建socket
- int client = socket(PF_INET, SOCK_STREAM, 0);
- //connect
- struct 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() {
- //创建socket
- int 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;
- }
- //listen
- ret = listen(server, 3);
- if (ret == -1) {
- close(server);
- return;
- }
- //accept
- struct 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;
- }
- //read
- size_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() {
- //创建socket
- int client = socket(PF_INET, SOCK_STREAM, 0);
- //connect
- struct 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() {
- //创建socket
- int 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;
- }
- //listen
- ret = listen(server, 3);
- if (ret == -1) {
- close(server);
- return;
- }
- //accept
- struct 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;
- }
- //read
- size_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() {
- //创建socket
- int client = socket(PF_INET, SOCK_STREAM, 0);
- //connect
- struct 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();
- }
复制代码
进一步相识TCP
Linux(socket网络编程)I/O缓冲、三次握手、四次挥手
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |