[From]http://dev.cbw.com/c/c/200510195601_4292587.shtml
1.
首先将标志位设为
Non-blocking
模式,准备在非阻塞模式下调用
connect
函数
2.
调用
connect
,正常情况下,因为
TCP
三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回
EINPROGRESS
,表示在建立连接但还没有完成。
3.
在读套接口描述符集
(fd_set rset)
和写套接口描述符集
(fd_set wset)
中将当前套接口置位(用
FD_ZERO()
、
FD_SET()
宏),并设置好超时时间
(struct timeval *timeout) 4.
调用
select( socket, &rset, &wset, NULL, timeout )
返回
0
表示
connect
超时
如果你设置的超时时间大于
75
秒就没有必要这样做了,因为内核中对
connect
有超时限制就是
75
秒。
[From]http://www.ycgczj.com.cn/34733.html
网络编程中
socket
的分量我想大家都很清楚了,
socket
也就是套接口,在套接口编程中,提到超时的概念,我们一下子就能想到
3
个:发送超时,接收超时,以及
select
超时(注:
select
函数并不是只用于套接口的,但是套接口编程中用的比较多),在
connect
到目标主机的时候,这个超时是不由我们来设置的。不过正常情况下这个超时都很长,并且
connect
又是一个阻塞方法,一个主机不能连接,等着
connect
返回还能忍受,你的程序要是要试图连接多个主机,恐怕遇到多个不能连接的主机的时候,会塞得你受不了的。我也废话少说,先说说我的方法,如果你觉得你已掌握这种方法,你就不用再看下去了,如果你还不了解,我愿意与你分享。本文是已在
Linux
下的程序为例子,不过拿到
Windows
中方法也是一样,无非是换几个函数名字罢了。
Linux
中要给
connect
设置超时,应该是有两种方法的。一种是该系统的一些参数,这个方法我不讲,因为我讲不清楚
:P
,它也不是编程实现的。另外一种方法就是变相的实现
connect
的超时,我要讲的就是这个方法,原理上是这样的:
1
.建立
socket 2
.将该
socket
设置为非阻塞模式
3
.调用
connect() 4
.使用
select()
检查该
socket
描述符是否可写(注意,是可写)
5
.根据
select()
返回的结果判断
connect()
结果
6
.将
socket
设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)
如果你对网络编程很熟悉的话,其实我一说出这个过程你就知道怎么写你的程序了,下面给出我写的一段程序,仅供参考。
/****************************** * Time out for connect() * Write by Kerl W ******************************/
#include <sys/socket.h> #include <sys/types.h>
#define TIME_OUT_TIME 20 //connect
超时时间
20
秒
int main(int argc , char **argv) { ……………… int sockfd = socket(AF_INET, SOCK_STREAM, 0); if(sockfd < 0) exit(1);
struct sockaddr_in serv_addr; ………//
以服务器地址填充结构
serv_addr int error=-1, len; len = sizeof(int); timeval tm; fd_set set;
unsigned long ul = 1; ioctl(sockfd, FIONBIO, &ul); //
设置为非阻塞模式
bool ret = false; if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { tm.tv_set = TIME_OUT_TIME; tm.tv_uset = 0; FD_ZERO(&set); FD_SET(sockfd, &set); if( select(sockfd+1, NULL, &set, NULL, &tm) > 0) { getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len); if(error == 0) ret = true; else ret = false; } else ret = false; } else ret = true;
ul = 0; ioctl(sockfd, FIONBIO, &ul); //
设置为阻塞模式
if(!ret) { close( sockfd ); fprintf(stderr , "Cannot Connect the server!/n"); return; } fprintf( stderr , "Connected!/n"); //
下面还可以进行发包收包操作
…………… }
以上代码片段,仅供参考,也是为初学者提供一些提示,主要用到的几个函数,
select, ioctl, getsockopt
都可以找到相关资料,具体用法我这里就不赘述了,你只需要在
linux
中轻轻的敲一个
man <
函数名
>
就能够看到它的用法。
此外我需要说明的几点是,虽然我们用
ioctl
把套接口设置为非阻塞模式,不过
select
本身是阻塞的,阻塞的时间就是其超时的时间由调用
select
的时候的最后一个参数
timeval
类型的变量指针指向的
timeval
结构变量来决定的,
timeval
结构由一个表示秒数的和一个表示微秒数(
long
类型)的成员组成,一般我们设置了秒数就行了,把微妙数设为
0
(注:
1
秒等于
100
万微秒)。而
select
函数另一个值得一提的参数就是上面我们用到的
fd_set
类型的变量指针。调用之前,这个变量里面存了要用
select
来检查的描述符,调用之后,针对上面的程序这里面是可写的描述符,我们可以用宏
FD_ISSET
来检查某个描述符是否在其中。由于我这里只有一个套接口描述符,我就没有使用
FD_ISSET
宏来检查调用
select
之后这个
sockfd
是否在
set
里面,其实是需要加上这个判断的。不过我用了
getsockopt
来检查,这样才可以判断出这个套接口是否是真的连接上了,因为我们只是变相的用
select
来检查它是否连接上了,实际上
select
检查的是它是否可写,而对于可写,是针对以下三种条件任一条件满足时都表示可写的:
1
)套接口发送缓冲区中的可用控件字节数大于等于套接口发送缓冲区低潮限度的当前值,且或者
i)
套接口已连接,或者
ii)
套接口不要求连接(
UDP
方式的)
2
)连接的写这一半关闭。
3
)有一个套接口错误待处理。
这样,我们就需要用
getsockopt
函数来获取套接口目前的一些信息来判断是否真的是连接上了,没有连接上的时候还能给出发生了什么错误,当然我程序中并没有标出那么多状态,只是简单的表示可连接
/
不可连接。
下面我来谈谈对这个程序测试的结果。我针对
3
种情形做了测试:
1
.
目标机器网络正常的情况
可以连接到目标主机,并能成功以阻塞方式进行发包收包作业。
2
.
目标机器网络断开的情况
在等待设置的超时时间(上面的程序中为
20
秒)后,显示目标主机不能连接。
3
.
程序运行前断开目标机器网络,超时时间内,恢复目标机器的网络
在恢复目标主机网络连接之前,程序一只等待,恢复目标主机后,程序显示连接目标主机成功,并能成功以阻塞方式进行发包收包作业。
以上各种情况的测试结果表明,这种设置
connect
超时的方法是完全可行的。我自己是把这种设置了超时的
connect
封装到了自己的类库,用在一套监控系统中,到目前为止,运行还算正常。这种编程实现的
connect
超时比起修改系统参数的那种方法的有点就在于它只用于你的程序之中而不影响系统。
connect
超时
,socket connect,socket
超时
,socket
连接超时设置
,connect,mysql connect,connect by,connect by prior,connect player,media connect
关于
c/s socket
中超时问题的总结
[size=18:ac54d21053]
在客户端与服务器端通过
socket
连接时,有两个问题必须考虑
1
、
connect
连接时可能会发生连接不上的情况,需要实现超时退出程序。
2
、连接后在接收数据的过程中,可能发生网络中断,不能接受数据的情况,需要退出程序。
这两个问题应该很常见,希望高手给大家详细地讲解一下,谢谢。
[/size:ac54d21053]
|
|
【
发表回复
】【
查看论坛原帖
】【
添加到收藏夹
】【
关闭
】
|
gadfly
回复于:
2003-08-11 14:40:32
|
这两个都可以用非阻塞
socket
,
select
控制超时
|
yuanyawei
回复于:
2003-08-12 09:08:00
|
我觉得第一种情况用
select
可以很好解决。
但第二种情况在遇到客户端直接拔网线的情况时,
server
端的情况较难判断,要看内核的参数,
linux
下较好处理,
BSD
也没问题,
HP
和
AIX
也能处理,但
SCO
下就不好办了(参数老调不好)。
|
minsky
回复于:
2003-08-12 10:52:27
|
1.connect
超时
: 1)setsockopt();//
将
socket
置为非阻塞模式
; 2)connect(); 3)
判断
connect()
的返回值
,
一般情况会返回
-1,
这时你还必须判断错误码如果是
EINPROGRESS,
那说明
connect
还在继续
;
如果错误码不是前者那么就是有问题了
,
不必往下执行
,
必须关掉
socket;
待下次重联
; 4)select();
设置好函数中的超时时间
,
将
select()
中的
read
和
write
项置上
,
在超时时间内
,
如果
select
返回
1,
即描述字变为了可写
,
那么连接成功
;
如果返回
2,
即描述字变为即可读又可写
,
那么出错
;
如果返回
0,
那么超时
; ============================================ 2.
网络中断
:
如果你的程序是客户端
.
用
select
检查描述符的状态
,
如果可读就
recv(),
根据
recv()
的返回值来判断网络情况
;
|
calfen
回复于:
2003-12-18 15:18:55
|
unp
上明确说
setsockopt
只能用在读写时候不能用在
connect
上啊
...
|
grouploo
回复于:
2004-06-25 23:06:35
|
/********************************************/ /****
作者:
:
夕君
**/ /****
时间:
2004.04.04 **/ /****
北京金万维科技
http://www.gnway.com **/ /*******************************************/ /*
此函数实现判断
m_server
的
m_port
端口是否可以连上,超时限制为
nTimeOut
秒
*/ BOOL ConnectTest(char * m_server,int m_port) {
struct hostent* host = NULL; struct sockaddr_in saddr; unsigned int s = 0; BOOL ret; time_t start; int error; host = gethostbyname (m_server); if (host==NULL)return FALSE;
saddr.sin_family = AF_INET; saddr.sin_port = htons(m_port); saddr.sin_addr = *((struct in_addr*)host->h_addr);
if( (s=socket(AF_INET, SOCK_STREAM, 0))<0){ return FALSE; }
fcntl(s,F_SETFL, O_NONBLOCK);
if(connect(s,(struct sockaddr*)&saddr, sizeof(saddr)) == -1) { if (errno == EINPROGRESS){// it is in the connect process struct timeval tv; fd_set writefds; tv.tv_sec = m_nTimeOut; tv.tv_usec = 0; FD_ZERO(&writefds); FD_SET(s, &writefds); if(select(s+1,NULL,&writefds,NULL,&tv)>0){ int len=sizeof(int); //
下面的一句一定要,主要针对防火墙
getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len); if(error==0) ret=TRUE; else ret=FALSE; }else ret=FALSE;//timeout or error happen }else ret=FALSE; } else ret=TRUE;
close(s); return ret;
}
|
|
setsockopt函数解析(转) - [IT]{#timeline}
Tag: IT
int setsockopt (
SOCKET
s
,
int
level
,
int
optname
,
const char FAR *
optval
,
int
optlen
);
The Windows Sockets
setsockopt function sets a socket option.
中文解释好像是:设置套接字的选项。
先看如下代码:
setsockopt(SockRaw,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int))
这里是设置SockRaw这个套接字的ip选项中的IP_HDRINCL
参考以下资料:
***************************************************************************************************
Linux
网络编程--8.
套接字选项
有时候我们要控制套接字的行为(如修改缓冲区的大小),这个时候我们就要控制套接字的选项了.
8.1 getsockopt和
setsockopt
int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen)
int
setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen)
level指定控制套接字的层次.可以取三种值:
1)SOL_SOCKET:通用套接字选项.
2)IPPROTO_IP:IP选项.
3)IPPROTO_TCP:TCP选项.
optname指定控制的方式(选项的名称),我们下面详细解释
optval获得或者是设置套接字选项.根据选项名称的数据类型进行转换
选项名称 说明 数据类型
========================================================================
SOL_SOCKET
------------------------------------------------------------------------
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSERADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
==========================================================================
IPPROTO_IP
--------------------------------------------------------------------------
IP_HDRINCL 在数据包中包含IP首部 int
IP_OPTINOS IP首部选项 int
IP_TOS 服务类型
IP_TTL 生存时间 int
==========================================================================
IPPRO_TCP
--------------------------------------------------------------------------
TCP_MAXSEG TCP最大数据段的大小 int
TCP_NODELAY 不使用Nagle算法 int
=========================================================================
关于这些选项的详细情况请查看
Linux Programmer"s Manual
8.2 ioctl
ioctl可以控制所有的文件描述符的情况,这里介绍一下控制套接字的选项.
int ioctl(int fd,int req,...)
==========================================================================
ioctl的控制选项
--------------------------------------------------------------------------
SIOCATMARK 是否到达带外标记 int
FIOASYNC 异步输入/输出标志 int
FIONREAD 缓冲区可读的字节数 int
==========================================================================
详细的选项请用 man ioctl_list 查看.
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=588497
1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));
2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历
TIME_WAIT的过程:
BOOL bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));
3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限:
int nNetTimeout=1000;//1秒
//发送时限
setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
//接收时限
setsockopt(socket,SOL_S0CKET,
SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));
4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节
(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据
和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发:
// 接收缓冲区
int nRecvBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
5. 如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响
程序的性能:
int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));
6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):
int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));
7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:
BOOL bBroadcast=TRUE;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));
8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可
以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的
作用,在阻塞的函数调用中作用不大)
BOOL bConditionalAccept=TRUE;
setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));
9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们
一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体
应用的要求(即让没发完的数据发送出去后在关闭socket)?
struct linger {
u_short l_onoff;
u_short l_linger;
};
linger m_sLinger;
m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)
// 如果m_sLinger.l_onoff=0;则功能和2.)作用相同;
m_sLinger.l_linger=5;//(容许逗留的时间为5秒)
setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));