这是我写的关于socket发送数据的操作,请各位斧正!
引用:int write_data(int sockfd, unsigned char *buffer, int buf_len)
{
struct timeval tv;
fd_set w_set;
int bytes_to_write_total = buf_len;
int bytes_have_written_total = 0;
int bytes_write = 0;
int result = -1;
unsigned char *ptemp = buffer;
if (-1 == sockfd || NULL == buffer || buf_len <=0)
{
return -1;
}
while (bytes_to_write_total > 0)
{
FD_ZERO(&w_set);
tv.tv_sec = 10;
tv.tv_usec = 0;
result = select(sockfd+1, NULL, &w_set, NULL, &tv);
if (result < 0)
{
if (EINTR == errno)
{
continue;
}
close(sockfd);
return -1;
}
else if (0 == result)
{
//this means timeout, it is not an error, so we return 0.
return 0;
}
else
{
if (FD_ISSET(sockfd, &w_set))
{
bytes_write = send(sockfd, ptemp, bytes_to_write_total, 0);
if (bytes_write < 0)
{
if (EAGAIN == errno || EINTR == errno)
{
continue;
}
close(sockfd);
return -1;
}
else if (0 == bytes_write)
{
//this means that the server has close the connection gracefully.
close(sockfd);
return -1;
}
else
{
bytes_to_write_total -= bytes_write;
bytes_have_written_total += bytes_write;
ptemp += bytes_have_written_total;
}
}
}
}
return 0;
}
这里有几处可能犯的不足之处:
1. 对tv的赋值放在while循环的外面。本意是每次发送数据的延时是10秒,如果放在外面就变成了把所有数据发送完毕的超时时间是10秒,如果网络状况不好,我们会经常收到超时错误。
2. ptemp忘记加上偏移,那发送的数据就错了,因为它们总是buffer开头的那一段数据。
3. send返回0值时没有做相应的处理,其实这个时候表示与服务器的连接已经断开了。
4. 没有注意对EAGAIN和EINTR错误的处理,当send返回-1时“一刀切”的返回错误。
5. 其实还有一个隐含的问题,有的程序中没有FD_ISSET这个if语句,当然在上面这个程序中不会出问题,但是如果w_set是调用函数传进来的,不加这个判断就有问题了,是有socket可以发送数据,但不一定就是你现在的socket。
6. 有的程序喜欢把select这一段放在后面(当然程序源代码就不是这个样子了。),即先去收数据,当send产生EAGAIN错误时才去select,其实这样不好,会浪费大量的CPU资源,根据实际经验,先收后select会使该函数所在的任务CPU占用率经常保持在90%以上。如果改为先select发数据,那么CPU占用率就会在10%以下。
于 2013-09-04 16:36:01发表:
else if (0 == bytes_write)
{
//this means that the server has close the connection gracefully.
close(sockfd);
return -1;
}
send返回值为0代表的应该是发送缓冲区空间满了,无法把数据拷贝到发送缓冲区了呀,怎么是 server关闭了链接呢?