多进程并发服务端
程序流程
服务端实现流程:
- 创建套接字,绑定地址结构,设置监听上限等。
- 循环调用accept函数接收客户端请求并创建子进程。
- 子进程中关闭套接字(不进行其他操作),父进程中完成服务端与客户端数据通信。
- 在父进程中注册信号捕捉函数,等待子进程结束并进行回收,避免僵尸进程的出现。
- 调用read函数循环读取客户端数据,完成大小写转换,调用write函数写回数据至客户端。
服务端程序
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <signal.h>
#include "wrap.h"
using namespace std;
//定义服务端端口号
#define SERVER_PORT 9527
void catch_child(int signum)
{
while((waitpid(0,NULL,WNOHANG))>0);
return ;
}
int main (int argc ,char*argv[])
{
int lfd=0;//用于监听的套接字
int cfd=0;//用于通信的套接字
pid_t pid;
int ret ;
char buf[BUFSIZ];
//创建套接字
lfd=Socket(AF_INET,SOCK_STREAM,0);
//创建地址结构
struct sockaddr_in server_addr,client_addr;
socklen_t client_addr_len;
//初始化
//memset(&server_addr,0,sizeof(server_addr));//将地址结构清零
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(SERVER_PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//绑定地址结构
Bind(lfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
//设置监听
Listen(lfd,128);
client_addr_len=sizeof(client_addr);
// 循环接收客户端请求
while(1)
{
cfd=Accept(lfd,(struct sockaddr*)&client_addr,&client_addr_len);
//创建子进程
pid=fork();
if (pid<0)//检查创建进程是否成功
{
sys_err("fork error");
}
else if(pid==0)//子进程
{
close(lfd);//关闭监听套接字
break;
}
//父进程
else{
struct sigaction act;
act.sa_handler=catch_child;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
//注册新号捕捉函数
ret=sigaction(SIGCHLD,&act,NULL);
if(ret!=0)
{
sys_err("sigaction error");
}
//关闭套接字
close(cfd);
continue;
}
}
if(pid==0)
{
//循环读取客户端数据信息
for(;;)
{
ret=Read(cfd,buf,sizeof(buf));
//输出到屏幕
write(STDOUT_FILENO,buf,ret);
if(ret==0)
{
//读完数据后关闭套接字
close(cfd);
exit(1);
}
//完成大小写转换
for (int i=0;i<ret;i++)
{
buf[i]=toupper(buf[i]);
}
//数据写回到buf
write(cfd,buf,ret);
}
}
return 0;
}
客户端程序
这里与之前的客户端程序一样
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ctype.h>
#include <arpa/inet.h>
#include "wrap.h"
using namespace std;
//定义一个端口号(服务端)
#define SERVER_PORT 9527
//#define BUFSIZ _IO_BUFSIZ
// void sys_err(const char *str)
// {
// perror(str);
// exit(1);
// }
int main (int argc,char *argv[])
{
int ret=0;
int c_fd=0;//创建用于通信的文件描述符
char buf[BUFSIZ];
struct sockaddr_in server_addr;//创建服务端地址结构体
//初始化成员
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(SERVER_PORT);
//server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//任意有效ip
//将点分十进制的ip地址转化为用于网络传输的二进制数值格式
inet_pton(AF_INET,"127.0.0.1",&(server_addr.sin_addr));
c_fd=Socket(AF_INET,SOCK_STREAM,0);//创建套接字
if(c_fd==-1)
{
sys_err("socket error");
}
//客户端与服务端建立连接
ret=connect(c_fd,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1)
{
sys_err("connect error");
}
//循环写入数据
while(1)
{
fgets(buf,sizeof(buf),stdin);
write(c_fd,buf,strlen(buf));
ret=read(c_fd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,ret);
}
//关闭套接字
close(c_fd);
return 0;
}
运行结果
服务端:
客户端:
客户端中止进程(Ctrl+C):
如果子进程不进行回收,会出现僵尸进程
多线程并发服务端
程序流程
服务端实现流程:
- 创建套接字,绑定地址结构,设置监听上限等。
- 循环调用accept函数接收客户端请求并创建子线程。
- 子线程回调函数用于完成客户端与服务端的数据通信,调用read函数循环读取客户端数据,完成大小写转换,调用write函数写回数据至客户端。
- 在主线程中完成线程分离(调用pthread_detach函数),自动回收子线程。
服务端程序
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <signal.h>
#include <pthread.h>
#include "wrap.h"
using namespace std;
//定义服务端端口号
#define SERVER_PORT 9527
struct s_info { //定义一个结构体, 将地址结构跟cfd捆绑
struct sockaddr_in client_addr;
int cfd;
};
//子线程回调函数
void *do_work(void *arg)
{
int n,i;
struct s_info *ts = (struct s_info*)arg;
char buf[BUFSIZ];
char str[INET_ADDRSTRLEN]; //#define INET_ADDRSTRLEN 16 可用"[+d"查看
while (1) {
n = Read(ts->cfd, buf, BUFSIZ); //读客户端
if (n == 0) {
printf("the client %d closed...\n", ts->cfd);
break; //跳出循环,关闭cfd
}
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &(*ts).client_addr.sin_addr, str, sizeof(str)),
ntohs((*ts).client_addr.sin_port)); //打印客户端信息(IP/PORT)
for (i = 0; i < n; i++)
buf[i] = toupper(buf[i]); //小写-->大写
Write(STDOUT_FILENO, buf, n); //写出至屏幕
Write(ts->cfd, buf, n); //回写给客户端
}
Close(ts->cfd);
return (void *)0; //pthread_exit() 结束子线程
}
int main (int argc ,char*argv[])
{
int lfd=0;//用于监听的套接字
int cfd=0;//用于通信的套接字
pid_t pid;
int ret,i=0 ;
char buf[BUFSIZ];
//创建套接字
lfd=Socket(AF_INET,SOCK_STREAM,0);
//创建地址结构
struct sockaddr_in server_addr,client_addr;
socklen_t client_addr_len;
pthread_t tid;
//根据最大线程数创建结构体数组
struct s_info ts[256];
//初始化
//memset(&server_addr,0,sizeof(server_addr));//将地址结构清零
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(SERVER_PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//绑定地址结构
Bind(lfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
//设置监听
Listen(lfd,128);
client_addr_len=sizeof(client_addr);
// 循环接收客户端请求
while(1)
{
cfd=Accept(lfd,(struct sockaddr*)&client_addr,&client_addr_len);
ts[i].client_addr = client_addr;
ts[i].cfd = cfd;
/* 达到线程最大数时,pthread_create出错处理, 增加服务器稳定性 */
pthread_create(&tid, NULL, do_work, (void*)&ts[i]);
//子线程分离,防止僵线程产生.
pthread_detach(tid);
i++;
}
return 0;
}
客户端程序
同多进程客户端
运行结果
服务端:
客户端:
附录(错误处理函数封装)
//wrap.cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include "wrap.h"
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
int n;
again:
if ((n = accept(fd, sa, salenptr)) < 0) {
if ((errno == ECONNABORTED) || (errno == EINTR))
goto again;
else
sys_err("accept error");
}
return n;
}
int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
int n;
if ((n = bind(fd, sa, salen)) < 0)
sys_err("bind error");
return n;
}
// int Connect(int fd, const struct sockaddr *sa, socklen_t salen)
// {
// int n;
// n = connect(fd, sa, salen);
// if (n < 0) {
// perr_exit("connect error");
// }
// return n;
// }
int Listen(int fd, int backlog)
{
int n;
if ((n = listen(fd, backlog)) < 0)
sys_err("listen error");
return n;
}
int Socket(int domain, int type, int protocol)
{
int n;
if ((n = socket(domain, type, protocol)) < 0)
sys_err("socket error");
return n;
}
ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
again:
if ( (n = read(fd, ptr, nbytes)) == -1) {
if (errno == EINTR)
goto again;
else
return -1;
}
return n;
}
// ssize_t Write(int fd, const void *ptr, size_t nbytes)
// {
// ssize_t n;
// again:
// if ((n = write(fd, ptr, nbytes)) == -1) {
// if (errno == EINTR)
// goto again;
// else
// return -1;
// }
// return n;
// }
// int Close(int fd)
// {
// int n;
// if ((n = close(fd)) == -1)
// perr_exit("close error");
// return n;
// }
// /*参三: 应该读取的字节数*/ //socket 4096 readn(cfd, buf, 4096) nleft = 4096-1500
// ssize_t Readn(int fd, void *vptr, size_t n)
// {
// size_t nleft; //usigned int 剩余未读取的字节数
// ssize_t nread; //int 实际读到的字节数
// char *ptr;
// ptr = vptr;
// nleft = n; //n 未读取字节数
// while (nleft > 0) {
// if ((nread = read(fd, ptr, nleft)) < 0) {
// if (errno == EINTR)
// nread = 0;
// else
// return -1;
// } else if (nread == 0)
// break;
// nleft -= nread; //nleft = nleft - nread
// ptr += nread;
// }
// return n - nleft;
// }
// ssize_t Writen(int fd, const void *vptr, size_t n)
// {
// size_t nleft;
// ssize_t nwritten;
// const char *ptr;
// ptr = vptr;
// nleft = n;
// while (nleft > 0) {
// if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
// if (nwritten < 0 && errno == EINTR)
// nwritten = 0;
// else
// return -1;
// }
// nleft -= nwritten;
// ptr += nwritten;
// }
// return n;
// }
// static ssize_t my_read(int fd, char *ptr)
// {
// static int read_cnt;
// static char *read_ptr;
// static char read_buf[100];
// if (read_cnt <= 0) {
// again:
// if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { //"hello\n"
// if (errno == EINTR)
// goto again;
// return -1;
// } else if (read_cnt == 0)
// return 0;
// read_ptr = read_buf;
// }
// read_cnt--;
// *ptr = *read_ptr++;
// return 1;
// }
// /*readline --- fgets*/
// //传出参数 vptr
// ssize_t Readline(int fd, void *vptr, size_t maxlen)
// {
// ssize_t n, rc;
// char c, *ptr;
// ptr = vptr;
// for (n = 1; n < maxlen; n++) {
// if ((rc = my_read(fd, &c)) == 1) { //ptr[] = hello\n
// *ptr++ = c;
// if (c == '\n')
// break;
// } else if (rc == 0) {
// *ptr = 0;
// return n-1;
// } else
// return -1;
// }
// *ptr = 0;
// return n;
// }
//wrap.h
#ifndef __WRAP_H_
#define __WRAP_H_
void sys_err(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
// int Connect(int fd, const struct sockaddr *sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int domain, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
// ssize_t Write(int fd, const void *ptr, size_t nbytes);
// int Close(int fd);
// ssize_t Readn(int fd, void *vptr, size_t n);
// ssize_t Writen(int fd, const void *vptr, size_t n);
// ssize_t my_read(int fd, char *ptr);
// ssize_t Readline(int fd, void *vptr, size_t maxlen);
#endif