linux select 函数使用Demo

参考资料来源

linux tcp并发式服务器应用SELECT函数编写实例源代码 

单线程多客户端的

tcp_select.cpp

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>

const int MYPORT = 1240;
const int BACKLOG = 5;
const int BUF_SIZE = 1024;

using namespace std;

int main()
{
int sock_fd,new_fd;

struct sockaddr_in server_addr,client_addr;
char buf[BUF_SIZE];

if((sock_fd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
perror("socket");
exit(1);
}


int yes = 1;//yes = 1;(即TRUE)

if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) == -1)
{
perror("setsockopt");
exit(1);
}

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(MYPORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
memset(server_addr.sin_zero,'0',sizeof(server_addr.sin_zero));

if(bind(sock_fd,(struct sockaddr * )&server_addr,sizeof(server_addr)) == -1)
{
perror("bind");
exit(1);
}

if(listen(sock_fd,BACKLOG) == -1)
{
perror("listen");
exit(1);
}

fd_set fdsr;
int maxsock;
int i;
struct timeval tv;
int fd_A[BACKLOG];//fd
int con_amount = 0;//
int ret;

maxsock = sock_fd;
//int sin_size = sizeof(client_addr);//int len ;  wrong
socklen_t sin_size = sizeof(client_addr);

while(1)
{
//select函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中
FD_ZERO(&fdsr);
FD_SET(sock_fd,&fdsr);

tv.tv_sec = 30;//每一次查询  套接字是否就绪的时间
tv.tv_usec = 0;
//for(i = 0;i<BACKLOG;i++) //BACKLOG --段错误 吐核
for(i = 0;i<con_amount;i++)
{
if(fd_A[i] != 0)
FD_SET(fd_A[i],&fdsr);
}

ret = select(maxsock + 1,&fdsr,NULL,NULL,&tv);//
if(ret < 0)
{
perror("select");
break;
}
else if(ret == 0)// 没有准备就绪的文件描述符  就进入下一次循环
{
cout << "timeout" << endl;
continue;
}
//有准备就绪的套接字,则分别检查哪些  连接套接字 和 监听套接字 已经就绪  分别进行数据读取和建立连接
for(i = 0;i<con_amount;i++)
{
if(FD_ISSET(fd_A[i],&fdsr))
{
ret = recv(fd_A[i],buf,BUF_SIZE -1,0);
if(ret > 0)
{
buf[ret] = '\0';
cout << "msg from " << fd_A[i] << "is: " << buf << endl;
}
else//------------------client close 
{
cout << "client " << fd_A[i] << " is closed!" << endl;
close(fd_A[i]);
//FD_CLR(fd_A[i],&fdsr);
fd_A[i] = 0;
/*
perro("recv");
exit(1);
*/
}

}
}//end of "for" 

if(FD_ISSET(sock_fd,&fdsr))
{
//if((new_fd = accept(sock_fd,(struct sockaddr *)&client_addr,sizeof(client_addr)) == -1)//error
if((new_fd = accept(sock_fd,(struct sockaddr *)&client_addr,&sin_size)) == -1)
{
perror("accept");
exit(1);
}

//
if(con_amount < BACKLOG)
{
fd_A[con_amount++] = new_fd;
//FD_SET(new_fd,&fdsr); // no need , fd_A[i] != 0 will be added to fdsr in the begin of each loop
cout << "new client:" <<inet_ntoa(client_addr.sin_addr) << ":" << ntohs(client_addr.sin_port) << endl;

//update maxsock ------
if(new_fd > maxsock)
maxsock = new_fd;
}
else
{//send msg 
cout << "Max connections arrived!" << endl;
send(new_fd,"Bye",4,0);
close(new_fd);
//break;//
}

}






}//end of "while(1)"

/*
close(sock_fd);
for(i = 0;i<con_amount;i++)
{
if(fd_A[i] != 0)
close(fd_A[i]);
}
*/


return 0;
}

test_client.c

</pre><pre name="code" class="cpp">#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
/* 服务器程序监听的端口号 */
//#define PORT 1240
/* 我们一次所能够接收的最大字节数 */
#define MAXDATASIZE 100
int main(int argc, char *argv[])
{
/* 套接字描述符 */
int sockfd, numbytes;
char buf[MAXDATASIZE];
int port;
struct hostent *he;
/* 连接者的主机信息 */
struct sockaddr_in their_addr;
/* 检查参数信息 */
if(argc!= 3)
{
/* 如果没有参数,则给出使用方法后退出 */
fprintf(stderr,"usage: server_host server_port\n");
exit(1);
}
/* 取得主机信息 */
if ((he=gethostbyname(argv[1])) == NULL)
{
/* 如果 gethostbyname()发生错误,则显示错误信息并退出 */
herror("gethostbyname");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
{
/* 如果 socket()调用出现错误则显示错误信息并退出 */
perror("socket");
exit(1);
}

port = atoi(argv[2]);//

/* 主机字节顺序 */
their_addr.sin_family = AF_INET;
/* 网络字节顺序,短整型 */
their_addr.sin_port = htons(port);
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
/* 将结构剩下的部分清零*/
bzero(&(their_addr.sin_zero), 8);
if(connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)
{
/* 如果 connect()建立连接错误,则显示出错误信息,退出 */
perror("connect");
exit(1);
}

/*
if((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1)
{
// 如果接收数据错误,则显示错误信息并退出 
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("Received: %s",buf);

bzero(buf,100);*/

strcpy(buf,"Received your message!\n");
send(sockfd,buf,strlen(buf),0);
/*
sleep(100);
strcpy(buf,"Received your message2!\n");
send(sockfd,buf,strlen(buf),0);
*/

//close(sockfd);
return 0;
}


测试结果:



笔记:


select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中

 

连接数达到最大的假象

分析:客户端连接断开仅仅fd_A[i] = 0; 并没有压缩和清空数组。

所以用数组存放套接字描述符并不是最佳选择。(需要删除数组中的元素和调整长度)

而conn_amount没有减小,会造成位置浪费,

即设BACKLOG = 5,如果有5个连接,断开了3个,第6个连接到来的时候仍然被判断为超过最大连接数。

 

没有初始化的数组元素越界异常:




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值