服务器处理多个连接请求,上篇使用了fork()创建子进程,进程的创建和销毁,会消耗服务器大量的资源。利用线程去处理请求,减轻服务器断电压力。
服务器端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //
#include
#include
#include //gethostbyaddr
#define MAXLINE 1024
/*
基于多线程的并发服务器
*/
void toggle(int conn_sock);
void handlep(int* conn_sock_p);
int startup(int *port);
void error_die(const char *sc);
int main(int argc,char **argv){
int listen_sock,port,clientlen;
int *conn_sock_p;
clientlen = sizeof(struct sockaddr_in); //must
struct sockaddr_in clientaddr;
pthread_t tid;
if(argc!=2){
fprintf(stderr,"input:%s \n" ,argv[0]);
exit(1);
}
port=atoi(argv[1]); //string to int
listen_sock = startup(&port);
while(1){
conn_sock_p = malloc(sizeof(int));
*conn_sock_p = accept(listen_sock,(struct sockaddr*)&clientaddr,&clientlen);
pthread_create(&tid,NULL,(void *)handlep,conn_sock_p);
}
exit(0);
}
/*线程处理*/
void handlep(int* conn_sock_p){
int conn_sock = *conn_sock_p;
pthread_detach(pthread_self());
free(conn_sock_p);
printf("Server connection success(pthread %lu)\n",pthread_self()); //要使用%lu方式
toggle(conn_sock);
printf("Server connection over(pthread %lu)\n",pthread_self());
close(conn_sock);
return;
}
void toggle(int conn_sock){
int n;
int i;
char buf[MAXLINE];
while(n=recv(conn_sock,buf,MAXLINE,0)){
printf("togglest server received %d bytes\n",n);
for(i=0;i<n;i++){
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else
buf[i]=toupper(buf[i]);
}
send(conn_sock,buf,n,0);
}
}
int startup(int *port)
{
int httpd = 0;
struct sockaddr_in name;
httpd = socket(PF_INET, SOCK_STREAM, 0);
if (httpd == -1)
error_die("socket");
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(*port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定socket
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
error_die("bind");
//如果端口没有设置,提供个随机端口
if (*port == 0) /* if dynamically allocating a port */
{
socklen_t namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);
}
//监听
if (listen(httpd, 5) < 0)
error_die("listen");
return(httpd);
}
void error_die(const char *sc)
{
perror(sc);
exit(1);
}
客户端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //gethostbyaddr
#define MAXLINE 1024
/*
可同事处理多个客户端连接请求的并发服务器
*/
void toggle(int conn_sock);
int startup(int *port);
void error_die(const char *sc);
/*
处理僵尸进程
*/
void sigchld_handler(int sig){
while(waitpid(-1,0,WNOHANG)>0);
return;
}
int main(int argc,char **argv){
int listen_sock,conn_sock,port,clientlen,cpid;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
if(argc!=2){
fprintf(stderr,"input:%s \n" ,argv[0]);
exit(1);
}
port=atoi(argv[1]); //string to int
signal(SIGCHLD,sigchld_handler);
listen_sock = startup(&port);
while(1){
clientlen=sizeof(clientaddr);
conn_sock=accept(listen_sock,(struct sockaddr*)&clientaddr,&clientlen);
if(fork() == 0){
//子进程运行,父进程继续循环accept()
hp=gethostbyaddr((const char*)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr),AF_INET);
haddrp=inet_ntoa(clientaddr.sin_addr);
cpid = getpid();
printf("Server(chile process %d) connected to %s(%s)\n",cpid,hp->h_name,haddrp);
close(listen_sock);
toggle(conn_sock);
close(conn_sock);
printf("Server(chile process %d) connection closed\n",cpid);
exit(0); //子进程退出
}
close(conn_sock); //父进程关闭这个连接描述符,因为这个描述符对父进程是没有用的,子进程复制了一个
}
exit(0);
}
void toggle(int conn_sock){
int n;
int i;
char buf[MAXLINE];
while(n=recv(conn_sock,buf,MAXLINE,0)){
printf("toggle server received %d bytes\n",n);
for(i=0;i<n;i++){
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else
buf[i]=toupper(buf[i]);
}
send(conn_sock,buf,n,0);
}
}
int startup(int *port)
{
int httpd = 0;
struct sockaddr_in name;
httpd = socket(PF_INET, SOCK_STREAM, 0);
if (httpd == -1)
error_die("socket");
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(*port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定socket
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
error_die("bind");
//如果端口没有设置,提供个随机端口
if (*port == 0)
{
socklen_t namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);
}
//监听
if (listen(httpd, 5) < 0)
error_die("listen");
return(httpd);
}
void error_die(const char *sc)
{
perror(sc);
exit(1);
}
执行结果: