IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> linux socket write()函数阻塞卡住线程问题(线程无法结束)write()非阻塞代码 -> 正文阅读

[系统运维]linux socket write()函数阻塞卡住线程问题(线程无法结束)write()非阻塞代码

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;
				}

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

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-21 19:18:51  更:2022-05-21 19:19:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 15:19:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码