Windows下的非阻塞socket编程初探

下午检查灰度部署的结果,突然系统有点延迟,原来是因为网络策略的变更,导致防火墙把socket的一个主连接挡住了。
虽然会自动切换到备连接,但是系统每隔5分钟会再去检查主连接是否恢复。

这个时候,由于防火墙挡掉的socket链接就会阻塞很长一段时间才会返回错误,需要修改。

因为在Linux下面的情况比较简单,提供一个下面的语句做参考。

setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); // 通过SO_SNDTIMEO来控制connect的超时时间

据表哥介绍,这个设置原本是用来设置发送超时的,不过Linux下面貌似会很”巧合”的把连接超时也搞定了。
Windows下面当然没这么简单,下面整理网上的一些资料,对windows下面的socket非阻塞链接做探讨。

写在前面:下面的程序在windows下编译需要链接ws2_32.lib/DLL

1. 客户端测试
#include 
#include 
#include 
#include 
#include 

#define  IP_ADDRESS "172.30.7.83"

int PORT = 4000;

WSADATA wsd;
SOCKET cClient;
int ret;
struct sockaddr_in server;
hostent *host=NULL;

int main()
{
    if( WSAStartup(MAKEWORD(2,0),&wsd) )
	{
		return 0;
	}

    cClient = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    if( cClient == INVALID_SOCKET)
	{
		return 0;
	}

    //set Recv and Send time out

    int TimeOut=6000; //设置发送超时6秒

    if(::setsockopt(cClient, SOL_SOCKET, SO_SNDTIMEO, (char *)&TimeOut, sizeof(TimeOut)) == SOCKET_ERROR)
	{
        return 0;
    }
    
	TimeOut=6000;//设置接收超时6秒
    if(::setsockopt(cClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut, sizeof(TimeOut)) == SOCKET_ERROR)
	{
        return 0;
    }

    //设置非阻塞方式连接
    unsigned long ul = 1;
    ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul);
    if (ret == SOCKET_ERROR) return 0;

    //连接
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = inet_addr(IP_ADDRESS);
    if(server.sin_addr.s_addr == INADDR_NONE)
	{
		return 0;
	}

    connect(cClient,(const struct sockaddr *)&server,sizeof(server));

    //select 模型,即设置超时
    struct timeval timeout ;
    fd_set r;

    FD_ZERO(&r);
    FD_SET(cClient, &r);
    timeout.tv_sec = 3; //连接超时3秒
    timeout.tv_usec =0;
	printf("start to connect!\n");
    ret = select(0, 0, &r, 0, &timeout);
    if ( ret <= 0 )
    {
        ::closesocket(cClient);
        return 0;
    }

	char * SendBuffer = "亚瑟王,你找到圣杯了!\n";

	ret = send(cClient, SendBuffer, (int)strlen(SendBuffer), 0);
    
 ////一般非锁定模式套接比较难控制,可以根据实际情况考虑 再设回阻塞模式
 //   unsigned long ul1= 0 ;
 //   ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul1);
 //   if(ret==SOCKET_ERROR){
 //       ::closesocket (cClient);
 //       return 0;
 //   }

	printf("%d\n", ret);

	system("pause");
	
	return 0;
}

2. 另外赠送一个Server端:
//Server.cpp
#include 
#include 

using namespace std;

#define  PORT 4000
#define  IP_ADDRESS "172.30.7.83"

DWORD WINAPI ClientThread(LPVOID lpParameter)
{
    SOCKET CientSocket = (SOCKET)lpParameter;
    int Ret = 0;
    char RecvBuffer[MAX_PATH];

    while ( true )
    {
        memset(RecvBuffer, 0x00, sizeof(RecvBuffer));
        Ret = recv(CientSocket, RecvBuffer, MAX_PATH, 0);
        if ( Ret == 0 || Ret == SOCKET_ERROR ) 
        {
            cout<<"客户端退出!"<

最后,VIM删除列的方法,V模式,ctrl+V,移动,x or d

此条目发表在C++, Windows, 系统, 编程分类目录,贴了, , 标签。将固定链接加入收藏夹。

发表评论

邮箱地址不会被公开。 必填项已用*标注