多进程回声服务器
服务器搭建思路
- 处理僵尸进程
- 生成服务端
- 循环接收客服端连接并开辟新进程处理新连接
以下是服务器实例:
c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/tcp.h>
#include<wait.h>
#include<signal.h>
#include<bits/sigaction.h>
//子进程退出信号回调函数
void ChildExitHandle()
{
//使用waitpid()函数
int status;
pid_t pid = waitpid(-1,&status,WNOHANG);//监控任意子进程、状态信息、非阻塞
if (WIFEXITED(status))
{
printf("Child process exited.\n");
printf("The child process's id is %d\n",WEXITSTATUS(status));
}
}
//错误处理
void error_handing(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
int main(int argc, char const *argv[])
{
//步骤1:处理僵尸进程 -> 使用sigaction()函数
struct sigaction sig_cfg;
sig_cfg.sa_handler = ChildExitHandle;
sigaction(SIGCLD,&sig_cfg,NULL);
//步骤2.1:生成服务端socket
int server_sock;
server_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
if (server_sock == -1)
{
error_handing("socket() error");
}
//步骤2.2:绑定监听端口
struct sockaddr_in server_addr;
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(atoi("3333"));
if (bind(server_sock,(struct sockaddr *)&server_addr,sizeof(server_addr)) == -1)
{
error_handing("bind() error");
}
//步骤2.3:开启端口监听
if (listen(server_sock,5) == -1)
{
error_handing("listen() error");
}
//步骤3:循环接收客服端连接并开辟新进程处理新连接
while (1)
{
//步骤3.1:接受连接
int accept_sock;
struct sockaddr accept_addr;
socklen_t len = sizeof(accept_addr);
accept_sock = accept(server_sock,&accept_addr,&len);//第二个参数存储接入的客户端的地址信息
if (accept_sock == -1)
{
//error_handing("accept() error");
continue;
}
else
{
printf("New client connected...\n");
}
//步骤3.2:开辟新进程与客户端交互
pid_t pid = fork();
if (pid == 0)//子进程
{
//子进程同时也复制了父进程的server_sock,要关闭
close(server_sock);
//步骤3.3:开始交互
const int message_size = 10;
char message[message_size];
int read_len = 0;
while((read_len=read(accept_sock, message, message_size))!=0)
write(accept_sock, message, read_len);
//步骤3.3:交互完毕,关闭连接
close(accept_sock);
//步骤3.4:子进程退出
exit(2);
}
else//父进程
{
//父进程同时也包含了子进程的accept_sock,要关闭
close(accept_sock);
}
}
close(server_sock);
return 0;
}
客户端实例:采用分割I/O的方法,将输入和出输出分割,简化逻辑
c
#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);
}
//步骤1:创建套接字
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1)
error_handling("socket() error");
//步骤2:连接端口
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");
}
//步骤3:进行通讯
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);
}
}
使用客户端 :./client "服务端ip" "端口"