connect_timo函数,给定timeout秒内连接服务端,0成功,其它失败;
writen函数,阻塞写n个字节,返回实际读取的字节数;
readn函数,可以设置超时限制,在超时时间内阻塞读取n个字节,返回实际读取的字节数。
readn和writen封装函数的意义:不少初学者容易犯这样的错误,调用send或recv不检查返回值。这跟不熟悉TCP和操作系统的特性有关,TCP是可以分片的,操作系统的buffer也是有限制的,一次阻塞send调用未必可以将所希望的长度写完(虽然仅仅是写到操作系统buffer而已),一次阻塞recv调用读取到的也未必是所指定的读取长度(但现在版本glibc的recv函数flags参数支持MSG_WAITALL了)。
--- 贴代码 tcp_util.c ---
/*
* tcp_util.c tpc sock utils module
* Author Digger Wu ([email protected])
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
/*
* connect within given timeout
*/
int
connect_timo(struct in_addr addr, u_short port, int seconds)
{
struct sockaddr_in server;
int sock;
fd_set fdw;
struct timeval timeout;
int fflag;
int errcode;
int errlen;
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = addr.s_addr;
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
return -1;
}
if ((fflag = fcntl(sock, F_GETFL, 0)) < 0) {
close(sock);
return -2;
}
if (fcntl(sock, F_SETFL, fflag|O_NDELAY) < 0) {
close(sock);
return -3;
}
connect(sock, (struct sockaddr *)&server, sizeof(server));
timeout.tv_sec = seconds;
timeout.tv_usec = 0;
FD_ZERO(&fdw);
FD_SET(sock, &fdw);
CNNT_AGAIN:
switch (select(sock + 1, NULL, &fdw, NULL, &timeout)) {
case -1:
if (errno == EINTR)
goto CNNT_AGAIN;
close(sock);
return -4;
case 0:
close(sock);
return -5;
default:
if (FD_ISSET(sock, &fdw)) {
errlen = sizeof(errcode);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR,
&errcode, (socklen_t *)&errlen) < 0) {
close(sock);
return -6;
}
if (errcode == 0) {
if (fcntl(sock, F_SETFL, fflag) < 0) {
close(sock);
return -7;
}
return (sock);
} else {
close(sock);
return -8;
}
}
}
return -1;
}
/*
* write exactly n chars
*/
int
writen(int fd, char *buffer, int length)
{
char *ptr;
int n, left = length;
ptr = buffer;
while (left) {
if ((n = send(fd, ptr, left, 0)) > 0) {
left -= n;
ptr += n;
} else
return -1;
}
return length;
}
/*
* read exactly n chars within given timeout
*/
int
readn(int fd, char *buffer, int length, int timo)
{
char *ptr;
int n, left = length;
fd_set fds;
int i, res, maxrcv = 16;
struct timeval wait;
wait.tv_sec = timo;
wait.tv_usec = 0;
if (length > 1024 * 16)
maxrcv = length / 1024;
ptr = buffer;
i = 0;
while (i++ < maxrcv && left != 0) {
FD_ZERO(&fds);
FD_SET(fd, &fds);
res = select(fd + 1, &fds, NULL, NULL, &wait);
if (res < 0) {
if (errno == EINTR) continue;
return -1;
} else if (res == 0) {
return (length - left);
}
n = recv(fd, ptr, left, 0);
if (n > 0) {
left -= n;
ptr += n;
} else if (n == 0) {
return (length - left);
} else {
return -1;
}
}
return (length - left);
}