linux socket write()函数阻塞卡住线程问题(线程无法结束)write()非阻塞代码

  • A+
所属分类:Linux C/C++

1、参考文章:C++网络通信中write和read的为什么会阻塞

现在要搞明白,如何让调用write()函数的时候,先让它去判断发送缓冲区(send buffer)满了没,如果满了,就跳过;

不过这里是不是讲得不太对?遇到换行符的话,也能直接发吧?不用存满?
linux socket write()函数阻塞卡住线程问题(线程无法结束)write()非阻塞代码

2、参考文章:网络编程(24)—— linux中write和read函数的阻塞试验

linux socket write()函数阻塞卡住线程问题(线程无法结束)write()非阻塞代码

意思说是除了socket描述符之外,其余的文件描述符都是非阻塞的

找write非阻塞代码

1

(这文章不错,可以转载过来)
参考文章:socket阻塞和非阻塞

对于阻塞Socket而言,如果发送缓冲区没有空间或者空间不足的话,write操作会直接阻塞住,如果有足够空间,则拷贝所有数据到发送缓冲区,然后返回.

对于写操作write,原理和read是类似的,非阻塞socket在发送缓冲区没有空间时会直接返回错误号EWOULDBLOCK,表示没有空间可写数据,如果错误号是别的值,则表明发送失败。如果发送缓冲区中有足够空间或者是不足以拷贝所有待发送数据的空间的话,则拷贝前面N个能够容纳的数据,返回实际拷贝的字节数。

非阻塞的write操作一般写法是:

ssize_t writen(int connfd, const void *pbuf, size_t nums)
{
   
	int32 nleft = 0;
	int32 nwritten = 0;
	char *pwrite_buf = NULL;

	if ((connfd <= 0) || (NULL == pbuf) || (nums < 0))
	{
   
		return -1;
	}

	pwrite_buf = (char *)pbuf;
	nleft = nums;

	while(nleft>0)
	{
   
		if (-1 == (nwritten = send(connfd, pwrite_buf, nleft, MSG_NOSIGNAL)))
		{
   
			if (EINTR == errno || EWOULDBLOCK == errno || EAGAIN == errno)
			{
   
				nwritten = 0;
			}
			else
			{
   
				errorf("%s,%d, Send() -1, 0x%x\n", __FILE__, __LINE__, errno);
				return -1;
			}
		}
		nleft -= nwritten;
		pwrite_buf += nwritten;
	}

	return(nums);
}

不过上面代码貌似不对啊,怎么看起来像是windows的。。。。

2

服务器编程心得(四)—— 如何将socket设置为非阻塞模式 - 张小方的文章 - 知乎

3

这篇讲得比较透彻
从linux源码看socket的阻塞和非阻塞 - 无毁的湖光的文章 - 知乎

找了半天找不到代码。。。。。

我的代码1

我看了这篇写了个代码:socket非阻塞读写

