以下为本人编写的几个linux下通常的服务器模型,已经经过测试。(第三方网络开发库libevent的使用案例见marioTCP,网上有下载)
server.c
/**
*@Description:模式:每个客户端连接分配一个进程(或线程)处理
*@Date: 2014-03-19 1:17 AM
*@Author: xl
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <time.h>
const char* log_file="server.log";
int writeLog(const char *format,...);
void waitChild(int signo)
{
int status;
while(-1!=wait(&status));
}
void* thread_client_process(void *arg)
{
writeLog("thread start.");
pthread_detach(pthread_self());
int fd=*(int*)arg;
char buf[1024]={0};
while(1)
{
memset(buf,sizeof(buf),0);
//MSG_PEEK
if(0>=recv(fd,buf,sizeof(buf),0))
{
writeLog("(fd=%d)recv error",fd);
close(fd);
//shutdown(fd,SHUT_RDWR);
return NULL;
}
writeLog("buf:%s",buf);
}
return NULL;
}
int writeLog(const char *format,...)
{
FILE *fp;
time_t t;
struct tm* tv;
char tmp[256];
va_list args;
fp = fopen(log_file,"a");
if(fp==NULL)
{
perror("fopen() failed!\n");
return -1;
}
time(&t);
tv=localtime(&t);//&t脳梅脦陋脰禄露脕虏脦脢媒
va_start(args,format);
vsprintf(tmp,format,args);
va_end(args);
fprintf(fp,"[%04d %02d.%02d %02d:%02d:%02d] - %s\n",tv->tm_year+1900, tv->tm_mon+1, tv->tm_mday, tv->tm_hour, tv->tm_min, tv->tm_sec, tmp);//why tm_mon need +1?
fclose(fp);
return 0;
}
int main()
{
int sockfd;
int clifd;
struct sockaddr_in srvaddr;
struct sockaddr_in cliaddr;
int addrlen;
writeLog("server start at:%s",__DATE__);
//run as daemon
daemon(1,1);
sockfd=socket(PF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
writeLog("create socket failed.");
return -1;
}
srvaddr.sin_family=AF_INET;
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
srvaddr.sin_port = htons(8888);
if(bind(sockfd,(const struct sockaddr*)&srvaddr,sizeof(srvaddr)) < 0)
{
writeLog("bind failed.%s",strerror(errno));
return -1;
}
if(listen(sockfd,10) < 0)
{
writeLog("listen failed.%s",strerror(errno));
return -1;
}
while(1)
{
clifd=accept(sockfd,(struct sockaddr*)&cliaddr,(socklen_t*)&addrlen);
if(clifd==-1)
{
writeLog("failed.%s",strerror(errno));
continue;
}
writeLog("accept!!");
//writeLog("accept:%s:%d",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
#ifdef __FORK__
//安装回收子进程资源信号
signal(SIGCHLD,waitChild);
//dispatch a process
pid_t pid=fork();
if(pid==0)
{
close(sockfd);//子进程关闭父进程中的文件描述符副本
int ret;
char buf[1024]={0};
while(1)
{
bzero(buf,sizeof(buf));
ret=recv(clifd,buf,sizeof(buf),0);
if(ret<=0) {
//客户端调用关闭套接字,ret=0
if(ret==-1)
writeLog("recv error:%s",strerror(errno));
close(clifd);
_exit(0);//退出进程
}
writeLog("recv: %s\n",buf);
}
_exit(0);
}
#else
//dispatch a thread
pthread_t tid;
if(0!=pthread_create(&tid,NULL,thread_client_process,&clifd))
{
writeLog("pthread_create fail");
continue;
}
#endif
#ifdef __FORK__
close(clifd); //父进程关闭子进程中的文件描述符副本
#endif
}
close(sockfd);
return 0;
}
select_server.c
/**
*@Description: server select模式
* 可用telnet客户端测试本程序
*@Date: 2014-03-18 17:22 PM
*@Author: xl
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <time.h>
#include <sys/select.h>
#define SERVER_PORT 8888
const char* log_file="server.log";
int writeLog(const char *format,...)
{
FILE *fp;
time_t t;
struct tm* tv;
char tmp[256];
va_list args;
fp = fopen(log_file,"a");
if(fp==NULL)
{
perror("fopen() failed!\n");
return -1;
}
time(&t);
tv=localtime(&t);//&t脳梅脦陋脰禄露脕虏脦脢媒
va_start(args,format);
vsprintf(tmp,format,args);
va_end(args);
fprintf(fp,"[%04d %02d.%02d %02d:%02d:%02d] - %s\n",tv->tm_year+1900, tv->tm_mon+1, tv->tm_mday, tv->tm_hour, tv->tm_min, tv->tm_sec, tmp);//why tm_mon need +1?
fclose(fp);
return 0;
}
int main()
{
int sockfd;
int clifd;
struct sockaddr_in srvaddr;
struct sockaddr_in cliaddr;
int addrlen;
int on;
writeLog("server start at:%s",__DATE__);
//run as a daemon
daemon(1,1);
sockfd=socket(PF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
writeLog("create socket failed.");
return -1;
}
srvaddr.sin_family=AF_INET;
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
srvaddr.sin_port = htons(SERVER_PORT);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(char*)&on,sizeof(on));
if(bind(sockfd,(const struct sockaddr*)&srvaddr,sizeof(srvaddr)) < 0)
{
writeLog("bind failed.%s",strerror(errno));
return -1;
}
if(listen(sockfd,10) < 0)
{
writeLog("listen failed.%s",strerror(errno));
return -1;
}
int i;
int maxfd=sockfd;
fd_set readfds;
fd_set masterfds;
FD_ZERO(&readfds);
FD_SET(sockfd,&readfds);
masterfds = readfds;
while(1)
{
//重新整理监控的套接字集
readfds=masterfds;
//select返回后,readfds装载了可读的套节字集合
int nfds=select(maxfd+1,&readfds,NULL,NULL,NULL);
if(nfds==-1)
{
writeLog("select error:%s",strerror(errno));
return -1;
if(errno==EBADF)
continue;
else
break;
}
for(i=0;i<=maxfd;i++)//依次遍历(如果连接量大,效率低下问题显著,epoll在这方面作了优化)
{
if(FD_ISSET(i,&readfds))//鏄惁鍦╮eadfds闆嗗悎涓?
{
if(i==sockfd)本地监听的套接字
{
clifd=accept(sockfd,(struct sockaddr*)&cliaddr,(socklen_t*)&addrlen);
if(clifd==-1)
{
writeLog("acceprt failed.%s",strerror(errno));
continue;
}
writeLog("new connection.fd=%d",clifd);
FD_SET(clifd,&masterfds);
if(clifd>maxfd)
maxfd=clifd;
send(clifd,"Welcome!",7,0);
}
else//客户端套接字,data coming,在这里recv肯定不会阻塞
{
writeLog("recv from fd=%d",i);
//continue;
//若不在这里读完,select下次监听会马上返回,可以把上面的continue打开试试
//这个跟epoll Edge trigger不同(读事件只触发一次,没读完的数据等待下次读事件触发,由epoll_wait返回)
/开始接收数据
char buf[1024]={0};
memset(buf,sizeof(buf),0);
//MSG_PEEK
if(0>=recv(i,buf,sizeof(buf),0))
{
writeLog("(fd=%d)recv error",i);
close(i);
FD_CLR(i,&masterfds);//从监控集合中移除
}
else
writeLog("buf:%s",buf);
}
}
}
}
close(sockfd);
return 0;
}
epoll_server.c
/**
*epoll ET triger,2014-05-04
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <strings.h>
#include <errno.h>
#define LISTEN_PORT 8888
#define MAX_EVENTS 5000
int epfd;
void setnonblocking(int fd)
{
int flag=fcntl(fd,F_GETFD);
if(flag==-1)
{
printf("%s:%s\n",__func__,strerror(errno));
close(fd);
return;
}
flag|=O_NONBLOCK;
if(-1==fcntl(fd,F_SETFD,flag))
{
printf("%s:%s\n",__func__,strerror(errno));
close(fd);
}
}
void do_use_fd(int fd)
{
char buf[10]={0};
int ret;
ret=recv(fd,buf,sizeof(buf),0);
if(ret<=0)
{
struct epoll_event ev;
ev.data.fd=fd;
epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&ev);
close(fd);
printf("close %d\n",fd);
}
else
printf("data:%s\n",buf);
}
int main()
{
int listener,client;
struct sockaddr_in listenaddr,clientaddr;
socklen_t addrlen;
struct epoll_event ev, *events;
int nfds;
int n;
listener = socket(PF_INET,SOCK_STREAM,0);
if(-1==listener)
{
perror("socket error");
return -1;
}
bzero(&listenaddr,sizeof(listenaddr));
listenaddr.sin_family = AF_INET;
listenaddr.sin_port = htons(LISTEN_PORT);
if(-1==bind(listener,(struct sockaddr*)&listenaddr,sizeof(struct sockaddr_in)) )
{
perror("bind error");
return -2;
}
listen(listener,200);
//epoll-ET
epfd=epoll_create(MAX_EVENTS);
ev.events = EPOLLIN|EPOLLET;
ev.data.fd = listener;
epoll_ctl(epfd,EPOLL_CTL_ADD,listener,&ev);
//浠ヤ笅浠巈poll鎵嬪唽涓憳鎶勭殑
for(;;)
{
nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
for(n = 0; n < nfds; ++n) {
if(events[n].data.fd == listener) {
client = accept(listener, (struct sockaddr *) &clientaddr,
&addrlen);
if(client < 0){
perror("accept");
continue;
}
setnonblocking(client);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%d\n",
client);
return -1;
}
}
else
do_use_fd(events[n].data.fd);
}
}
return 0;
}