#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<strings.h>
void perr_exit(const char *s)
{
perror(s);
exit(-1);
}
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
int n;
again:
if((n = accept(fd, sa, salenptr)) < 0)
{
if((errno == ECONNABORTED) || (errno == EINTR))//如果是被信号中断和软件层次中断,不能退出
goto again;
else
perr_exit("accept error");
}
return n;
}
int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
int n;
if((n = bind(fd, sa, salen)) < 0)
perr_exit("bind error");
return n;
}
int Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
int n;
if((n = connect(fd, sa, salen)) < 0)
perr_exit("connect error");
return n;
}
int Listen(int fd, int backlog)
{
int n;
if((n = listen(fd, backlog)) < 0)
perr_exit("listen error");
return n;
}
int Socket(int family, int type, int protocal)
{
int n;
if((n = socket(family, type, protocal)) < 0)
perr_exit("socket error");
return n;
}
ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
again:
if((n = read(fd, ptr, nbytes)) == -1)
{
if(errno == EINTR)
goto again;
else
return -1;
}
return n;
}
ssize_t Write(int fd, const void *ptr, size_t nbytes)
{
ssize_t n;
again:
if((n = write(fd, ptr, nbytes)) == -1)
{
if(errno == EINTR)
goto again;
else
return -1;
}
return n;
}
// 读取固定的字节数数据
ssize_t Readn(int fd, void *vptr, size_t n)
{
size_t nleft; //unsigned int 剩余未读取的字节数
ssize_t nread; //int 实际读到的字节数
char *ptr;
ptr = vptr;
nleft = n;
while(nleft > 0)
{
if((nread = read(fd, ptr, nleft)) < 0)
{
if(errno == EINTR)
nread = 0;
else
return -1;
}
else if(nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return n - nleft;
}
// 从文件描述符中读取一个字符的函数
// 返回实际读取的字节数,出错时返回-1
static ssize_t my_read(int fd, char *ptr)
{
static int read_cnt;
static char *read_ptr;
static char read_buf[100];
// 如果缓冲区为空,重新从文件中读取数据
if(read_cnt <= 0)
{
again:
// 调用read函数读取数据
if((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0)
{
// 如果被中断,重新尝试读取
if(errno == EINTR)
goto again;
return -1;
}
// 文件描述符被关闭,退出
else if(read_cnt == 0)
return 0;
read_ptr = read_buf;
}
// 从缓冲区中读取一个字符
read_cnt--;
*ptr = *read_ptr++;
return 1;
}
// 从文件描述符中读取一行数据的函数,限制最大长度为maxlen
// 返回实际读取的字节数,出错时返回-1
ssize_t Readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
// 循环读取每个字符,直到达到最大长度
for(n = 1; n < maxlen; n++)
{
// 调用my_read函数读取一个字符
if((rc = my_read(fd, &c)) == 1)
{
*ptr++ = c;
// 如果读到换行符,结束循环
if(c == '\n')
break;
}
// 文件结束,将字符串结束符添加并返回实际读取的字节数
else if(rc == 0)
{
*ptr = 0;
return n-1;
}
// 出错时返回-1
else
return -1;
}
*ptr = 0;
// 返回实际读取的字节数
return n;
}
// 创建并绑定IPV4 TCP套接字,返回监听套接字描述符
// 参数:
// - port:要绑定的端口号
// - IP:要绑定的IP地址,如果为NULL,则绑定到所有可用的接口
// 返回值:监听套接字描述符
int tcp4bind(short port, const char *IP)
{
struct sockaddr_in serv_addr; // 服务器地址结构体
int lfd = Socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
bzero(&serv_addr, sizeof(serv_addr)); // 将服务器地址结构体清零
if(IP == NULL)
{
// 如果IP为空,则绑定到所有可用接口(0.0.0.0)
serv_addr.sin_addr.s_addr = INADDR_ANY;
}
else
{
// 将IP地址从文本形式转换为网络字节序
if(inet_pton(AF_INET, IP, &serv_addr.sin_addr.s_addr) <= 0)
{
perror(IP); // 转换失败
exit(1); // 退出程序
}
}
serv_addr.sin_family = AF_INET; // 地址族为IPv4
serv_addr.sin_port = htons(port); // 设置端口号并转换为网络字节序
// 设置套接字选项,可选,用于地址重用
// int opt = 1;
//setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 将套接字绑定到指定地址和端口
Bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
return lfd; // 返回监听套接字描述符
}
tcp包裹函数
于 2024-01-22 12:44:53 首次发布