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

470 篇文章 38 订阅

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

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

不过这里是不是讲得不太对?遇到换行符的话,也能直接发吧?不用存满?
在这里插入图片描述

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

在这里插入图片描述

意思说是除了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上的数据貌似是正常的哎。。。
在这里插入图片描述
那么说,我其实可以把上面的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;
				}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dontla

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值