select()函数的使用在哪些方面?跟send(),和recv()怎么配合使用?

CJackRose 2011-03-29 01:58:43
select()函数的使用在哪些方面?跟send(),和recv()怎么配合使用?
...全文
160 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiyuer 2011-10-08
  • 打赏
  • 举报
回复
hzhxxx 高手啊,,,学习了!~~~~
hzhxxx 2011-03-29
  • 打赏
  • 举报
回复

/*****************************************************************/
//name : tcpclient.cpp
//function : tcp 请求
//copyright :
//author :
//date : 2010-03-20
//modifier : 使用非阻塞socket,结合select
//去判断数据发送和接收情况,默认最坏情况,从连接发起到发送接收完
//成数据,都失败4次,耗时最大浪费接近 3 秒,其中发送1000 毫
//秒,连接和接收各1000毫秒。实际每次操作的最大耗时是C:
//设定A=(sum(1~(MAXIMUMERRORNUMBER - 1)) *1000) 毫秒,如果A大于
//timeoutmillisecond,则相应MAXIMUMERRORNUMBER缩小,直到符合
//A小于timeoutmillisecond;
//设定B=(timeoutmillisecond + MAXIMUMERRORNUMBER * 100) 毫秒,
//则最大耗时基本上小于B;最终是:A<C<B
/***********************************************************************/

#include "tcpclient.h"
#include "func_utility.h"
#include "datetime.h"


//#include "CftLogger.h"
//#include "globalconfig.h"
//extern CCftLogger* gPtrAppLog;
//extern GlobalConfig* gPtrConfig;


#include <map>
#include <vector>
using namespace std;

#ifdef WIN32
#include "WINSOCK2.H"
#include <io.h>
#include "ws2tcpip.h"


class CWSADATA
{
public:
CWSADATA()
{
WSADATA wsaData;
WSAStartup(0x101,&wsaData);
}
WSADATA()
{
WSACleanup();
}
};
CWSADATA instance;


#else
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <netdb.h>
//#include <sys/select.h>
#endif

using namespace wappay;

CTcpClient::CTcpClient(const std::string& srvip ,int port,int timeoutmillisecond,
int maxretrycount)
:m_fd(MY_INVALID_SOCKET),
m_timeoutmillisecond(timeoutmillisecond),
m_basicmicrosecond(200000),
MAXIMUMERRORNUMBER(maxretrycount),
m_srvip(srvip),
m_srvport(port)
{
if(this->m_timeoutmillisecond > 6000 * 10 || this->m_timeoutmillisecond < 1000)
{
m_timeoutmillisecond = 1500;
}
}

CTcpClient::~CTcpClient(void)
{
release();
}

void CTcpClient::release()
{
if(m_fd != MY_INVALID_SOCKET)
{
closesock(m_fd);
m_fd = MY_INVALID_SOCKET;
}
}

int CTcpClient::closesock(mysocket connectsocket)
{
#ifdef WIN32
return ::closesocket(connectsocket);
#else
return ::close(connectsocket);
#endif
}

//设置 socket 阻塞和非阻塞
bool CTcpClient::setblocking(bool op)
{
unsigned long result = 0;
#ifdef WIN32
if(op)
{
result = 0;
}
else
{
result = 1;
}
result = ::ioctlsocket(m_fd,FIONBIO,(u_long*)&result);
#else
int fl = 0;
if ((fl = fcntl(m_fd,F_GETFL,0)) == -1)
{
return false;
}

if(op)
{
fl &= ~O_NONBLOCK;
}
else
{
fl |= O_NONBLOCK;
}

if (fcntl(m_fd,F_SETFL,fl) == -1)
{
result = 1;
}
#endif
return result == 0?true:false;
}

