社区
C语言
帖子详情
select()函数的使用在哪些方面?跟send(),和recv()怎么配合使用?
CJackRose
2011-03-29 01:58:43
select()函数的使用在哪些方面?跟send(),和recv()怎么配合使用?
...全文
160
5
打赏
收藏
select()函数的使用在哪些方面?跟send(),和recv()怎么配合使用?
select()函数的使用在哪些方面?跟send(),和recv()怎么配合使用?
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用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的资料,说的很清楚的
Socket中
send
()
函数
和
recv
()
函数
详解
目录1、
send
函数
2、
recv
函数
关于socket的
send
和
recv
的超时设置 1、
send
函数
int
send
( SOCKET s, const char FAR *buf, int len, int flags ); 不论是客户还是服务器应用程序都用
send
函数
来向TCP连接的另一端发送...
send
函数
和
recv
函数
– 当flags为0时,
send
()/
recv
() 在功能上等价于 write()/read() ; flags可选配置: 可选项 含义
send
recv
MSG_OOB 用于传输带外数据(Out Of Band data),即:紧急数据 ✔ ✔ MSG_PEEK 窥探接收缓冲...
select
函数
使用
与不
使用
的区别
select
函数
常用来监视端口是否有可读、可写的事件发生,通俗的讲就是监测读缓冲区有没有数据或者写缓冲区有没有...所以,我们可以在
recv
函数
和
send
函数
前都先
使用
select
函数
进行监测。
select
函数
的另一个优点就是IO
send
()和
recv
()
函数
详解(
send
返回值举例)
转载:
send
()和
recv
()
函数
详解_weixin_34281537的博客-CSDN博客 不论是客户还是服务器应用程序都用
send
函数
来向TCP连接的另一端发送数据。客户程序一般用
send
函数
向服务器发送请求,而服务器则通常用
send
函数
来向...
TCP之
send
&
recv
接触过网络开发的人,大抵都知道,上层应用
使用
send
函数
发送数据,
使用
recv
来接收数据,而
send
和
recv
的实现原理又是怎样的呢? 在前面的几篇文章中,我们有提过,TCP是个可靠的、全双工协议。其流量控制或者拥塞...
C语言
69,369
社区成员
243,082
社区内容
发帖
与我相关
我的任务
C语言
C语言相关问题讨论
复制链接
扫一扫
分享
社区描述
C语言相关问题讨论
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章