//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)