linux客户端Socket非阻塞connect编程
You have a non-blocking socket and you are calling connect()
in it. Since connect()
needs the 3-way handshake to happen (so a network roundtrip), it either blocks waiting for the SYN-ACK in blocking sockets, or gives you some indication that it hasn't succeded yet in non-blocking sockets. Normally, non-blocking sockets return EAGAIN/EWOULDBLOCK to tell you that they couldn't progress and you should try again: this is not exactly your case, connect()
returns EAGAIN/EWOULDBLOCK when there are no free ephemeral ports to tell you that you should try again later; so there is another error for non-blocking connect: EINPROGRESS, which tells you that the operation is in progress and you should check its status later.
To check the status later, the socket will become ready for writability, so you can useselect()
/poll()/...
to test for that, after which you'll have to getsockopt(...SO_ERROR...)
to get the success/failure status of your connect() operation.
int tcp_connect(const char *addr, int port, int timeout)
{
int fd, error, tmp;
unsigned int len;
struct sockaddr_in sa_in;
struct timeval tv;
fd_set fds;
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
return -1;
bzero(&sa_in, sizeof(sa_in));
sa_in.sin_family = AF_INET;
sa_in.sin_port = htons(port);
sa_in.sin_addr.s_addr = inet_addr(addr);
set_nonblocking(fd, 1);
PRINTF("Connect IP: %s PORT: %d(tcp_client)\n", addr, port, timeout);
tmp = connect(fd, (struct sockaddr *)&sa_in, sizeof(sa_in));
if (tmp < 0) {
error = errno;
if (error != EINTR && error != EINPROGRESS) {
close(fd);
PRINTF("Connect fail reason:%s(tcp_client)\n",
strerror(error));
return -1;
}
}
while (timeout > 0) {
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(fd, &fds);
tmp = select(fd + 1, NULL, &fds, NULL, &tv);
if (tmp > 0) {
len = sizeof(error);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0
|| error) {
close(fd);
PRINTF("Connect fail reason:%s(tcp_client)\n",
strerror(error));
return -1;
}
else {
set_nonblocking(fd, 0);
PRINTF("Connect ok, fd is %d\n", fd);
return fd;
}
}
else if (tmp < 0) {
error = errno;
if (error != EINTR && error != EINPROGRESS) {
close(fd);
PRINTF("Connect fail, reason:%s(tcp_client)\n",
strerror(error));
return -1;
}
}
msleep(100);
timeout -= 100;
}
close(fd);
PRINTF("Connect timeout(tcp_client)\n");
return -1;
}