我可以在接受服务器上延长TCP握手持续时间吗?
Can I lengthen TCP handshake duration on accepting server?
这个问题从为什么在连接()或Accept()之前是非块套接字写的?。
以下代码催生了聆听TCP连接的线程。主线程连接到服务器正在收听的地址。
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <pthread.h>
#include <semaphore.h>
class SafeSocket
{
public:
/** Ctor.
* Creates a nonblocking socket at the specified IP in the AF_INET family and
* at a dynamic port.
*/
SafeSocket( const std::string& ip )
{
in_addr_t host_ip = inet_network( ip.c_str() );
if ( ( socket_ = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
{
std::cout << "socket() failed: " << errno << " " << strerror( errno )
<< std::endl;
socket_ = -1;
}
sockaddr_in si;
memset( &si, 0, sizeof( si ) );
si.sin_family = AF_INET;
si.sin_port = 0; // Dynamic port
si.sin_addr.s_addr = htonl( host_ip );
if ( bind( socket_, (sockaddr*)&si, sizeof si ) )
{
std::cout << "bind() failed: " << errno << " " << strerror( errno )
<< std::endl;
close( socket_ );
socket_ = -1;
}
// Make the socket do nonblocking connect().
int flags = fcntl( socket_, F_GETFL, 0 );
fcntl( socket_, F_SETFL, flags | O_NONBLOCK );
}
~SafeSocket()
{
if ( socket_ >= 0 )
{
shutdown( socket_, SHUT_RDWR );
close( socket_ );
}
}
operator int() const
{
return socket_;
}
private:
int socket_;
};
int connectToClient( const SafeSocket& sock, const std::string& clientIp,
const int clientPort )
{
struct sockaddr_in clientAddr;
memset( &clientAddr, 0, sizeof clientAddr );
inet_pton( AF_INET, clientIp.c_str(), &clientAddr.sin_addr );
clientAddr.sin_family = AF_INET;
clientAddr.sin_port = htons( clientPort );
return connect( sock, (sockaddr*)&clientAddr, sizeof clientAddr );
}
std::string serverIp( "127.0.0.200" );
int serverPort = 9099; // Random, hopefully unused.
sem_t listenSem;
/** Entry point to pthread.
*/
void* acceptConnection( void* arg )
{
int listenSock = socket( PF_INET, SOCK_STREAM, 0 );
if ( listenSock < 0 )
{
std::cout << "socket() failed: " << errno << " " << strerror( errno )
<< std::endl;
return NULL;
}
sockaddr_in si;
si.sin_family = AF_INET;
inet_aton( serverIp.c_str(), &si.sin_addr );
si.sin_port = htons( serverPort );
int optval = 1;
setsockopt( listenSock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
int result = bind( listenSock, (sockaddr*)&si, sizeof si );
if ( result )
{
std::cout << "bind() failed: " << errno << " " << strerror( errno )
<< std::endl;
close( listenSock );
return NULL;
}
std::cout << "listening on socket " << listenSock << std::endl;
if ( listen( listenSock, 3 ) )
{
std::cout << "listen() failed: " << errno << " " << strerror( errno )
<< std::endl;
close( listenSock );
return NULL;
}
sem_post( &listenSem );
fd_set readfds;
FD_ZERO( &readfds );
FD_SET( listenSock, &readfds );
struct timeval ts = { 5, 0 };
if ( -1 != select( listenSock + 1, &readfds, NULL, NULL, &ts ) )
{
if ( FD_ISSET( listenSock, &readfds ) )
{
sockaddr_in peerSi;
socklen_t peerAddrLen = sizeof peerSi;
memset( &peerSi, 0, peerAddrLen );
sleep( 3 );
int acceptSock = accept( listenSock, (sockaddr*)&peerSi, &peerAddrLen );
if ( acceptSock > 0 )
{
std::cout << "accepted connection on socket " << acceptSock
<< std::endl;
close( acceptSock );
}
}
else
{
std::cout << "did not receive a connection to accept." << std::endl;
}
}
close( listenSock );
return NULL;
}
int main( int argc, char* argv[] )
{
sem_init( &listenSem, 0, 0 );
SafeSocket s( "127.0.0.100" );
std::cout << "Created socket " << s << std::endl;
pthread_t tid;
pthread_create( &tid, NULL, acceptConnection, NULL );
timespec listenTimeout;
clock_gettime( CLOCK_REALTIME, &listenTimeout );
listenTimeout.tv_sec += 5;
sem_timedwait( &listenSem, &listenTimeout );
fd_set readFds;
fd_set writeFds;
FD_ZERO( &readFds );
FD_ZERO( &writeFds );
FD_SET( s, &writeFds );
timeval timeout = { 5, 0 };
int result = connectToClient( s, serverIp, serverPort );
std::cout << "connectToClient() returned " << result << " "
<< errno << " " << strerror( errno ) << std::endl;
if ( -1 == select( s+1, &readFds, &writeFds, NULL, &timeout ) )
{
std::cout << "select() failed: " << errno << " " << strerror( errno )
<< std::endl;
}
if ( FD_ISSET( s, &writeFds ) )
{
std::cout << s << " is writable!" << std::endl;
int result = -1;
socklen_t result_len = sizeof result;
getsockopt( s, SOL_SOCKET, SO_ERROR, &result, &result_len );
std::cout << "result: " << result << " " << strerror( result ) << std::endl;
}
pthread_join( tid, NULL );
return 0;
}
输出:
>g++ --version
g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
>g++ -g main.cpp
>
>./a.out
Created socket 3 // Immediate
listening on socket 4 // Immediate
connectToClient() returned -1 115 Operation now in progress // Immediate
3 is writable! // Immediate
result: 0 Success // Immediate
accepted connection on socket 5 // Delayed
ejp在著名的问题上发表了评论,他指出,在这种情况下,只要"目标TCP握手",select()
就会封锁。
那么,是否有一种加长" TCP握手"过程的方法?IE。我想延长select()
阻止等待套接字s
的时间。可以做到吗?您可能会观察到我试图通过在接受之前添加sleep()
来做到这一点,但这不起作用。
握手遵循以下阶段:
-
阶段1:连接在客户端上运行。这是从调用连接到至少客户从服务器中获取SYN ACK数据包的时运行的。我说至少是因为客户端内核可能需要一段时间来处理该数据包。我的理解是,直到第1阶段结束时,插座才会是可写的。
-
阶段2:插座在客户端上是可写的;连接完成了。服务器尚未从接受。这包括服务器等待从客户端接收ACK的时间,以及接收ACK和服务器应用程序调用
accept
的任何时间(另外等待该调用accept
进行处理) -
阶段3:双方完成。
我已经构成了以下阶段:我绘制的区别旨在使您可以轻松地讨论您的问题,不要遵循TCP状态机的任何方面。
没有有用的方法来延长阶段1,当然可以使阶段1包含阶段2。P>
第1阶段和第2阶段之间的时间差是服务器计算机内的网络延迟和处理。客户无法采取行动。
对于许多应用程序,一旦插座是可写的,就需要立即开始写作。如果不是,则普遍接受的解决方案是让您的协议让服务器向客户端发送初始问候。然后,等待插座是可读的,并在行动之前对待问候。
相关文章:
- 从持续时间构造std::chrono::system_clock::time_point
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 时间持续时间到时间字符串
- 指向(数据)成员的指针作为非类型模板参数,例如具有自动存储持续时间/无链接
- 为什么具有静态存储持续时间的同一内联变量在包含在 VS2017 编译的两个翻译单元中时会构造和销毁两次
- 将毫秒转换为给定格式的持续时间
- 具有静态存储持续时间的常量初始化变量的初始化顺序
- 划分和乘以STD :: Chrono ::持续时间
- 静态存储持续时间初始化
- C++计划持续时间内(字体)资源的分配
- FFMPEG:具有不同持续时间的多路复用流
- 在不同翻译单元中具有静态存储持续时间的依赖非局部常量浮点变量的常量初始化
- 使用System_Clock :: TO_TIME_T警告持续时间_t
- 访问和存储/解析性std :: Chrono ::持续时间:: milliseconds(cpprest)时使用什么类型
- 我可以让QT到概要文件插槽执行持续时间吗?
- 在STD :: Chrono ::剩余时间测量的持续时间
- 自定义 AVIOContext 的未定义 AVFormatContext 持续时间
- 使用计时比较C++的持续时间/秒数并不像预期的那样工作
- 为什么 std::future::wait_for 不等待正确的持续时间
- 我可以在接受服务器上延长TCP握手持续时间吗?