编程思想:
so,我们一分钱没花改造了一个简易TCP服务器,具体的:
1 当accept正常返回后,创建一个子进程用于处理数据
2 在子进程中 关闭socket返回的fd,在父进程中关闭accept返回的fd,防止资源泄露及不可预知的风险
3 父进程中忽略子进程结束信号,等于自动回收,防止变僵尸
当有了一个主体结构可以运行,如何观察这种并发的效果???
每连接一个客户端,计数器加1 ,处理完成计数器减1,但使用什么可以完成这种效果?
全局变量?每个进程都有自己的全局变量 修改自己的别人看不见
信号量隆重登场! posix风格的信号量不同于system V,开局直接设0,还可以进程间共享,每当一个连接加入就加1,每当一个连接处理完进程退出就减1,用完就关上,重开代码先unlink
注意事项:
1 使用if判断函数的返回值
2 子进程结束 必须使用exit函数,否则可以观察到不符合逻辑的超自然现象
3 赠送手动运行客户端 可以编译后在多个终端运行,也可在IDE中运行多次观察效果
服务端:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <semaphore.h>#define SERVER_IP "192.168.142.132"
#define SERVER_PORT 50007int main()
{int server_sockfd, client_sockfd;struct sockaddr_in server_sockaddr, client_sockaddr;memset(&server_sockaddr, 0, sizeof(server_sockaddr));memset(&client_sockaddr, 0, sizeof(client_sockaddr));socklen_t client_sockaddr_len = sizeof(client_sockaddr);socklen_t server_sockaddr_len = sizeof(server_sockaddr);ssize_t send_bytes, recv_bytes;char send_buf[1024] = "How can I help you today ?";char recv_buf[1024] = {0};sem_unlink("/sem1");server_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (server_sockfd == -1){perror("socket");}int optval = 1;if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1){perror("setsockopt");}server_sockaddr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &server_sockaddr.sin_addr.s_addr);server_sockaddr.sin_family = AF_INET;if ((bind(server_sockfd, (struct sockaddr *)&server_sockaddr, server_sockaddr_len)) == -1){perror("bind");}if ((listen(server_sockfd, 16)) == -1){perror("listen");}sem_t *sem = sem_open("/sem1", O_CREAT, 0777, 0);if (sem == SEM_FAILED){perror("sem_open");}printf("listening on : %d\n", SERVER_PORT);while (1){client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_sockaddr, &client_sockaddr_len);if (client_sockfd == -1){perror("accept");}pid_t pid = fork();if (pid == 0){sem_post(sem);int sval;sem_getvalue(sem, &sval);printf("V [+] connected : %d\n", sval);if (close(server_sockfd) == -1){perror("close");}recv_bytes = recv(client_sockfd, recv_buf, sizeof(recv_buf), 0);if (recv_bytes == -1){perror("recv");}if (recv_bytes == 0){printf("close by peer\n");}if (recv_bytes > 0){printf("Message : %s\n", recv_buf);}sleep(9);send_bytes = send(client_sockfd, send_buf, strlen(send_buf), 0);if (send_bytes == -1){perror("send");}if (close(client_sockfd) == -1){perror("close");}sem_wait(sem);printf("P [-] job done \n");sem_close(sem);exit(EXIT_SUCCESS);}else if (pid > 0){if (signal(SIGCHLD, SIG_IGN) == SIG_ERR){perror("signal");}if (close(client_sockfd) == -1){perror("close");}}else{perror("fork");}}return 0;
}
客户端:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>#define SERVER_IP "192.168.142.132"
#define SERVER_PORT 50007int main()
{int client_sockfd;struct sockaddr_in server_sockaddr, client_sockaddr;memset(&server_sockaddr, 0, sizeof(server_sockaddr));memset(&client_sockaddr, 0, sizeof(client_sockaddr));socklen_t client_sockaddr_len = sizeof(client_sockaddr);ssize_t send_bytes, recv_bytes;char send_buf[1024] = {"hello server !!!"};char recv_buf[1024] = {0};client_sockfd = socket(AF_INET, SOCK_STREAM, 0);inet_pton(AF_INET, SERVER_IP, &server_sockaddr.sin_addr.s_addr);server_sockaddr.sin_port = htons(SERVER_PORT);server_sockaddr.sin_family = AF_INET;connect(client_sockfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr));send_bytes = send(client_sockfd, send_buf, strlen(send_buf), 0);recv_bytes = recv(client_sockfd, recv_buf, sizeof(recv_buf), 0);printf("%s\n", recv_buf);close(client_sockfd);return 0;
}