基于select模型的server
程序员文章站
2022-05-07 22:58:52
...
前面一篇介绍了IO模型。其中重点介绍了IO多路转接中的三种模型,包括了select,poll,epoll三种。下面就是基于select模型编写的服务器与客户机,两者可以进行交互。
服务器端代码:k
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/select.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
int fdArray[sizeof(fd_set)*8];
int startup(int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock<0){
perror("socket");
exit(2);
}
//为了防止服务器主动断开连接,无法连接重启的问题
int opt = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_port = htons(port);
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
exit(3);
}
if(listen(sock,5)<0)
{
perror("listen");
exit(4);
}
return sock;
}
// ./select_server 8080
int main(int argc,char* argv[])
{
if(argc!=2)
{
printf("Usage: %s port\n",argv[0]);
return 1;
}
int listen_sock = startup(atoi(argv[1]));
//已经创建listen_sock
fdArray[0] = listen_sock;//合法的描述符
int num = sizeof(fdArray)/sizeof(fdArray[0]);
int i = 1;
for(i;i<num;i++)
{
fdArray[i] = -1;
}
while(1)
{
fd_set rfds;
FD_ZERO(&rfds);//清空文件描述符集
//查找所有文件描述符集最大的
int max = fdArray[0];//永远不会出错,因为第一个永远是有效的
for(i = 0;i<num;i++)
{
//将fdArray中所有有效的文件描述符设置进了文件描述符集rfds
if(fdArray[i]>=0)
{
//这就是合法的描述符
FD_SET(fdArray[i],&rfds);
if(max<fdArray[i]){
max = fdArray[i];
}
}
}
struct timeval timeout = {5,0};
switch(select(max+1,&rfds,NULL,NULL,&timeout))
{
case 0:
printf("timeout\n");
break;
case -1:
perror("select");
break;
default:
//走到这里,说明至少有一个文件描述符上的事件已经就绪了
{
for(i=0;i<num;i++)
{
if(fdArray[i]==-1){
continue;
}
if(i==0 && fdArray[i]==listen_sock && FD_ISSET(fdArray[i],&rfds)){
//已知哪个文件描述符就绪了并且能够获得新连接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
if(new_sock<0){
perror("accept");
continue;
}
//不敢被阻塞,
//添加到数组里
for(i=0;i<num;i++)
{
if(fdArray[i]==-1){
break;
}
}
//找到一个没有被使用的位置
if(i<num){
fdArray[i] = new_sock;
}//如果没有被添加说明数组满了,不能再处理新连接了
else{
close(new_sock);
}
//新连接要么已经放到数组里要么被关闭了
continue;
}
//走到这里说明是普通的读事件就绪
if(FD_ISSET(fdArray[i],&rfds))
{
char buf[1024] = {0};
ssize_t s = read(fdArray[i],buf,sizeof(buf)-1);
if(s==0){
printf("client quit");
close(fdArray[i]);//关闭当前的文件描述符
fdArray[i]=-1;//并且将数组里的值设为-1,不然下次可能会再被读到
}
if(s<0){
perror("read");
close(fdArray[i]);
fdArray[i]=-1;
break;
}
printf("client: %s\n",buf);
write(fdArray[i],buf,strlen(buf));
}
}
}
break;
}
}
return 0;
}
客户端代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
int main(int argc,char* argv[])
{
if(argc!=2)
{
printf("Usage :./select_client [port]");
return 1;
}
//
struct sockaddr_in client;
client.sin_port = htons(atoi(argv[1]));
client.sin_family = AF_INET;
client.sin_addr.s_addr = htonl(INADDR_ANY);
//创建套接字
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return 1;
}
//连接服务器
if(connect(sock,(struct sockaddr*)&client,sizeof(client))<0)
{
perror("connect");
return 1;
}
for(;;)
{
//输入消息并刷新缓冲区
printf("client >");
fflush(stdout);
//将消息读到buf里
char buf[1024] = {0};
read(0,buf,sizeof(buf)-1);
//将消息写给文件描述符
if(write(sock,buf,strlen(buf))<0){
perror("write");
continue;
}
//将服务器返回的消息写到buf里
int ret = read(sock,buf,sizeof(buf)-1);
if(ret<0){
perror("read");
continue;
}
if(ret==0)
{
printf("server close\n");
break;
}
printf("server:%s\n",buf);
}
close(sock);
return 0;
}
这样通过客户端与服务器的交互就可以互通消息了。
上一篇: NIO Select
下一篇: 下拉框带搜索功能