0 一开始我写了个这样的非阻塞write代码

				KY_AI_LOG("[ky_event_proc_thread] write start\n"); //for debug

				//设置socket_fd为非阻塞
				unsigned long ul = 1;
				//KY_AI_LOG("[ky_get_connected_socket_fd] ioctl start\n"); //for debug
				ioctl(socket_fd, FIONBIO, &ul); //设置为非阻塞模式
				//KY_AI_LOG("[ky_get_connected_socket_fd] ioctl end\n"); //for debug


				int write_pos = 0;
				//int nLeft = nLen;
				int nLeft = strlen(sendbuf);
				int write_flag = 1;

				int write_count = 0;
				while(thread_flg && nLeft > 0)
				{
   
					int nWrite = 0;
					if( (nWrite = write(socket_fd, sendbuf+write_pos, nLeft)) <= 0)
					{
   
						write_error_count++;
						KY_AI_LOG("[ky_event_proc_thread] write_error_count: [%d]\n", write_error_count);
						
						write_flag = 0;		
						
						KY_AI_LOG("[ky_event_proc_thread] write error: errno[%d]\n", errno); //for debug

						/** if(errno == EWOULDBLOCK || errno == EAGAIN) { nWrite = 0; } else { //写失败 } **/
					}
					else
					{
   
						write_count++;
						KY_AI_LOG("[ky_event_proc_thread] write times: [%d]\n", write_count);
						
						nLeft -= nWrite;
						write_pos += nWrite;
					}
				}



				//设置socket_fd回阻塞状态
				ul = 0;
				//KY_AI_LOG("[ky_get_connected_socket_fd] ioctl(socket_fd, FIONBIO, &ul) start\n"); //for debug
				ioctl(socket_fd, FIONBIO, &ul); //设回阻塞模式(非阻塞模式有缺陷,什么缺陷?)
				//KY_AI_LOG("[ky_get_connected_socket_fd] ioctl(socket_fd, FIONBIO, &ul) end\n"); //for debug





				if(0 == write_flag)
				{
   
					KY_AI_LOG("[ky_event_proc_thread] close start\n"); //for debug
					if(-1 == close(socket_fd))KY_AI_LOG("[ky_event_proc_thread] close socket_fd failed\n");
					KY_AI_LOG("[ky_event_proc_thread] close end\n"); //for debug
					socket_fd = -1;
					
					break;
				}
				KY_AI_LOG("[ky_event_proc_thread] write end\n");	//for debug

但是发现,我这边日志全部都显示一次就发完了,这是因为缓冲区远远大于需要发送的消息的原因?

我决定改发到我的ubuntu看看内容正不正常?

改好了,发送到ubuntu上的数据貌似是正常的哎。。。
linux socket write()函数阻塞卡住线程问题(线程无法结束)write()非阻塞代码
那么说,我其实可以把上面的write循环给去掉?因为我们数据产生的时间是远远大于实际发送数据时间的,同时我们线程是判断数据有更新后,再去发的,所以这么做应该是可行的。。。但是假如以后有数据产生时间小于我们发送时间的需求呢?。。。(那我们必然来不及发送,需要丢弃一部分数据了。。。)

1 去掉循环write

				//KY_AI_LOG("[ky_event_proc_thread] write start\n"); //for debug 

				//设置socket_fd为非阻塞
				unsigned long ul = 1;
				//KY_AI_LOG("[ky_get_connected_socket_fd] ioctl start\n"); //for debug
				ioctl(socket_fd, FIONBIO, &ul); //设置为非阻塞模式
				//KY_AI_LOG("[ky_get_connected_socket_fd] ioctl end\n"); //for debug


				if(write(socket_fd, sendbuf, strlen(sendbuf)) <= 0)	//write很快,阻塞状态下几乎1ms就搞定了
				{
   				
					write_error_count_all++;
					write_error_count++;
					KY_AI_LOG("[ky_event_proc_thread] write_error_count_all: [%d], write_error_count: [%d]\n", 
						write_error_count_all, write_error_count);
					KY_AI_LOG("[ky_event_proc_thread] write error: errno[%d]\n", errno);
				}
				else
				{
   
					write_error_count = 0;
				}


				//设置socket_fd回阻塞状态
				ul = 0;
				//KY_AI_LOG("[ky_get_connected_socket_fd] ioctl(socket_fd, FIONBIO, &ul) start\n"); //for debug
				ioctl(socket_fd, FIONBIO, &ul); //设回阻塞模式(非阻塞模式有缺陷,什么缺陷?)
				//KY_AI_LOG("[ky_get_connected_socket_fd] ioctl(socket_fd, FIONBIO, &ul) end\n"); //for debug

				if(write_error_count>=3)	//连续三次write失败
				{
   
					KY_AI_LOG("[ky_event_proc_thread] close start\n"); //for debug
					if(-1 == close(socket_fd))KY_AI_LOG("[ky_event_proc_thread] close socket_fd failed\n");
					KY_AI_LOG("[ky_event_proc_thread] close end\n"); //for debug
					socket_fd = -1;					
					break;
				}

接下来就是测试看有没有什么问题

w3cjava

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: