この質問はWhy is nonblocking socket writable before connect() or accept()?に続きます。受理サーバでTCPハンドシェイクの時間を長くすることはできますか?
次のコードは、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
at the noted questionコメントEJPは、限りのため、このような状況では、select()
ブロックと指摘、 "ターゲットTCPハンドシェイクです。"
この「TCPハンドシェイク」プロセスを長くする方法はありますか?私。 select()
ブロックがソケットs
を待つブロックが書き込み可能になるまでの時間を長くしたいと思います。これはできますか?受け入れる前にsleep()
を追加することでこれを行おうとしましたが、うまくいかないことがあります。