LINUX C 进行TCP网络连接,怎样设置连接超时时间

LINUX C 进行TCP网络连接,怎样设置连接超时时间,第1张

如果你确定,真的不需要等这么久,或者用户希望可以随时中上连接过程,那么一般是用 非阻塞模式来做的. 看看我的这段连接代码(节选),可以作为TCP连接的典范:

bool CRemoteLink::Connect()

{

OnDisconnected() // 如果已经连接,则断开

if(!m_bUseProxy)

{

m_iConnStatus = SS_CONNECTING // 正在连接状态

GNTRACE ("开始连接到远程服务器[%s][%ld]...\n", m_strip.c_str(), m_port)

// 建立套接字, 准备连接到服务器

m_socket = ::socket(AF_INET, SOCK_STREAM, 0)

if (socket <0) {

if(m_pCallBack)

m_pCallBack->OnSocketError(SE_CREATE, MSG_SE_CREATE)

return false

}

// 设为异步 *** 作方式

unsigned long on = 1

if (::ioctlsocket(m_socket, FIONBIO, &on) <0) {

::closesocket(m_socket)

if(m_pCallBack)

m_pCallBack->OnSocketError(SE_CREATE, MSG_SE_CREATE)

return false

}

sockaddr_in addr

memset(&addr, 0, sizeof(addr))

addr.sin_family = AF_INET

addr.sin_addr.s_addr = inet_addr(m_strip.c_str())

addr.sin_port = htons(m_port)

int rt

rt = ::connect(m_socket, (sockaddr *) &addr, sizeof(addr))

if (rt == 0) {

OnConnected()

return true

}

// ==================================================================

timeval to

// 首先建立连接

fd_set wfds

fd_set efds

FD_ZERO(&wfds)

FD_ZERO(&efds)

// test shutdown event each 100ms.

to.tv_sec = 0

// CONNECT_TIMEOUT

to.tv_usec = 100000

int it = 0

while(!m_meShutdown.Wait(0) &&!m_meConnStop.Wait(0))

{

FD_SET(m_socket, &wfds)

FD_SET(m_socket, &efds)

int n = select(m_socket + 1, NULL, &wfds, &efds, &to)

if (n >0) {

if(FD_ISSET(m_socket, &wfds))

{

OnConnected()

return true

}

else

{

//int err = ::WSAGetLastError()

//const char* msg = GetLastErrorMessage(err)

GNTRACE ("CRemoteLink::Connect : connection attempt failed!\n")

if(m_pCallBack)

m_pCallBack->OnSocketError(SE_CONN, MSG_SE_CONN)

break

}

} else if (n <0) { // Select Error

int err = ::WSAGetLastError()

const char* msg = GetLastErrorMessage(err)

GNTRACE ("CRemoteLink::Connect : Select Error.[%d] - %s\n", err, msg)

if(m_pCallBack)

m_pCallBack->OnSocketError(err, msg)

break

}

else

{

it += 100

if(it >30000) // 连接超时 -- (30S)

{

GNTRACE ("CRemoteLink::Connect : Time out.\n")

if(m_pCallBack)

m_pCallBack->OnSocketError(SE_TIMEOUT, MSG_SE_TIMEOUT)

break

}

}

}

if(m_meConnStop.Wait(0))

{

GNTRACE("连接过程进行时被取消。\n")

}

}

else

{

// 通过代理服务器连接

通过设置linux参数 net.ipv4.tcp_fin_timeout = 30 ,可以调整

如发现系统存在大量TIME_WAIT状态的连接,通过调整内核参数解决:

编辑文件/etc/sysctl.conf,加入以下内容

tcp 通过序列号seq记录已经发送数据刻度,通过ack记录已经接收的数据量。seq记录的是发送的数据,ack记录的是接收的数据量。单位是字节(8bit)

tcp在每次发包时都会计算往复时间及其偏差。将这个往返时间和偏差相加,重发超时时间就是比这个总和要稍大一点的值。

由于最初的数据包还不知道往返时间,所以其重发超时一般设置为6s左右。

在建立tcp连接时,三次握手的时候会计算mss(最大消息长度),建立连接的双方会把自己的接口能适应的mss值放到tcp首部里面发送给对方,最后取较小的那个mss。

tcp窗口大小指的是无需等待确认应答而可以继续发送数据的最大值,窗口大小为4个端。即在收到确认应答之前可以发送的数据的段数。

接收端没有按序列顺序收到数据端时,会不停的发送确认应答,并将当前收到的顺序出问题的数据放到缓冲区。发送端连续三次收到相同序列号的数据段时,会重新发送该段的数据。接收端在接收到遗失的数据的时候会将数据与缓冲区的数据组合,重新按顺序确定ack的序列号,继续接收数据。

tcp窗口的大小是由接收端的处理能力决定的,接收端会在ack的tcp首部中将能处理的窗口大小传给发送端。

拥塞窗口是限制每次发送的数据的大小,初始值是1mss,也就是慢启动。随着正常的收发的进行,拥塞窗口的值会不断的增加。但是不会超过接收端处理窗口的大小。

一开始拥塞窗口每次都会翻倍的增长,在超过慢启动阈值后增长速度会减慢。

增长速率=一个数据段的大小 / 拥塞窗口的大小 *一个数据段的大小

超时重发时,拥塞窗口会变为1mss, 慢启动阈值为原有窗口的一半

重复确认应答时,慢启动阈值为原有窗口的一半,拥塞窗口会变为慢启动阈值+3数据端,

1、已发送的数据收到了ack回执

2、可以发送mss大小的数据时

只有以上两个数据都满足时才发送数据。会有延迟,对延迟敏感的需求可以关。

1、收到2*最大端长度的数据

2、最大延迟0.5s发送确认应答

将tcp的确认应答和回执数据通过一个包发送。

接收数据之后等待应用处理生成返回数据以后在发送回复时同时发送回执。

需要开启延迟确认应答。

可以作为TCP连接的典范:

bool CRemoteLink::Connect()

{

OnDisconnected()   // 如果已经连接,则断开

if(!m_bUseProxy)

{

m_iConnStatus = SS_CONNECTING  // 正在连接状态

GNTRACE ("开始连接到远程服务器[%s][%ld]...\n", m_strip.c_str(), m_port)

// 建立套接字, 准备连接到服务器

m_socket = ::socket(AF_INET, SOCK_STREAM, 0)

if (socket < 0) {

if(m_pCallBack)

m_pCallBack->OnSocketError(SE_CREATE, MSG_SE_CREATE)

return false

}

// 设为异步 *** 作方式

unsigned long on = 1

if (::ioctlsocket(m_socket, FIONBIO, &on) < 0) {

::closesocket(m_socket)

if(m_pCallBack)

m_pCallBack->OnSocketError(SE_CREATE, MSG_SE_CREATE)

return false

}

sockaddr_in addr

memset(&addr, 0, sizeof(addr))

addr.sin_family = AF_INET

addr.sin_addr.s_addr = inet_addr(m_strip.c_str())

addr.sin_port = htons(m_port)

int rt

rt = ::connect(m_socket, (sockaddr *) &addr, sizeof(addr))

if (rt == 0) {

OnConnected()

return true

}

// ==================================================================

timeval to

// 首先建立连接

fd_set wfds

fd_set efds

FD_ZERO(&wfds)

FD_ZERO(&efds)

// test shutdown event each 100ms.

to.tv_sec = 0 

// CONNECT_TIMEOUT

to.tv_usec = 100000

int it = 0

while(!m_meShutdown.Wait(0) && !m_meConnStop.Wait(0))

{

FD_SET(m_socket, &wfds)

FD_SET(m_socket, &efds)

int n = select(m_socket + 1, NULL, &wfds, &efds, &to)

if (n > 0) {

if(FD_ISSET(m_socket, &wfds))

{

OnConnected()

return true

}

else

{

//int err = ::WSAGetLastError()

//const char*  msg = GetLastErrorMessage(err)

GNTRACE ("CRemoteLink::Connect : connection attempt failed!\n")

if(m_pCallBack)

m_pCallBack->OnSocketError(SE_CONN, MSG_SE_CONN)

break

}

} else if (n < 0) {  // Select Error

int err = ::WSAGetLastError()

const char*  msg = GetLastErrorMessage(err)

GNTRACE ("CRemoteLink::Connect : Select Error.[%d] - %s\n", err, msg)

if(m_pCallBack)

m_pCallBack->OnSocketError(err, msg)

break

}

else

{

it += 100

if(it > 30000)      // 连接超时 -- (30S)

{

GNTRACE ("CRemoteLink::Connect : Time out.\n")

if(m_pCallBack)

m_pCallBack->OnSocketError(SE_TIMEOUT, MSG_SE_TIMEOUT)

break

}

}

}

if(m_meConnStop.Wait(0))

{

GNTRACE("连接过程进行时被取消。\n")

}

}

else

{

// 通过代理服务器连接


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8641259.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-19
下一篇 2023-04-19

发表评论

登录后才能评论

评论列表(0条)

保存