我创建了一个常规的C套接字。连接后,它会按预期返回EWOULDBLOCK/WSAEWOULDBLOCK
,因为我这样做了:
unsigned long int mode = 0;
ioctlsocket(ssl_info->sock, FIONBIO, &mode);
setsockopt(ssl_info->sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));
setsockopt(ssl_info->sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv));
将套接字置于非阻塞模式。之后我做:
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
return SSL_connect(ssl);
但是,它返回-1。
我在网上读到,这意味着我需要处理SSL_ERROR_WANT_READ
和SSL_ERROR_WANT_WRITE
。
所以我做了:
int res = -1;
while(res == -1)
{
res = SSL_connect(ssl);
switch (SSL_get_error(ssl, res))
{
case SSL_ERROR_WANT_CONNECT:
MessageBox(NULL, "Connect Error", "", 0);
break;
case SSL_ERROR_WANT_READ: //prints this every time..
MessageBox(NULL, "Read Error", "", 0);
break;
case SSL_ERROR_WANT_WRITE:
MessageBox(NULL, "Write Error", "", 0);
break;
}
SelectSocket(ssl);
}
std::cout<<"Connected!n";
其中SelectSocket
定义为:
bool SelectSocket(SSL* ssl)
{
if (blockmode)
{
fd_set readfds;
fd_set writefds;
FD_ZERO(&readfds);
FD_ZERO (&writefds);
FD_SET(ssl_info->sock, &readfds);
FD_SET(ssl_info->sock, &writefds);
struct timeval tv = {0};
tv.tv_sec = timeout / 1000;
tv.tv_usec = timeout % 1000;
return select(sock + 1, &readfds, &writefds, NULL, &tv) >= 0;
}
return select(sock + 1, NULL, NULL, NULL, NULL) != SOCKET_ERROR;
}
那么,我究竟该如何让它连接呢?当套接字未阻塞时,我似乎无法读取或写入任何内容:S。
有什么想法吗?
SSL_connect()返回的(-1)表示底层BIO无法满足SSL_connecte()继续握手的需要。
通常,调用进程必须在采取适当的操作以满足SSL_connect()的需求后重复调用。
但是,当使用非阻塞套接字时,什么也不做;但是select()可以用于检查所需的条件。
(使用缓冲BIO时,如BIO对,必须先将数据写入BIO或从BIO中检索数据,然后才能继续。)
您的代码实际上禁用了非阻塞I/O。当您将0作为FIONBIO的参数值传递给ioctlsocket时,它被记录为:
FIONBIO
*argp参数是指向无符号长值的指针。如果非阻塞模式应启用,则将*argp设置为零值;如果非阻塞模式应禁用,则将零。[..]
https://msdn.microsoft.com/en-us/library/windows/desktop/ms738573%28v=vs.85%29.aspx