我写了一个支持SSL的嵌入式web服务器,可以在linux上运行。它运作得很好。除了一件事。
在SSL_accept()
上,它返回-1(对于OpenSSL 1.1.0)或0(对于OpenSSL 1.0.2),SSL_get_error()
返回SSL_ERROR_SYSCALL
。
在我孤立的测试环境中,即使我发送了300万个请求,我也没有任何问题。但是当在AWS(相同的CentOS 7)或连接到互联网的另一台VM上运行时,我经常看到这些问题。当发生这种情况时,应用程序也会挂起几秒钟。
这是我的听力线程的相关代码部分:
int myPort = 443;
struct sockaddr_in serverSockAddr;
memset(&serverSockAddr,0,sizeof(serverSockAddr));
serverSockAddr.sin_family = AF_INET;
serverSockAddr.sin_port = htons(myPort);
serverSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
int tcpListenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (tcpListenSocket != INVALID_SOCKET)
{
int val = 1;
setsockopt(tcpListenSocket,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
if (bind(tcpListenSocket,(struct sockaddr *)&serverSockAddr,sizeof(serverSockAddr)) == 0)
{
if (listen(tcpListenSocket,128) != 0) // Allow 128 backlog connections
{
printf("Failed to listen on port: %d\n",myPort);
tcpListenSocket = INVALID_SOCKET;
}
}
else
{
printf("Not able to bind port: %d (%d-%s)\n",myPort,errno,strerror(errno));
tcpListenSocket = INVALID_SOCKET;
}
}
if (tcpListenSocket != INVALID_SOCKET)
{
printf("TCP Listen Thread: Socket for port %d bound\n",myPort);
printf("TCP Listen Thread: Waiting for incoming data\n");
// Hostname or IP Adress of client who sent the request. Allocate here to avoid reallocation on stack on each accept.
char clientHostName[NI_MAXHOST];
clientHostName[0] = '\0';
while (_tcpListenShutdown == 0)
{
// wait for incoming data
int pendingConnection = 0;
do
{
struct timeval tv;
fd_set descriptor;
tv.tv_sec = 0;
tv.tv_usec = 75000; // 75ms
FD_ZERO(&descriptor);
FD_SET(tcpListenSocket,&descriptor);
pendingConnection = select(tcpListenSocket+1,&descriptor,NULL,NULL,&tv);
}while((_tcpListenShutdown == 0) && (pendingConnection == 0));
if (pendingConnection > 0)
{
struct sockaddr_in clientSockAddr;
ovsocklen_t clientAddrLen = sizeof(clientSockAddr);
memset(&clientSockAddr,0,sizeof(clientSockAddr));
int clientSocket = accept(tcpListenSocket,(struct sockaddr *)&clientSockAddr,&clientAddrLen);
if (clientSocket != INVALID_SOCKET)
{
// find hostname who originates connection
getnameinfo((struct sockaddr *)&clientSockAddr,sizeof(clientSockAddr),clientHostName,sizeof(clientHostName),NULL,0,0);
// If resolving failed use IP address
if (clientHostName[0] == '\0')
strlcpy(clientHostName,inet_ntoa(clientSockAddr.sin_addr),sizeof(clientHostName));
int clientPort = ntohs(clientSockAddr.sin_port);
printf("TCP Listen Thread: Received connection from host '%s:%d' on clientsocket:'%d'\n",clientHostName,clientPort,clientSocket);
SSL *sslSession = NULL;
// create SSL Session
int error = 1;
sslSession = SSL_new(_sslContext);
if (sslSession)
{
// hook to our own client socket
SSL_set_fd(sslSession,clientSocket);
printf("TCP Listen Thread('%s:%d'): Accepting on SSL session\n",clientHostName,clientPort);
// SSL_Accept() might need to be repeated if socket is non blocking
while (error)
{
int acceptResult = SSL_accept(sslSession);
if (acceptResult == 1)
error = 0;
else
{
int sslError = SSL_get_error(sslSession,acceptResult);
if ((sslError == SSL_ERROR_WANT_READ) || (sslError == SSL_ERROR_WANT_WRITE))
continue;
else
{
printf("TCP Listen Thread('%s:%d'): Could not accept SSL session(%d/%d) (%d-%s)",clientHostName,clientPort,acceptResult,sslError,errno,strerror(errno));
break;
}
}
}
}
else
printf("TCP Listen Thread('%s:%d'): Could not generate SSL session\n",clientHostName,clientPort);
// create new thread for handling new connection
if (error == 0)
{
/// SSL Handshake complete => Handle request
}
else
{
printf("TCP Listen Thread('%s:%d'): Dropping connection due to errors.\n",clientHostName,clientPort);
if (sslSession)
SSL_free(sslSession);
shutdown(clientSocket,SHUT_RDWR);
}
}
}
else
{
if (_tcpListenShutdown == 0)
printf("TCP Listen Thread: Lost connection => exiting\n");
}
}
示例错误消息:
TCP听 螺纹( 'ec2-54-248-220-38.ap-northeast-1.compute.amazonaws.com:43507'): 无法接受SSL会话(-1/5)(0成功)
-1是SSL_accept()
的结果
5是SSL_get_error()
的结果,表示SSL_ERROR_SYSCALL
其余的是errno及其字符串表示。
错误消息通常出现在3到15条相同类型但来自不同主机的消息中。看起来这个线程会阻塞一段时间,当它继续时,出现的消息代表了无法及时完成的连接尝试。
有任何想法可以隔离并修复此问题吗? 谢谢你的帮助!