LINUX下关于服务器模型的总结
服务器分为1:循环服务器
2:并发服务器
循环服务器:在同一时刻只能响应一个客户端的请求。
并发服务器:在同一时刻可以响应多个客户端的请求。
在循环服务器中,分为TCP服务器和 UDP服务器,循环服务器一般很少使用。
在并发服务器中,一般有多进程并发服务器,多线程服务器,IO多路复用并发服务器。
(1)多进程的并发服务器
只要有客户端连接服务器,服务器就创建子进程与客户端通信创建子进程后,父进程—-继续等待其他客户端的连接,子进程—-与客户端通信。这样就实现了多个客户端连接服务器的要求。特点:多进程服务器,比较浪费资源,适合于客户端数量较少,但是长连接的情况
(2)多线程的并发服务器
只要有客户端连接服务器,服务器就创建子线程与客户端通信
由于在创建子线程时,以及xian毁子线程时,比较浪费时间,一般可以使用线程池多线程存在的问题:存在资源竞争以及同步的问题
特点:适用于客户端较少,但是长时间连接的情况
(3)IO多路复用的并发服务器
适合于客户端数量较多,但是短连接的情况
代码具体实现多进程的并发服务器,process_server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
void signfun(int sigNo);
int main()
{
//定义Internet协议结构,服务器的端口号和IP地址
struct sockaddr_in myaddr;
memset(&myaddr,0,sizeof(myaddr));
myaddr.sin_family = PF_INET;
myaddr.sin_port = htons(1314);
myaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//1.创建套接字
int serverId = socket(PF_INET,SOCK_STREAM,0);
if(serverId<0)
{
perror("serverFd\n");
return -1;
}
printf("socket ok\n");
//使用setsockopt防止使用地址错误
int on=1;
int set=setsockopt(serverId,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
if(set<0)
{
printf("setsockopt\n");
return -1;
}
//2.绑定地址信息
int ret = bind(serverId,(struct sockaddr *)&myaddr,sizeof(myaddr));
if(ret<0)
{
perror("bind\n");
close(serverId);
return -1;
}
printf("bind ok\n");
//3.创建一个监听队列
if(listen(serverId,10)<0)
{
perror("listen\n");
close(serverId);
return -1;
}
printf("listening....\n");
//4.接受链接请求
while(1)
{
printf("服务器正常工作中。。。\n");
int conId=accept(serverId,NULL,NULL);//接收链接
if(conId<0)
{
perror("accept\n");
close(serverId);
return -1;
}
printf("accept ok\n");
pid_t pid;
if((pid=fork())<0)//创建子进程处理链接请求,父进程继续监听
{
perror("fock\n");
close(serverId);
close (conId);
return -1;
}
else if(pid==0) //子进程里处理聊天
{
close(serverId);
while(1)
{
char buf[1024];
memset(buf,0,1024);
int ret = recv(conId,buf,sizeof(buf),0);
if(ret<0)
{
perror("recv\n");
break;
}
if(ret ==0)
{
printf("对方下线\n");
break;
}
printf("from xldclient:%s\n",buf);
printf("xldserver:");
gets(buf);
if(strcmp(buf,"quit")==0)
{
break;
}
ret = send(conId,buf,sizeof(buf),0);
if(ret <0)
{
perror("send\n");
break;
}
}
close(conId);//关闭链接套接字
exit(0); //进程退出
}
else
{
close(conId);
signal(SIGCHLD,signfun);//信号处理函数,
//子进程结束时会给父进程发送SIGCHLD
}
}
//6.关闭套接字
close(serverId);
return 0;
}
void signfun(int sigNo)
{
if(sigNo == SIGCHLD)
{
wait(NULL);
}
printf("子进程已结束\n");
}
多线程的并发服务器具体代码,pthread_server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
void *profunC(void *arg);//线程处理函数的声明
int main()
{
//定义Internet协议结构,服务器的端口号和IP地址
struct sockaddr_in myaddr;
memset(&myaddr,0,sizeof(myaddr));
myaddr.sin_family = PF_INET;
myaddr.sin_port = htons(1314);
myaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//1.创建套接字
int serverId = socket(PF_INET,SOCK_STREAM,0);
if(serverId<0)
{
perror("serverFd\n");
return -1;
}
printf("socket ok\n");
//2.绑定地址信息
int ret = bind(serverId,(struct sockaddr *)&myaddr,sizeof(myaddr));
if(ret<0)
{
perror("bind\n");
close(serverId);
return -1;
}
printf("bind ok\n");
//3.创建一个监听队列
if(listen(serverId,10)<0)
{
perror("listen\n");
close(serverId);
return -1;
}
printf("listening....\n");
//4.接受链接请求
while(1)
{
printf("服务器正常工作中。。。\n");
int conId=accept(serverId,NULL,NULL);
if(conId<0)
{
perror("accept\n");
close(serverId);
return -1;
}
printf("accept ok\n");
pthread_t th1;//主线程继续等待链接
while(pthread_create(&th1,NULL,profunC,&conId)<0);
}
//6.关闭套接字
close(serverId);
return 0;
}
void *profunC(void *arg)
{
int conId=*(int *)arg;
//子线程完成通信
while(1)
{
char buf[1024];
memset(buf,0,1024);
int ret = recv(conId,buf,sizeof(buf),0);
if(ret<0)
{
perror("recv\n");
break;
}
if(ret ==0)
{
printf("对方下线\n");
break;
}
printf("from xldclient:%s\n",buf);
printf("xldserver:");
gets(buf);
if(strcmp(buf,"quit")==0)
{
break;
}
ret = send(conId,buf,sizeof(buf),0);
if(ret <0)
{
perror("send\n");
break;
}
}
close(conId);
pthread_exit(NULL);
}
io多路复用的并发服务器的具体代码实现io_server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <netinet/in.h>
int main()
{
//定义Internet协议结构,服务器的端口号和IP地址
struct sockaddr_in myaddr;
memset(&myaddr,0,sizeof(myaddr));
myaddr.sin_family = PF_INET;
myaddr.sin_port = htons(1314);
myaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//1.创建套接字
int serverId = socket(PF_INET,SOCK_STREAM,0);
if(serverId<0)
{
perror("serverFd\n");
return -1;
}
printf("socket ok\n");
//设置套接字选项避免地址使用错误
int on=1;
int st=setsockopt(serverId,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
if(st<0)
{
perror("setsockopt error\n");
close(serverId);
return -1;
}
//2.绑定地址信息
int ret = bind(serverId,(struct sockaddr *)&myaddr,sizeof(myaddr));
if(ret<0)
{
perror("bind\n");
close(serverId);
return -1;
}
printf("bind ok\n");
//3.创建一个监听队列
if(listen(serverId,10)<0)
{
perror("listen\n");
close(serverId);
return -1;
}
printf("listening....\n");
//1创建集合
fd_set sebuf;
//2清空集合
FD_ZERO(&sebuf);
//3将对应的文件描述符加入集合
FD_SET(0,&sebuf);
FD_SET(1,&sebuf);
FD_SET(serverId,&sebuf);
int maxfd=serverId;
//使用seclect监视
fd_set tmp ;
int i;
while(1)
{
printf("before select\n");
tmp = sebuf;
//select监视文件的变化
int retu1=select(maxfd+1,&tmp,NULL,NULL,NULL);
printf("retu1=%d\n",retu1);
if(retu1<0)
{
perror("seclect\n");
close(serverId);
return -1;
}
for(i=0;i<=maxfd;i++)
{
if(FD_ISSET(i,&tmp))
{
if(i==0)
{
printf("stdin\n");
char buf[1024]={0};
gets(buf);
printf("from stdin %s\n",buf);
}
else if(i==1)
{
printf("stdout\n");
}
else if(i==serverId)
{
//4.接受链接请求
int conId=accept(serverId,NULL,NULL);
if(conId<0)
{
perror("accept\n");
close(serverId);
return -1;
}
printf("accept ok\n");
FD_SET(conId,&sebuf);
if(conId > maxfd)
{
maxfd = conId;
}
}
else
//5.收发消息
{
char buf[1024];
memset(buf,0,1024);
ret = recv(i,buf,sizeof(buf),0);
if(ret<0)
{
perror("recv\n");
close(i);
return -1;
}
if(ret==0)
{
printf("对方已下线\n");
close(i);
FD_CLR(i,&sebuf);
continue;
}
printf("from xldclient:%s\n",buf);
memset(buf,0,1024);
printf("xldserver:");
gets(buf);
ret = send(i,buf,sizeof(buf),0);
if(ret <0)
{
perror("send\n");
close(i);
return -1;
}
}
}
}
}
//6.关闭套接字
close(serverId);
return 0;
}
代码写得略有粗糙,继续努力吧。