最近学弟学妹们在写聊天室,期间遇到了很多问题,也“逼迫”我们这些大二(其实即将大三)狗考虑了许多以前没有考虑过的东西。现在就着我们小组的聊天室的项目,送给学弟学妹们”几个可能安全的封装函数。“
frist : 保证发送“len”字节到套接字
ssize_t Sendlen(int fd, const void *buf, size_t len, int flags)
{
ssize_t n = 0;
size_t sum = 0;
const char *ptr;
ptr = (const char *)buf;
while (sum < len)
{
n = send(fd, (void *)ptr, len - sum, flags);
if (n < 0)
{
if (errno == EINTR)
n = 0;
else
err_sys("send error ", __LINE__);
}
sum += n;
ptr += n;
}
return (sum);
}
second:保证接受“len”字节到套接字
ssize_t Recvlen(int fd, void *buf, size_t len, int flags)
{
ssize_t n = 0;
size_t sum = 0;
const char *ptr;
ptr = buf;
while (sum < len)
{
n = recv(fd, (void *)ptr, len - sum, flags);
if (n < 0)
{
if (errno == EINTR)
n = 0;
else
err_sys("recv error ", __LINE__);
}
else if (n == 0)
{
printf("对端连接关闭!!!\n");
break;
}
sum += n;
ptr += n;
}
return (sum);
}
thrid:从套接字读取一行数据
static int recv_cnt = 0;
static char *recv_ptr = NULL;
static char recv_buf[MAXLINE];
static ssize_t my_recv(int fd, char *ptr, int flags)
{
if (recv_cnt <= 0)
{
again:
if ((recv_cnt = recv(fd, recv_buf, sizeof(recv_buf), flags)) < 0)
{
if (errno == EINTR)
goto again;
else
return (-1);
}
else if (recv_cnt == 0)
{
return (0);
}
recv_ptr = recv_buf;
}
recv_cnt--;
*ptr = *recv_ptr++;
return (1);
}
ssize_t recvline(int fd, void *vptr, size_t maxlen, int flags)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n = 1; n < maxlen; n++)
{
if ((rc = my_recv(fd, &c, flags)) == 1)
{
*ptr++ = c;
if (c == '\n')
break;
}
else if (rc == 0)
{
*ptr = 0;
return (n - 1);
}
else
return (-1);
}
*ptr = 0;
return (n);
}
ssize_t Recvline(int fd, void *buf, size_t Maxlen, int flags)
{ //注意参数 Maxlen
ssize_t n;
if ((n = recvline(fd, buf, Maxlen, flags)) < 0)
err_sys("recvline error ", __LINE__);
return (n);
}
注意事项:
send和recv有三个要点:
1. 在每次进行send
或者recv
时,第二个参数是随着读取的字节数量而改变的。
2. 传进来的第二个参数是 void *
的类型,给他加1减1,就会出现问题。必须首先转换为char *
3. 第三个参数len
。同上,也是需要随着读取的字节数而改变
recvline
:
static int recv_cnt = 0; //读取的数据量
static char *recv_ptr = NULL; //指向recv_buf的字节型指针
static char recv_buf[MAXLINE]; //存放读取数据的结构
recvline
函数从recv_buf
中拿到一行数据 。
my_recv
函数检查如果读取的数据量 <=0
,就进行一次读取。问:这里为什么要用recv
而不用自己封装的 Recvlen
? 因为Recvlen
直到读取len
个字节才会返回,而我们这里并不要求,只要读取从缓冲区中读取比MAXLINE
小的字节就行了 。
if (recv_cnt <= 0)
{
again:
if ((recv_cnt = recv(fd, recv_buf, sizeof(recv_buf), flags)) < 0)
{
if (errno == EINTR)
goto again;
else
return (-1);
}
else if (recv_cnt == 0)
{
return (0);
}
recv_ptr = recv_buf;
}