/*
*两个线程一个负责监听客户端,一个负责读客户端请求。 服务器模型,
*主控线程负责accept监听链接的客户端,
*把客户端fd放入任务队列中(),分离子线程则从任务队列取出所有的
*客户端描述加入select并处理客户端读请求。
*13:06:22
*/
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/select.h>
#include<sys/time.h>
#include<pthread.h>
#include<memory.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<signal.h>
#include<semaphore.h>
#include<malloc.h>
#include<fcntl.h>
typedef unsigned int uint32;
pthread_mutex_t lock;//同步主线程跟select线程操作任务队列
int* queue=NULL;
uint32 qcount=0;//任务队列有多少个任务
uint32 qcapty=4;//任务队列容量
/*
*
*MYLOCK 用于同步accept select两个线程,如果select线程先主线程准备好 *socket启动,那么select 就会报段错误。所以必须在accept准备好后select *在启动,否则一直阻塞。
*
*/
typedef struct{
int flag;//如果select子线程先执行到select函数会报错,所以必须等到主控线程accept监听。当主线程执行到accept通知子线程否则子线程阻塞。信号灯
sem_t sem;
}MYLOCK;
struct sockaddr_in server;
void deleQueue(int*);
void printerror()
{
printf("%d:%s\n",errno,strerror(errno));
deleQueue(queue);
exit(-1);
}
void addSet(fd_set* set,char* p){
FD_ZERO(set);
int i=0;
for(;i<qcapty;i++){//必须全部检索,因为队列fd不是顺序排列,可能有空位。
int tem=*(queue+i);
if(tem!=0){
FD_SET(tem,set);
// if(p==NULL)
// printf("addset: fd=%d\n",tem);
}
}
}
void initQueue(int **p,int n){
*p=(int*)malloc(sizeof(int)*n);
memset(*p,0,sizeof(int)*n);
pthread_mutex_init(&lock,0);
}
void deleQueue(int* p){
free(p);
}
int getMax(int* p,int num){//取得队列的最大描述符
pthread_mutex_lock(&lock);
int i=0;
int tem=0;
for(;i<num;i++){
if(tem<*(p+i)){
tem=*(p+i);
}
}
pthread_mutex_unlock(&lock);
return tem;
}
void enlargeQueue(int **pqueue,uint32* oldcapty){//当任务队列不能容下任务的话,要扩容,释放掉以前的队列,新建队列并考数据。初始16每次左移1位,扩大一倍。
uint32 old=*oldcapty;
*oldcapty=(*oldcapty)<<1;
int* nice=(int*)malloc((*oldcapty)*sizeof(int));
memset(nice,0,sizeof(int)*(*oldcapty));
memcpy(nice,*pqueue,sizeof(int)*old);
free(*pqueue);
*pqueue=nice;
}
void addQueue(int value){
pthread_mutex_lock(&lock);
int i=0;
if(qcapty-qcount<2){
enlargeQueue(&queue,&qcapty);
printf("kong rong le have %d client , capty=%d\n",qcount,qcapty);
}
for(;i<qcapty;i++){
if(*(queue+i)==0){
*(queue+i)=value;
qcount++;
// printf("have fd num = %u\n",qcount);
break;
}
}
pthread_mutex_unlock(&lock);
}
int getQueue(int dex){
pthread_mutex_lock(&lock);
int tem= *(queue+(dex-1));
pthread_mutex_unlock(&lock);
return tem;
}
void removeQueue(int value){
pthread_mutex_lock(&lock);
int i=0;
for(;i<qcapty;i++){
if(*(queue+i)==value){
*(queue+i)=0;
qcount--;
}
}
pthread_mutex_unlock(&lock);
}
fd_set set;
MYLOCK mlock;
void* th_hand(void* p){
sem_wait(&mlock.sem);
while(!mlock.flag){//等主控线程执行到accept并通知子线程执行。
sem_post(&mlock.sem);
sleep(1);
sem_wait(&mlock.sem);
}
sem_post(&mlock.sem);
printf("sub before select\n");
while(1){
addSet(&set,NULL);
struct timeval timeout={1,0};
int s=select(getMax(queue,qcapty)+1,&set,NULL,NULL,&timeout);
if(s<0){
printerror();
}else if(s==0){
continue;
}else{
int i=0;
for(;i<qcapty;i++){
int readfd=*(queue+i);
if(FD_ISSET(readfd,&set)){
char buff[40]={0};
printf("go read fd %d\n",readfd);
int rn=read(readfd,buff,sizeof(buff)-1);
if(rn==0){
struct sockaddr_in client;
memset(&client,0,sizeof(client));
int len=sizeof(client);
getpeername(readfd,(struct sockaddr*)&client,&len);
// printf("%s is closed\n",inet_ntoa(client.sin_addr));
removeQueue(readfd);
// printf("move fd %d\n",readfd);
/* int i=0;
for(;i<qcapty;i++){
int tem=*(queue+i);
printf("close : fd=%d\n",tem);
}
*/
close(readfd);
}else if(rn==-1){
printf("read fd error%d %d\n",readfd,rn);
printerror();
}else if(rn>0){
int wr=write(STDOUT_FILENO,buff,rn);
}
}
}
}
}
}
int initSocket(int port){
memset(&server,0,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.s_addr=htonl(INADDR_ANY);
server.sin_port=htons(port);
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1){
printerror();
}
int res=bind(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr));
if(res==-1){
printerror();
}
if(-1==listen(sockfd,10)){
printerror();
}
sem_wait(&mlock.sem);
mlock.flag=1;//通知子线程可以从队列取任务添加到select
sem_post(&mlock.sem);
printf("main before accept\n");
while(1){
int fd;
if((fd=accept(sockfd,NULL,NULL))==-1){
printerror();
}
printf("%d non is unblock\n",O_NONBLOCK);
addQueue(fd);//当有客户端连接,加入到任务队列中。
}
}
void sig_hand(int signo){
if(signo==SIGINT){
printf("have %d client\n",qcount);
deleQueue(queue);
exit(0);
}
}
pthread_t pid;
int main(int argc,char** argv){
if(argc<2){
puts("please input port\n");
exit(-1);
}
int port=atoi(argv[1]);
signal(SIGINT,sig_hand);
memset(&mlock,0,sizeof(mlock));
sem_init(&mlock.sem,0,1);
mlock.flag=0;
initQueue(&queue,qcapty);
pthread_create(&pid,NULL,th_hand,(void*)0);
pthread_detach(pid);
initSocket(port);
}