Linux共享库 socket辅助方法
//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正好就是-1 if (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.so start:$(OBJS) $(CC) -shared -o $(EXEC) $(OBJS) @echo "^_^-----OK------^_^" .c.o: $(CC) -Wall -g -fPIC -o $@ -c $< clean: rm -f $(OBJS) rm -f $(EXEC)