COW
写时复制机制(Copy On Write)可以提高进程创建效率。
子进程完整复制了父进程的地址空间,此时父子进程的虚拟内存空间映射到相同的物理内存空间。只有当二者之一执行了写入操作时才会复制写入区域的内容,为父子进程维护不同的物理页帧。
进程组
进程组ID(Process Group ID,PGID)在UNIX和类UNIX系统中用来标识一个或多个进程的集合。
进程组用于信号传递和终端控制。
会话是一个或多个进程组的集合。当用户登录到系统时,登录shell会成为一个新会话的领导。这个新会话会创建一个新的进程组,该组包含shell进程以及其子进程。
线程
线程是进程中的执行单元,它共享进程的资源和地址空间,但拥有自己的执行堆栈、程序计数器和一组寄存器。
由于线程共享相同进程内的资源,它们之间的通信和数据共享更容易。
TCP
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,广泛应用于互联网中。
它提供可靠的端到端通信,在发送数据之前,要在两个通信端点之间建立连接。
TCP通过一系列机制确保数据的可靠传输,这些机制包括序列号、确认应答、重传控制、流量控制和拥塞控制。
面向连接
TCP是一种面向连接的协议,这意味着在数据交换之前,两个通信端必须先建立连接。这个连接通过一个三次握手(SYN、SYN-ACK、ACK)来建立,确保双方都准备好数据交换。
可靠传输
TCP通过序列号和确认应答机制确保数据的可靠传输。发送方为每个报文段分配一个序列号,接收方通过发送确认应答(ACK)来确认已经收到特定序列号的报文段。如果发送方没有在合理的超时时间内收到确认应答,它将重传该报文段。
报文段的确认号是发送方期望从对方收到的下一个字节的序号。
套接字允许不同主机上的进程通过网络进行数据交换。
套接字主要由三个属性组成:
- 网络地址:通常是IP地址,用于标识网络上的设备。
- 端口号:用于标识设备上的特定应用或进程。端口号是一个16位的数字。
- 协议:如TCP和UDP,定义了数据传输的规则和格式。
网络字节序和主机字节序转换
字节序指的是多字节数据在内存中的存储顺序。主要有两种字节序:
- 大端字节序:高位字节序存储在内存的低地址处,地位字节序存储在高地址处。这种字节序准守自然书写习惯,也被称为网络字节序,IP协议就要求使用大端字节序。
- 小端字节序:低位字节存储在内存的低地址处,高位字节存储在高地址处。称为主机字节序。
int net_aton(const char* cp, struct in_addr *inp);
将来自IPV4点分十进制的主机地址转换为网络字节顺序。
服务端
void *read_from_client(void *arg){
// 使用recv接收客户端发送的数据 打印到控制台
char* read_buf = NULL;
int client_fd = *(int *)arg;
read_buf = malloc(sizeof(char) * 1024);
ssize_t count = 0;
while(count = recv(client_fd, read_buf, 1024, 0)){
fputs(readbuf, stdout);
}
printf("客户端请求关闭");
free(read_buf);
return NULL;
}
void *write_to_client(void *arg){
//接收控制台输入的信息 发给客户
char* write_buf = NULL;
int client_fd = *(int *)arg;
write_buf = malloc(sizeof(char) * 1024);
ssize_t count = 0;
while(fgets(write_buf, 1024, stdin) != NULL){
send(client_fd, write_buf, 1024, 0);
}
printf("关闭连接");
shutdown(client_fd, SHUT_WR);
free(write_buf);
return NULL;
}
int main()
{
int sockfd, clientfd;
pthread_t pid_read, pid_write;
struct sockaddr_in server_addr, client_addr;
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
//填写服务器地址
server_addr.sin_family = AF_INET;
//填写ip地址
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//inet_pton(AF_INET, "0.0.0.0", server_addr.sin_addr.s_addr);
server_addr.sin_port = htons(6666);
//网络编程流程
//1、socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
//2、绑定地址
bind(sockfd, &server_addr, sizeof(server_addr));
//3、进入监听状态
listen(sockfd, 1024);
//4、获取客户端的连接,接收客户端的地址
socklen_t clientaddr_len = sizeof(client_addr);
// 如果调用accept之后没有客户端链接,会挂起等待
clientfd = accept(sockfd, &client_addr, &clientaddr_len);
printf("与客户端%s %d建立连接 文件描述符为%d", inet_ntoa(client_addr.sin_addr), inet_ntohs(client_addr.sin_port), clientfd);
//创建子线程用于收消息
pthread_create(&pid_read, NULL, read_from_client, (void *)clientfd);
//创建子线程用于发消息
pthread_create(&pid_write, NULL, write_to_client, (void *)clientfd);
//阻塞主线程
pthread_join(pid_read, NULL);
pthread_join(pid_write, NULL);
printf("释放资源\n");
close(clientfd);
close(sockfd);
return 0;
}
客户端
void *read_from_server(void *arg){
// 使用recv接收客户端发送的数据 打印到控制台
char* read_buf = NULL;
int client_fd = *(int *)arg;
read_buf = malloc(sizeof(char) * 1024);
ssize_t count = 0;
while(count = recv(client_fd, read_buf, 1024, 0)){
fputs(readbuf, stdout);
}
printf("服务端请求关闭");
free(read_buf);
return NULL;
}
void *write_to_server(void *arg){
//接收控制台输入的信息 发给客户
char* write_buf = NULL;
int client_fd = *(int *)arg;
write_buf = malloc(sizeof(char) * 1024);
ssize_t count = 0;
while(fgets(write_buf, 1024, stdin) != NULL){
send(client_fd, write_buf, 1024, 0);
}
printf("关闭连接");
shutdown(client_fd, SHUT_WR);
free(write_buf);
return NULL;
}
int main()
{
int sockfd;
pthread_t pid_read, pid_write;
struct sockaddr_in server_addr, client_addr;
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
//填写客户端地址
client_addr.sin_family = AF_INET;
//填写ip地址
inet_pton(AF_INET, "192.168.10.150", client_addr.sin_addr.s_addr);
client_addr.sin_port = htons(8888);
//填写服务器地址
server_addr.sin_family = AF_INET;
//填写ip地址
inet_pton(AF_INET, "127.0.0.1", server_addr.sin_addr.s_addr);
server_addr.sin_port = htons(6666);
//网络编程流程
//1、socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
//2、绑定地址
bind(sockfd, &client_addr, sizeof(client_addr));
//3、主动连接服务端
connect(sockfd, &server_addr, sizeof(server_addr));
printf("连接上服务端%s %d\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
//创建子线程用于收消息
pthread_create(&pid_read, NULL, read_from_server, (void *)sockfd);
//创建子线程用于发消息
pthread_create(&pid_write, NULL, write_to_server, (void *)sockfd);
//阻塞主线程
pthread_join(pid_read, NULL);
pthread_join(pid_write, NULL);
printf("释放资源\n");
close(sockfd);
return 0;
}
进程的缓存模式
进程在执行I/O操作时,可以有以下三种类型的缓冲模式。
行缓冲
在这种模式下,碰到换行符或缓冲区已满时刷写到目标文件。
行缓冲通常用于标准输出(stdout),尤其是当标准输出关联到终端(控制台)时。这样做可以确保用户每输入一行命令就能看到响应的输出,而不必等到缓冲区完全填满。
全缓冲
在全缓冲模式下,数据只会在缓冲区满时刷新,这意味着数据被存储在缓冲区直到缓冲区被填满,然后整个缓冲区的内容一次性刷写。
通常向文件写数据的默认模式就是全缓冲,这样可以减少对底层系统资源的调用次数,提高数据处理效率。
无缓冲
无缓冲模式意味着数据直接刷写,不经过缓冲区。
标准错误(stderr)通常是无缓冲的,以确保错误信息能够立即输出。
在C语言中,可以通过调用setvbuf()函数来设置文件流的缓冲模式。
UDP
服务端
char* buf = malloc(sizeof(char)*1024)
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//服务端绑定地址
socklen_t server_len = sizeof(server_addr);
socklen_t client_len = sizeof(client_addr);
bind(sockfd, (struct sockaddr *)&server_addr, &server_len );
//收发数据
do{
memset(buf, 0, 1024);
recvfrom(sockfd, buf, 1024, 0, (struct client_addr*), &client_len );
if(strncmp(buf, "EOF", 3) != 0){
printf("%s",buf);
strcpy(buf, "OK\N");
sendto(sockfd, buf, 4, 0, )
}else{
printf("准备关闭");
}
}while(strncmp(buf, "EOF", 3));