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)

 

posted on 2016-12-14 11:16  寒魔影  阅读(332)  评论(0编辑  收藏  举报

导航