//sockhelp.h
#ifndef _vx #define _vx#ifdef __cplusplus extern "C" { #endif /*** readn - 读取指定大小的字节* @fd:文件描述符* @buf:接收字节缓冲区* @count:指定的字节数* 成功返回指定字节数,失败返回-1,对方连接已经关闭,返回已经读取字节数<count* */ int readn(int fd, void *buf, int count);/*** writen - 写入指定大小的字节* @fd:文件描述符* @buf:发送字节缓冲区* @count:指定的字节数* 成功返回指定字节数,失败返回-1* */ int writen(int fd, void *buf, int count);/*** read_timeout - 读超时检测函数,不含读操作* @fd:文件描述符* @wait_seconds:等待超时秒数,如果为0表示不检测超时* 成功返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT* */ int read_timeout(int fd, unsigned int wait_seconds);/*** write_timeout - 写超时检测函数,不含写操作* @fd:文件描述符* @wait_seconds:等待超时秒数,如果为0表示不检测超时* 成功返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT* */ int write_timeout(int fd, unsigned int wait_seconds);/*** accept_timeout - 带超时accept (方法中已执行accept)* @fd:文件描述符* @addr:地址结构体指针* @wait_seconds:等待超时秒数,如果为0表示不检测超时* 成功返回已连接的套接字,失败返回-1,超时返回-1并且errno = ETIMEDOUT* */ int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);/*** connect_timeout - 带超时的connect(方法中已执行connect)* @fd:文件描述符* @addr:地址结构体指针* @wait_seconds:等待超时秒数,如果为0表示不检测超时* 成功返回0.失败返回-1,超时返回-1并且errno = ETIMEDOUT* */ int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);/*** activate_nonblock - 设置套接字非阻塞* @fd:文件描述符* 成功返回0,失败返回-1* */ int activate_nonblock(int fd);/*** deactivate_nonblock - 设置套接字阻塞* @fd:文件描述符* 成功返回0,失败返回-1* */ int deactivate_nonblock(int fd);#ifdef __cplusplus } #endif #endif
////sockhelp.c //socket发送接收底层辅助方法 /*底层辅助方法不打印错误信息,由上层调用通过errno打印信息,并且不做参数验证,有调用函数验证*/#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <sys/select.h> #include <fcntl.h> #include <netinet/in.h>/*** readn - 读取指定大小的字节* @fd:文件描述符* @buf:接收字节缓冲区* @count:指定的字节数* 成功返回指定字节数,失败返回-1,对方连接已经关闭,返回已经读取字节数<count* */ int readn(int fd, void *buf, int count) {//定义剩余字节数int lread = count;//定义每次读取的字节数int nread = 0;//定义辅助指针变量char *pbuf = (char *) buf;//如果剩余字节数大于0,循环读取while (lread > 0){nread = read(fd, pbuf, lread);if (nread == -1){//read()是可中断睡眠函数,需要屏蔽信号if (errno == EINTR)continue;//read()出错,直接退出return -1;} else if (nread == 0){//对方关联连接return count - lread;}//重置剩余字节数lread -= nread;//辅助指针变量后移pbuf += nread;}return count; }/*** writen - 写入指定大小的字节* @fd:文件描述符* @buf:发送字节缓冲区* @count:指定的字节数* 成功返回指定字节数,失败返回-1* */ int writen(int fd, void *buf, int count) {//剩余字节数int lwrite = count;//每次发送字节数int nwrite = 0;//定义辅助指针变量char *pbuf = (char *) buf;while (lwrite > 0){nwrite = write(fd, pbuf, lwrite);if (nwrite == -1){//注意:由于有TCP/IP发送缓存区,所以即使对方关闭连接,发送也不一定会失败//所以需要捕捉SIGPIPE信号return -1;}lwrite -= nwrite;pbuf += nwrite;}return count; }/*** 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 readfds;//清空文件描述符FD_ZERO(&readfds);//将当前文件描述符添加集合中FD_SET(fd, &readfds);//定义时间变量struct timeval timeout;timeout.tv_sec = wait_seconds;timeout.tv_usec = 0;do{ret = select(fd + 1, &readfds, NULL, NULL, &timeout);} while (ret == -1 && errno == EINTR);//ret==-1时,返回的ret正好就是-1if (ret == 0){errno = ETIMEDOUT;ret = -1;} 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 writefds;//清空集合FD_ZERO(&writefds);//添加文件描述符FD_SET(fd, &writefds);//定义时间变量struct timeval timeout;timeout.tv_sec = wait_seconds;timeout.tv_usec = 0;do{ret = select(fd + 1, NULL, &writefds, NULL, &timeout);} while (ret == -1 && errno == EINTR);if (ret == 0){errno = ETIMEDOUT;ret = -1;} else if (ret == 1){ret = 0;}}return ret; }/*** accept_timeout - 带超时accept (方法中已执行accept)* @fd:文件描述符* @addr:地址结构体指针* @wait_seconds:等待超时秒数,如果为0表示不检测超时* 成功返回已连接的套接字,失败返回-1,超时返回-1并且errno = ETIMEDOUT* */ int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) {int ret = 0;if (wait_seconds > 0){fd_set readfds;FD_ZERO(&readfds);FD_SET(fd, &readfds);struct timeval timeout;timeout.tv_sec = wait_seconds;timeout.tv_usec = 0;do{ret = select(fd + 1, &readfds, NULL, NULL, &timeout);} while (ret == -1 && errno == EINTR);if (ret == -1){return ret;} else if (ret == 0){ret = -1;errno = ETIMEDOUT;
return ret;}//成功无需处理,直接往下执行 }if (addr != NULL){socklen_t len = sizeof(struct sockaddr_in);ret = accept(fd, (struct sockaddr *) addr, &len);} else{ret = accept(fd, NULL, NULL);}return ret; }/*** activate_nonblock - 设置套接字非阻塞* @fd:文件描述符* 成功返回0,失败返回-1* */ int activate_nonblock(int fd) {int ret = 0;int flags = fcntl(fd, F_GETFL);if (flags == -1)return -1;flags = flags | O_NONBLOCK;ret = fcntl(fd, F_SETFL, flags);if (ret == -1)return -1;return ret; }/*** deactivate_nonblock - 设置套接字阻塞* @fd:文件描述符* 成功返回0,失败返回-1* */ int deactivate_nonblock(int fd) {int ret = 0;int flags = fcntl(fd, F_GETFL);if (flags == -1)return -1;flags = flags & (~O_NONBLOCK);ret = fcntl(fd, F_SETFL, flags);if (ret == -1)return -1;return ret; }/*** connect_timeout - 带超时的connect(方法中已执行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 = 0;//connect在网络中非常耗时,所以需要设置成非阻塞if (wait_seconds > 0){if (activate_nonblock(fd) == -1)return -1;}ret = connect(fd, (struct sockaddr *) addr, sizeof(struct sockaddr));if (ret == -1 && errno == EINPROGRESS){fd_set writefds;FD_ZERO(&writefds);FD_SET(fd, &writefds);struct timeval timeout;timeout.tv_sec = wait_seconds;timeout.tv_usec = 0;do{ret = select(fd + 1, NULL, &writefds, NULL, &timeout);} while (ret == -1 && errno == EINTR);if (ret == 0){errno = ETIMEDOUT;ret = -1;} else if (ret == 1){//判断可读是否是套接字错误int err = 0;socklen_t len = sizeof(err);ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);//ret==-1 不需要处理,正好给ret赋值if (ret == 0 && err != 0){errno = err;ret = -1;}}}if (wait_seconds > 0){if (deactivate_nonblock(fd) == -1)return -1;}return ret; }
.SUFFIXES:.c .o CC=gcc SRCS=sockhelp.c OBJS=$(SRCS:.c=.o) EXEC=libsockhelp.sostart:$(OBJS)$(CC) -shared -o $(EXEC) $(OBJS)@echo "^_^-----OK------^_^" .c.o:$(CC) -Wall -g -fPIC -o $@ -c $< clean:rm -f $(OBJS)rm -f $(EXEC)