基于进程的并发服务器
我们将扩展之前的回声服务器,使其可以同时向多个客户端体提供服务,实现模型如下图所示
即每当有客户端向服务器请求服务时,服务器端都创建一个子进程为其提供服务,比如有5个客户端请求服务,则创建个5子进程。
通过fork()复制的文件描述符
下图是父进程调用fork()函数后的示意图
分割TCP的I/O程序
即客户端的父进程负责接收数据,额外创建的子进程负责发送数据,分割后互不干扰,这样无论客户端是否完成从服务器接收完数据,都可以进行发送,可以提高频繁交换数据的程序性能
服务器代码
扩展之前的回声服务器,使其可以同时向多个客户端体提供服务。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // POSIX标准定义的通用函数,如close()
#include <arpa/inet.h> // 提供inet相关的函数,如inet_addr()
#include <sys/socket.h> // 提供socket相关的函数和数据结构
#include <signal.h>
#include <sys/wait.h>
#define BUFF_SIZE 30 //缓冲区大小
void error_handling(char* message);
void rea_childproc(int sig);
int main(int argc, char *argv[])
{
int serv_sock; // 服务器套接字
int clnt_sock; // 客户端套接字
struct sockaddr_in serv_addr; // 服务器地址结构
struct sockaddr_in clnt_addr; // 客户端地址结构
pid_t pid;
socklen_t clnt_addr_size; // 客户端地址结构的大小
struct sigaction act;
char opinfor[BUFF_SIZE];
int str_len,state;
if(argc!=2)
{
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
act.sa_handler=rea_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
sigaction(SIGCHLD, &act, 0);
// 创建一个服务器套接字
serv_sock=socket(PF_INET, SOCK_STREAM, 0);//使用SOCK_STREAM创建TCP套接字
if(serv_sock==-1)
error_handling("socket() error");
// 初始化服务器地址结构
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET; // 地址族设置为IPv4
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); // 服务器地址设置为任意
serv_addr.sin_port=htons(atoi(argv[1])); // 设置监听端口为命令行参数指定的端口
// 绑定套接字,调用bind()函数分配ip地址和端口号
if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1) // 监听套接字,最多监听5个连接
error_handling("listen() error");
while(1){
clnt_addr_size=sizeof(clnt_addr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
if(clnt_sock==-1)
error_handling("accept() error");
else
puts("New Client Connected...");
pid=fork();
if(pid==0){
close(serv_sock);
while((str_len=read(clnt_sock, opinfor, BUFF_SIZE))!=0)
write(clnt_sock, opinfor, str_len);
close(clnt_sock);
printf("client disconnected...");
return 0;
}
else
close(clnt_sock);
continue;
}
// 关闭客户端和服务器套接字
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
void rea_childproc(int sig)
{
pid_t pid;
int status;
pid=waitpid(-1, &status, WNOHANG);
if(WIFEXITED(status))
printf("Removed proc id: %d \n", pid);
}
客户端代码
分割了TCP的I/O程序的客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
void read_routline(int sock, char *buf);
void write_routline(int sock, char *buf);
int main(int argc, char *argv[])
{
int sock; // 客户端套接字
pid_t pid;
struct sockaddr_in serv_addr; // 服务器地址结构
char message[BUF_SIZE]; // 用于存储从服务器接收的消息
int str_len,i; // 读取的字节数
if (argc != 3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
// 创建一个客户端套接字
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1)
error_handling("socket() error");
// 初始化服务器地址结构
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET; // 地址族设置为IPv4
serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址
serv_addr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号
// 发送连接请求
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
error_handling("connect() error");
else{
printf("Connected ok\n");
}
pid=fork();
if(pid==0){
write_routline(sock,message);
}else{
read_routline(sock,message);
close(sock);
return 0;
}
}
void error_handling(char* message){
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
void write_routline(int sock, char *buf){
while(1){
fgets(buf, BUF_SIZE, stdin);
if(!strcmp(buf,"q\n")||!strcmp(buf,"Q\n")){
shutdown(sock, SHUT_WR);
return;
}
write(sock, buf, strlen(buf));
}
}
void read_routline(int sock, char *buf){
while(1){
int str_len=read(sock, buf, BUF_SIZE);
if(str_len==0){
return;
}
buf[str_len]=0;
printf("Message from server:%s", buf);
}
}
运行结果
可以同时为3个客户端提供回声服务。
标签:serv,服务器端,int,IP,sock,TCP,error,客户端,addr From: https://blog.csdn.net/m0_53115174/article/details/139242857