1.介绍
Linux网络循环服务器是指逐个处理客户端的连接,处理完一个连接后再处理下一个连接,是一个串行处理的方式,比较适合时间服务器,DHCP服务器.对于TCP服务器来说,主要阻塞在accept函数,等待客户端的连接。而对于UDP服务器来说,主要阻塞在recv函数.
2.循环服务器模型
TCP循环服务器:
算法如下:socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
read(...);
process(...);
write(...);
close(...);//关闭客户端连接
}
close(....);//关闭服务器连接
UDP循环服务器:
算法如下:
socket(...)
bind(....);
while(1){
recvfrom(....);
process(...);
sendto(....);
close(....);//关闭客户端连接
}
close(....);//关闭服务器连接
从上面的流程可以看出,TCP循环服务器在accept处阻塞一直等待客户端的到来,而UDP循环服务器在recv处阻塞,等待客户端发送数据.
3. 循环服务器的例子
下面的程序是一个时间服务器,客户端发出TIME时间请求,服务器将本地时间返回给客户端.
(1)TCP循环服务器
服务器:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
/**
TCP循环服务器
在accept处阻塞等待客户端的连接,并处理请求
**/
#define PORT 8888
#define BUFFERSIZE 1024
#define LISTEN 10
int main(int argc,char*argv[]){
int s;
int ret;
int len;
int size;
int sc;
time_t now;
char buffer[BUFFERSIZE];
struct sockaddr_in server_addr,client_addr;
//建立流式套接字
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
//将地址绑定到套接字上
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(PORT);
ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret<0){
perror("bind error");
return -1;
}
//监听队列的长度
listen(s,LISTEN);
len=sizeof(client_addr);
//接受客户端的请求
while(1){
sc=accept(s,(struct sockaddr*)&client_addr,&len);//等待客户端的连接,client_addr存放的是客户端的信息
memset(buffer,0,BUFFERSIZE);
size=recv(sc,buffer,BUFFERSIZE,0);//利用套接字描述符sc进行通信
if(size<0){
perror("recv error");
break;
}
else if(!strncmp(buffer,"TIME",4)){
now=time(NULL);
sprintf(buffer,"%24s",ctime(&now));
send(sc,buffer,strlen(buffer),0);
}
close(sc);//关闭本次通信的客户端连接
}
close(s);
return 0;
}
客户端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
/**
循环服务器客户端程序
**/
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
int s;
int ret;
int size;
struct sockaddr_in server_addr;
char buffer[BUFFERSIZE];
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
bzero(&server_addr,sizeof(server_addr));
//将地址结构绑定到套接字
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//连接服务器
ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror("connect error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
strcpy(buffer,"TIME");
size=send(s,buffer,strlen(buffer),0);
if(size<0){
perror("send error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
size=recv(s,buffer,BUFFERSIZE,0);
if(size<0){
perror("recv error");
return;
}
printf("%s",buffer);
close(s);
return 0;
}
运行结果:
[root@localhost 14章服务器模式]# ./circle-tcpc14Sat Feb 18 09:44:31 2012
(2)UDP循环服务器
服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
/**
UDP循环服务器
原理:服务器在recv函数与处理业务之间轮循处理
**/
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
int s;
struct sockaddr_in server_addr,client_addr;//分别表示客户端地址与服务器端地址
time_t now;
int ret;
int size;
char buffer[BUFFERSIZE];
//建立数报套接字
s=socket(AF_INET,SOCK_DGRAM,0);//数据报
if(s<0){
perror("socket error");
return -1;
}
//地址绑定
bzero(&server_addr,sizeof(server_addr));//地址结构清0
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(PORT);
ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror("bind error");
return -1;
}
//数据报套接字没有流量控制所以没有监听listen,也没有三次握手,所以没有接受连接accept
while(1){
memset(buffer,0,BUFFERSIZE);//清0
int len=sizeof(client_addr);
size=recvfrom(s,buffer,BUFFERSIZE,0,(struct sockaddr*)&client_addr,&len);
if(size<=0){
perror("recvfrom");
}else{
if(!strncmp(buffer,"TIME",4)){//判断是否是合法数据
memset(buffer,0,BUFFERSIZE);
now=time(NULL);//获得当前时间
sprintf(buffer,"%24s",ctime(&now));//ctime所指向的最后一个字符是\n
sendto(s,buffer,strlen(buffer),0,(struct sockaddr*)&client_addr,len);//发送数据,第3项表示发送数据的长度
}
}
}
close(s);
return 0;
}
客户端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
/**
UDP客店端,客户端向服务器发送时间请求,服务器返回相应的时间
**/
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
int s;//套接字描述符
int ret;//建立套接字的返回值
int size;
struct sockaddr_in server_addr;//地址结构
int len;
char buffer[BUFFERSIZE];
s=socket(AF_INET,SOCK_DGRAM,0);//建立流式套接字
if(s<0){
perror("socket error");
return -1;
}
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(PORT);
memset(buffer,0,BUFFERSIZE);
strcpy(buffer,"TIME");
//向服务器发送数据
size=sendto(s,buffer,strlen(buffer),0,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(size<0){
perror("sendto error");
return -1;
}
//从服务器接收数据
len=sizeof(server_addr);
size=recvfrom(s,buffer,BUFFERSIZE,0,(struct sockaddr*)&server_addr,&len);
if(size<0){
perror("recvfrom error");
return -1;
}
//write(1,buffer,size);
printf("%s\n",buffer);
close(s);
return 0;
}
运行结果:
[root@localhost 14章服务器模式]# ./circle-udpc14
Sat Feb 18 09:59:22 2012
总结:本文主要介绍了循环服务器的算法流程,并且给出了TCP与UDP循环服务器实例.