//支持指数回退,重新连接
int CTcpClient::connect()
{
int times = 1;
int result = MY_INVALID_SOCKET;

CDateTime start;
//printf("%d,%s\n",times,dt.LongDateTimeWithMilliSec().c_str());
while(result != 0 && times <= MAXIMUMERRORNUMBER)
{
release();
//操作最大时间
CDateTime now;
if(now.SubMilliSecond(start) >= m_timeoutmillisecond)
{
break;
}
m_fd = socket(AF_INET,SOCK_STREAM,0);
if(!this->setblocking(false))
{
times++;
result = MYERRNO;
continue;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(m_srvip.c_str());
addr.sin_port = htons(m_srvport);
result = ::connect(m_fd,(struct sockaddr*)&addr,sizeof(addr));
if (result == 0)
{
//连接成功
return result;
}
result = MYERRNO;

//开始用 select 去判断指定socket是否连接成功
fd_set writeset,exceptset;
FD_ZERO(&writeset);
FD_SET(m_fd,&writeset);
FD_ZERO(&exceptset);
FD_SET(m_fd,&exceptset);
struct timeval timeout = {0,0};
timeout.tv_sec = 0;
timeout.tv_usec = this->m_basicmicrosecond * times;
#ifdef WIN32
if (result != WSAEWOULDBLOCK)
{
times++;
continue;
}
do
{
result = select(0,0,&writeset,&exceptset,&timeout);
}while(result < 0 && MYERRNO == EINTR);
#else
if (result != EINPROGRESS)
{
times++;
continue;
}
do
{
result = select(m_fd + 1,0,&writeset,&exceptset,&timeout);
}while(result < 0 && MYERRNO == EINTR);
#endif

if(result == MY_SOCKET_ERROR)
{
//发生错误
result = MYERRNO;
times++;
continue;
}
else if(result == 0)
{
//超时发生,放弃连接
result = MY_SOCKET_ERROR;
times++;
continue;
}

if(FD_ISSET(m_fd,&writeset))
{
//完成连接
result = 0;
#ifdef WIN32
//
#else
socklen_t len = sizeof(result);
result = getsockopt(m_fd,SOL_SOCKET,SO_ERROR,(char*)&result,&len);
#endif
}
else
{
//超时发生,放弃连接
result = MY_SOCKET_ERROR;
}
times++;
}
//CDateTime dt1;
//printf("%d,%s\n",times,dt1.LongDateTimeWithMilliSec().c_str());

return result;
}

bool CTcpClient::send(const char *data,int total_len)
{
//int total_len = length;
int sendlen = 0;
int times = 1;
CDateTime start;
//printf("%d,%s\n",times,dt.LongDateTimeWithMilliSec().c_str());
while(sendlen < total_len
&& times <= MAXIMUMERRORNUMBER)
{
//操作最大时间
CDateTime now;
if(now.SubMilliSecond(start) >= m_timeoutmillisecond)
{
break;
}
int newsend = ::send(m_fd,data + sendlen ,total_len - sendlen,0);
if(newsend == MY_SOCKET_ERROR && MYERRNO != EAGAIN)
{
return false;
}
sendlen += (newsend>0?newsend:0);

//使用select 去判断是否数据可以继续发
fd_set writeset,exceptset;
FD_ZERO(&writeset);
FD_SET(m_fd,&writeset);
FD_ZERO(&exceptset);
FD_SET(m_fd,&exceptset);
struct timeval timeout = {0,0};
timeout.tv_sec = 0;
timeout.tv_usec = m_basicmicrosecond * times;
int result = 0;
#ifdef WIN32
do
{
result = select(0,0,&writeset,&exceptset,&timeout);
}while(result < 0 && MYERRNO == EINTR);
#else
do
{
result = select(m_fd + 1,0,&writeset,&exceptset,&timeout);
}while(result < 0 && MYERRNO == EINTR);
#endif

if(FD_ISSET(m_fd,&exceptset))
{
break;
}

//printf("%d\n",result);
//内核缓冲区间有空间,数据可以继续发送
if(FD_ISSET(m_fd,&writeset))
{
//
}

//发送无效果,算超时一次,算一次失败
if(newsend <= 0)
{
times++;
}
}

if(sendlen != total_len)
{
return false;
}

return true;
}

//接收数据,使用字符串协议,尽量一次收取全部数据
int CTcpClient::receive(char *buff,int bufflen,const char *end)
{
if(buff == 0 || bufflen <= 0)
{
return 0;
}

//必须是1,否则select 会被至少执行两次,最后一次必须超时才能返回
//如果要可配置,必须指定协议解析模块,能判断数据接收完毕后退出
//一般情况下,使用http 1.0 协议,指定 connection :close ,
//TCP 连接会进入同时关闭的情况,这样双方都不太消耗网络资源

int received = 0;
int times = 1;
CDateTime start;
//gPtrAppLog->debug("start:%d,%s\n",times,start.LongDateTimeWithMilliSec().c_str());
while(received < bufflen
&& times <= MAXIMUMERRORNUMBER)
{

//操作最大时间
CDateTime now;
if(now.SubMilliSecond(start) >= m_timeoutmillisecond)
{
break;
}

//开始用 select 去判断指定socket是否可以有数据读
fd_set readset,exceptset;
FD_ZERO(&readset);
FD_SET(m_fd,&readset);
FD_ZERO(&exceptset);
FD_SET(m_fd,&exceptset);
struct timeval timeout = {0,0};
timeout.tv_sec = 0;
timeout.tv_usec = this->m_basicmicrosecond * times;
int result = 0;
#ifdef WIN32
do
{
result = ::select(0,&readset,0,&exceptset,&timeout);
}while(result < 0 && MYERRNO == EINTR);
#else
do
{
result = ::select(m_fd + 1,&readset,0,&exceptset,&timeout);
}while(result < 0 && MYERRNO == EINTR);
#endif

// CDateTime dt1;
//gPtrAppLog->debug("vvvvv=%d,%d,%d,%s\n",times,result,MYERRNO,dt1.LongDateTimeWithMilliSec().c_str());

if(result == 0)
{
times++;
continue;
}

if(FD_ISSET(m_fd,&exceptset))
{
break;
}

//gPtrAppLog->debug("receive=%d\n",result);
//有数据可以读取
if(FD_ISSET(m_fd,&readset))
{
#ifdef WIN32
int newrecv = ::recv(m_fd,buff + received,bufflen - received,0);
#else
int newrecv = ::recv(m_fd,buff + received,bufflen - received,MSG_WAITALL);
#endif

//gPtrAppLog->debug("vv=%d,%d\n",newrecv,received);

if(newrecv == MY_SOCKET_ERROR)
{
int err = MYERRNO;
#ifdef WIN32
if(err != EAGAIN && err != WSAEWOULDBLOCK)
{
break;
}
#else
if(err != EAGAIN && err != EWOULDBLOCK)
{
break;
}
#endif
}
else if(newrecv == 0)
{
//连接被关闭
break;
}
else if(newrecv > 0)
{
//printf("aa=%d,%d\n",newrecv,received);
received += newrecv;
//数据包已经完整(这个方法是很不保险,如果数
//据报过大,http 头完整后,但是http数据部分却可能接收不完整,比如resin3.2)
//比较好的办法还是要通过socket 来判断数据是否接收完全
if(this->checkpacket(buff,end))
{
break;
}

//不管收到了多少数据,算正常
continue;
}
else
{
//
}
}
else
{
//算超时一次
times++;
}
}
CDateTime dt1;
//gPtrAppLog->debug("%d,%d,%s\n",times,received,dt1.LongDateTimeWithMilliSec().c_str());
return received;
}

//初步检测 数据包是否完整
bool CTcpClient::checkpacket(const char* buffer,const char *end)
{
if(strstr(buffer,end) != 0)
{
return true;
}

return false;
}

CJackRose 2011-03-29
  • 打赏
  • 举报
回复
资料有,但好象机制使用原因比较少?
赵4老师 2011-03-29
  • 打赏
  • 举报
回复
从来没用过select()函数但开发过不计其数socket网关的飘过。
bdmh 2011-03-29
  • 打赏
  • 举报
回复
去看一下socket的资料,说的很清楚的

69,369

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