1. 程式人生 > >封裝read、write、accept、connect函式,實現超時操作。

封裝read、write、accept、connect函式,實現超時操作。

學習linux網路程式設計的時候,看視訊學習,封裝了一些可以直接拿來用的函式,方便以後查詢。

/**
 * read_timeout - 讀超時檢測函式,不含讀操作
 *@fd: 檔案描述符
 *@wait_seconds: 等待超時秒數,如果為0表示不檢測超時
 * 成功(未超時)返回0,失敗返回-1,超時返回-1並且errno = ETIMEDOUT
 */
int read_timeout(int fd, unsigned int wait_seconds)
{
int ret = 0;
if (wait_seconds > 0)
{
fd_set read_fdset;
struct timeval timeout;


FD_ZERO(&read_fdset);
FD_SET(fd, &read_fdset);


timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do
{
ret = select(fd + 1, &read_fdset, NULL, NULL, &timeout);
} while (ret < 0 && errno == EINTR);


if (ret == 0)
{
ret = -1;
errno = ETIMEDOUT;
}
else if (ret == 1)
ret = 0;
}


return ret;
}

/**
 * write_timeout - 讀超時檢測函式,不含寫操作
 * @fd: 檔案描述符
 * @wait_seconds: 等待超時秒數,如果為0表示不檢測超時
 * 成功(未超時)返回0,失敗返回-1,超時返回-1並且errno = ETIMEDOUT
 */
int write_timeout(int fd, unsigned int wait_seconds)
{
int ret = 0;
if (wait_seconds > 0)
{
fd_set write_fdset;
struct timeval timeout;


FD_ZERO(&write_fdset);
FD_SET(fd, &write_fdset);


timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do
{
ret = select(fd + 1, NULL, NULL, &write_fdset, &timeout);
} while (ret < 0 && errno == EINTR);


if (ret == 0)
{
ret = -1;
errno = ETIMEDOUT;
}
else if (ret == 1)
ret = 0;
}


return ret;
}

/**
 * accept_timeout - 帶超時的accept
 * @fd: 套接字
 * @addr: 輸出引數,返回對方地址
 * @wait_seconds: 等待超時秒數,如果為0表示正常模式
 * 成功(未超時)返回已連線套接字,超時返回-1並且errno = ETIMEDOUT
 */
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in);


if (wait_seconds > 0)
{
fd_set accept_fdset;
struct timeval timeout;
FD_ZERO(&accept_fdset);
FD_SET(fd, &accept_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do
{
ret = select(fd + 1, &accept_fdset, NULL, NULL, &timeout);
} while (ret < 0 && errno == EINTR);
if (ret == -1)
return -1;
else if (ret == 0)
{
errno = ETIMEDOUT;
return -1;
}
}


if (addr != NULL)
ret = accept(fd, (struct sockaddr*)addr, &addrlen);
else
ret = accept(fd, NULL, NULL);
/*if (ret == -1)
ERR_EXIT("accept");
*/


return ret;
}


/**
 * connect_timeout - connect
 * @fd: 套接字
 * @addr: 要連線的對方地址
 * @wait_seconds: 等待超時秒數,如果為0表示正常模式
 * 成功(未超時)返回0,失敗返回-1,超時返回-1並且errno = ETIMEDOUT
 */
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in);


if (wait_seconds > 0)
activate_nonblock(fd);


ret = connect(fd, (struct sockaddr*)addr, addrlen);
if (ret < 0 && errno == EINPROGRESS)
{
//printf("AAAAA\n");
fd_set connect_fdset;
struct timeval timeout;
FD_ZERO(&connect_fdset);
FD_SET(fd, &connect_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do
{
/* 一量連線建立,套接字就可寫 */
ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout);
} while (ret < 0 && errno == EINTR);
if (ret == 0)
{
ret = -1;
errno = ETIMEDOUT;
}
else if (ret < 0)
return -1;
else if (ret == 1)
{
//printf("BBBBB\n");
/* ret返回為1,可能有兩種情況,一種是連線建立成功,一種是套接字產生錯誤,*/
/* 此時錯誤資訊不會儲存至errno變數中,因此,需要呼叫getsockopt來獲取。 */
int err;
socklen_t socklen = sizeof(err);
int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen);
if (sockoptret == -1)
{
return -1;
}
if (err == 0)
{
//printf("DDDDDDD\n");
ret = 0;
}
else
{
//printf("CCCCCC\n");
errno = err;
ret = -1;
}
}
}
if (wait_seconds > 0)
{
deactivate_nonblock(fd);
}
return ret;
}