コネクトは非ブロッキング時にEINPROGRESSエラーを発生する
非ブロッキングの場合に connect 関数を実行した場合、接続がすぐに確立できなければ、connect 関数は直ちに返り、エラーコードの EINPROGRESS が返されます。これは接続が進行中であることを意味します。
非ブロックモードでは、connect関数の返り値は接続が確立できたかどうかではなく、接続処理が開始されたかどうかを示します。接続が確立できれば、select、poll、epoll などの I/O マルチプレクシング関数を用いてソケットが書き込めるかどうかを確認し、接続完了を判断できます。
getsockopt関数のSO_ERRORオプション値を利用して、接続完了前にソケットの状態を取得し、これが0の場合は接続が正常に確立されたことを示し、それ以外のエラーコードの場合は接続が失敗したことを示します。非ブロックモードを使用して接続完了状態をチェックして、他の操作の継続が可能になります。
非ブロック接続を使用するサンプルコードを示します。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
int main() {
int sockfd;
struct sockaddr_in server_addr;
int ret;
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
exit(1);
}
// 设置套接字为非阻塞
if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
perror("fcntl");
exit(1);
}
// 初始化服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// 发起连接请求
ret = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (ret == -1 && errno != EINPROGRESS) {
perror("connect");
exit(1);
}
// 使用select函数等待套接字可写
fd_set write_fds;
FD_ZERO(&write_fds);
FD_SET(sockfd, &write_fds);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
ret = select(sockfd + 1, NULL, &write_fds, NULL, &timeout);
if (ret == -1) {
perror("select");
exit(1);
} else if (ret == 0) {
printf("connect timeout\n");
exit(1);
}
// 检查套接字的错误状态
int error = 0;
socklen_t len = sizeof(error);
ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
if (ret == -1) {
perror("getsockopt");
exit(1);
}
if (error != 0) {
printf("connect failed: %s\n", strerror(error));
exit(1);
}
printf("connect successful\n");
close(sockfd);
return 0;
}
例のコードでは、まずソケットを作成して、非ブロックモードに設定します。その後、connect関数を用いて接続要求を行います。connectが-1を返し、errnoがEINPROGRESSでない場合、接続に失敗したと判断し、エラーメッセージを出力して終了します。
接続要求が進行中の場合、プログラムはselect関数を使用してソケットが書き込み可能になるのを待ちます。selectが-1を返した場合は、エラーが発生したことを示し、プログラムはエラーメッセージを出力して終了します。selectが0を返した場合は、接続がタイムアウトしたことを示し、プログラムは対応するプロンプトメッセージを出力して終了します。
接続要求が完了したら、プログラムは getsockopt 関数を使ってソケットのエラー状態を取得します。エラー状態が 0 の場合は接続が正常に確立されたことを示し、エラー状態が他の値の場合は接続が失敗したことを示し、プログラムは対応するエラーメッセージを表示して終了します。
非ブロッキング接続のサンプルコードを記載しましたので、実際の用途に合わせて修正・拡張してください。