• 欢迎浏览“String me = Creater\忠实的资深Linux玩家;”,请文明浏览,理性发言,有侵犯你的权益请邮件我(creater@vip.qq.com).
  • 把任何的失败都当作一次尝试,不要自卑;把所有的成功都想成是一种幸运,不要自傲。
  •    5年前 (2013-07-02)  Socket library |   2 条评论  7 
    文章评分 0 次,平均分 0.0

    首先关注这个Open函数,这个函数的作用就是让TcpSocket作为客户端去主动连接服务器,并返回connect状态。

    bool TcpSocket::Open(SocketAddress& ad,SocketAddress& bind_ad,bool skip_socks)

    该函数的大致步骤为:

    //step1:检查ad是否有效
    //step2:检查handler是否还可以容纳Socket
    //step3:查看连接池是否可用,可用则搜索,如果找到可用则返回,否则
    //step4:创建一个新的套接字,并根据bind_ad决定是否绑定
    //step5:connect到ad指定的远程主机

    bool TcpSocket::Open(SocketAddress& ad,SocketAddress& bind_ad,bool skip_socks)
    {
    	if (!ad.IsValid())
    	{
    		Handler().LogError(this, "Open", 0, "Invalid SocketAddress", LOG_LEVEL_FATAL);
    		SetCloseAndDelete();
    		return false;
    	}
    	if (Handler().GetCount() >= Handler().MaxCount())
    	{
    		Handler().LogError(this, "Open", 0, "no space left for more sockets", LOG_LEVEL_FATAL);
    		SetCloseAndDelete();
    		return false;
    	}
    	
    	SetConnecting(false);
    	
    	// check for pooling
    	//在连接池中找可用的Socket
    #ifdef ENABLE_POOL
    	if (Handler().PoolEnabled())
    	{
    		ISocketHandler::PoolSocket *pools = Handler().FindConnection(SOCK_STREAM, "tcp", ad);
    		if (pools)
    		{
    			//将该连接信息拷贝过来
    			CopyConnection( pools );
    			//删除临时指针变量
    			delete pools;
    			//作为客户端
    			SetIsClient();
    			//ISocketHandler管理器必须调用OnConnect
    			SetCallOnConnect(); // ISocketHandler must call OnConnect
    			Handler().LogError(this, "SetCallOnConnect", 0, "Found pooled connection", LOG_LEVEL_INFO);
    			//成功
    			return true;
    		}
    	}
    #endif
    	// if not, create new connection
    	//创建一个新的连接
    	SOCKET s = CreateSocket(ad.GetFamily(), SOCK_STREAM, "tcp");
    	if (s == INVALID_SOCKET)
    	{
    		return false;
    	}
    	// socket must be nonblocking for async connect
    	//因为需要异步连接,所以这里设置成非阻塞
    	if (!SetNonblocking(true, s))
    	{
    		SetCloseAndDelete();
    		closesocket(s);
    		return false;
    	}
    #ifdef ENABLE_POOL
    	SetIsClient(); // client because we connect
    #endif
    	//设置需要连接的远程主机的SocketAddress
    	SetClientRemoteAddress(ad);
    	int n = 0;
    	//如果 需要绑定到本地端口和地址(一般客户端不需要绑定)
    	if (bind_ad.GetPort() != 0)
    	{//bind_ad为SocketAddress,所以存在这两个隐式转换
    		bind(s, bind_ad, bind_ad);
    	}
    	{
    	//主动连接远程服务器
    		n = connect(s, ad, ad);
    	//设置连接的远程主机SocketAddr信息
    		SetRemoteAddress(ad);
    	}
    	if (n == -1)
    	{
    		// check error code that means a connect is in progress
    		if (Errno == EINPROGRESS)
    		{
    			Attach(s);
    			SetConnecting( true ); // this flag will control fd_set's
    		}
    		else
    #ifdef ENABLE_RECONNECT
    		if (Reconnect())
    		{
    			Handler().LogError(this, "connect: failed, reconnect pending", Errno, StrError(Errno), LOG_LEVEL_INFO);
    			Attach(s);
    			SetConnecting( true ); // this flag will control fd_set's
    		}
    		else
    #endif
    		{
    			Handler().LogError(this, "connect: failed", Errno, StrError(Errno), LOG_LEVEL_FATAL);
    			SetCloseAndDelete();
    			closesocket(s);
    			return false;
    		}
    	}
    	else
    	{
    	//将描述符关联
    		Attach(s);
    		SetCallOnConnect(); // ISocketHandler must call OnConnect
    	}
    
    	// 'true' means connected or connecting(not yet connected)
    	// 'false' means something failed
    	return true; //!Connecting();
    }

    该函数最先就是对传入参数的正确性检测。
    由于该库是基于事件的,所以Open创建的套接字需要交给Handler来管理,这也就是需要检查Handler的容量是否超限。
    SetConnecting(false)指明现在还没有进行connect。
    如果启用了连接池,则在连接池中查找可用的匹配连接,并提取出来直接使用,而不是从头开始创建套机子再连接。

    #ifdef ENABLE_POOL
    	if (Handler().PoolEnabled())
    	{
    		ISocketHandler::PoolSocket *pools = Handler().FindConnection(SOCK_STREAM, "tcp", ad);
    		if (pools)
    		{
    			//将该连接信息拷贝过来
    			CopyConnection( pools );
    			//删除临时指针变量
    			delete pools;
    			//作为客户端
    			SetIsClient();
    			//ISocketHandler管理器必须调用OnConnect
    			SetCallOnConnect(); // ISocketHandler must call OnConnect
    			Handler().LogError(this, "SetCallOnConnect", 0, "Found pooled connection", LOG_LEVEL_INFO);
    			//成功
    			return true;
    		}
    	}
    #endif

    使用SetIsClient()来指明该连接为客户端。
    如果连接池没启用或者没有合适的连接则创建新的套接字。

    接着开始非常重要的设置。

    if (!SetNonblocking(true, s))

    设置套接字描述符为非阻塞,避免connect的阻塞,注意就是非阻塞的connect返回值为-1.
    更详细的描述为:
    connect立即返回一个EINPROGRESS错误,但是3次握手继续在进行,这个时候使用select来检测成功或者失败。
    连接如果成功,则select变可写;
    连接如果失败,则select变可读可写。
    那么为什么还要对n!=-1进行分支呢?这是因为如果服务器在同一个主机上,那么一般connect通常立刻建立。

    另外,如果连接失败if (Errno != EINPROGRESS),那么则可能启用Reconnect机制。

    #ifdef ENABLE_RECONNECT
    		if (Reconnect())
    		{
    			Handler().LogError(this, "connect: failed, reconnect pending", Errno, StrError(Errno), LOG_LEVEL_INFO);
    			Attach(s);
    			SetConnecting( true ); // this flag will control fd_set's
    		}
    		else
    #endif

    重复connect中会

    SetConnecting( true ); // this flag will control fd_set's

    设置connecting标志后,将会影响fd_set,进而影响select.

    还有一个Open函数,使用string作为主机地址参数,这个时候用(!Utility::u2ip(host,l))转换成ip地址就行了。

     

    除特别注明外,本站所有文章均为String me = "Creater\忠实的资深Linux玩家";原创,转载请注明出处来自http://unix8.net/home.php/1627.html

    关于

    发表评论

    暂无评论

    切换注册

    登录

    忘记密码 ?

    切换登录

    注册

    扫一扫二维码分享