Linux如何设置Socket阻塞模式:一步操作指南 (linux 设置socket阻塞)
在Linux系统中,Socket通信是非常常见的一种方式。而在Socket通信中,常常会使用到Socket阻塞模式,以实现Socket的数据读取和发送。那么究竟怎样来设置Socket阻塞模式呢?本文将为大家提供一步操作指南,帮助大家更好地了解Linux系统中如何设置Socket阻塞模式。
1.什么是Socket阻塞模式
在Socket通信中,Socket阻塞模式是指当一个Socket被设置为阻塞模式时,当Socket进行数据读取或发送时,如果当前没有新的数据或者Socket正在处理数据的过程中,Socket会暂停运行直至有数据可读或可写为止。可以理解为Socket在等待数据的到来,一旦数据到来就会立即进行处理。
2.Socket阻塞模式和非阻塞模式的区别
与阻塞模式不同,非阻塞模式下的Socket会一直处于运行状态,不会发生任何等待情况。当没有新的数据需要处理时,Socket会一直尝试重新接收数据,直至新数据到来为止。相比于阻塞模式,非阻塞模式下的Socket处理数据速度更快,但也更容易出现数据丢失等问题。
3.Linux系统中如何设置Socket阻塞模式
在Linux系统中,设置Socket阻塞模式非常简单,只需要使用setsockopt函数即可进行设置。以下是具体的步骤:
(1)首先需要创建Socket,可以使用socket函数进行创建:
“`c
int sockfd = socket(domn, type, protocol);
“`
其中,domn是指协议族,type是指Socket类型(如SOCK_STREAM表示TCP Socket,SOCK_DGRAM表示UDP Socket),protocol是指使用的协议。
(2)然后需要将Socket设置为阻塞模式。可以使用setsockopt函数来实现该功能:
“`c
int socket_set_block(int sockfd, int on)
{
int flags = fcntl(sockfd, F_GETFL, 0);
if (flags
return -1;
}
if (on) {
flags &= ~O_NONBLOCK; //设置为阻塞模式
} else {
flags |= O_NONBLOCK; //设置为非阻塞模式
}
return fcntl(sockfd, F_SETFL, flags);
}
“`
上述代码中,on为1表示将Socket设置为阻塞模式,为0表示将Socket设置为非阻塞模式。函数的返回值为-1时表示设置失败,否则表示设置成功。
(3)在Socket处理数据时,需要使用recv和send函数进行数据的读取和发送。阻塞模式下的recv和send函数将会一直等待新数据的到来:
“`c
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
“`
其中,sockfd为Socket描述符,buf为接收或发送缓冲区指针,len为缓冲区大小,flags用于设置Socket的具体属性。
4.常见的Socket阻塞模式问题解决方案
在使用Socket阻塞模式时,常常会遇到一些问题,如超时时间过长、进程/线程阻塞等。为了解决这些问题,可以使用一些解决方案:
(1)设置超时时间。可以通过设置Socket的SO_RCVTIMEO和SO_SNDTIMEO选项来设置超时时间,一旦超时就会返回错误。
(2)使用多线程/进程。在Socket处理数据的过程中,可以使用多线程或进程来避免主程序的阻塞。可以使用pthread_create函数创建线程,fork函数创建进程。
(3)使用select函数。select函数可以同时监控多个Socket,并且在有数据到来时立即处理。
5.
通过以上几步操作,我们就可以轻松地实现Linux系统中的Socket阻塞模式设置。需要注意的是,阻塞模式与非阻塞模式各有优缺点,应根据具体的情况选择。同时,在Socket处理数据时也应该尽可能地避免阻塞,避免出现超时等问题。
相关问题拓展阅读:
- Linux 怎样实现非阻塞connect
- Linux系统I/O模型及select、poll、epoll原理和应用
Linux 怎样实现非阻塞connect
1. 设置socket
int oldOption = fcntl(sockfd, F_GETFL);
int newOption = oldOption | O_NONBLOCK;
//设置sockfd非阻塞
fcntl(sockfd, F_SETFL, newOption);12345
2. 执行connect
如果返回0,表示连接成功,这种情况一般在本机上连接时会出现(否则怎么可能那么快)
否则,查看error是否等于EINPROGRESS(表明正在进行连接中),如果不等于,则连接失败
int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
if(ret == 0)
{
//连接成功
fcntl(sockfd, F_SETFL, oldOption);
return sockfd;
}
else if(errno != EINPROGRESS)
{
//连接没有立即返回,此时errno若不是EINPROGRESS,表明错误
perror(“connect error != EINPROGRESS”);
return -1;
}
3. 使用select,如果没用过select可以去看看
用select对socket的读写进行监听
那么监听结果有四种可能
1. 可写(当连接成功后,sockfd就会处于可写状态,此时表示连接成功)
2. 可读可写(在出错后,sockfd会处于可读可写状态,但有一种特殊情况见第三条)
3. 可读可写(我们可以想象,在我们connect执行完到select开始监听的这段时间内,
如果连接已经成功,并且服务端发送了数据,那么此时sockfd就是可读可写的,
因此我们需要对这种情况特殊判断)
说白了,在可读可写时,我们需要甄别此时是否已经连接成功,我们采用这种方案:
再次执行connect,然后查看error是否等于EISCONN(表示已经连接到该套接字)。
4. 错误
if(FD_ISSET(sockfd, &writeFds))
{
//可读可写有两种可能,一是连接错误,二是在连接后服务端已有数据传来
if(FD_ISSET(sockfd, &readFds))
{
if(connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) != 0)
{
int error=0;
socklen_t length = sizeof(errno);
//调用getsockopt来获取并清除sockfd上的错误.
if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &length)
{
printf(“get socket option failed\n”);
close(sockfd);
return -1;
}
if(error != EISCONN)
{
perror(“connect error != EISCONN”);
close(sockfd);
return -1;
}
}
}
//此时已排除所有错误可能,表明连接成功
fcntl(sockfd, F_SETFL, oldOption);
return sockfd;
}0
4. 恢复socket
因为我们只是需要将连接操作变为非阻塞,并不包括读写等,所以我们吃醋要将socket重新设置。
fcntl(sockfd, F_SETFL, oldOption);关于Linux命令的介绍,看看《linux就该这么学》,具体关于这一章地址3w(dot)linuxprobe/chapter-02(dot)html
Linux系统I/O模型及select、poll、epoll原理和应用
理解Linux的IO模型之前,首先要了解一些基本概念,才能理解这些IO模型设计的依据
操作系统使用虚拟内旦谈磨存来映射物理内存,对于32位的操作系统来说,虚拟地址空间为4G(2^32)。操作系统的核心是内核,为了保护用户进程不能直接操作内核,保证内核安全,操作系统将虚拟地址空间划分为内核空间和用户空间。内核可以访问全部的地址空间,拥有访问底层硬件设备的权限,普通的应用程序需要访问硬件设备必须通过
系统调用
来实现。
对于Linux系统来说,将虚拟内存的更高1G字节的空间作为内核空间仅供内核使用,低3G字节的空间供用户进程使用,称为用户空间。
又被称为标准I/O,大多数文件系统的默认I/O都是缓存I/O。在Linux系统的缓存I/O机制中,操作系统会将I/O的数据缓存在页缓存(内存)中,也就是数据先被拷贝到内核的缓冲区(内核地址空间),然后才会从内核缓冲区拷贝到应用程序的缓冲区(用户地址空间)。
这种方式很明显的缺点就是数据传输过程中需要再应用程序地址空间和内核空间进行多次数据拷贝操作,这些操作带来的CPU以及内存的开销是非常大的。
由于Linux系统采用的缓存I/O模式,对于一次I/O访问,以读操作举例,数据先会被拷贝到内核缓冲区,然后才会从内核缓冲区拷贝到应用程序的缓存区,当一个read系统调用发生的时候,会经历两个阶段:
正是因为这两个状态,Linux系统才产生了多种不同的网络I/O模式的方案
Linux系统默认情况下所有socke都是blocking的,一个读操作流程如下:
以UDP socket为例,当用户进程调用了recvfrom系统调用,如果数据还没准备好,应用进程被阻塞,内核直到数据到来且将数据从内核缓冲区拷贝到了应用进程缓冲区,然后向用户进程返回结果,用户进程才解除block状态,重新运行起来。
阻塞模行下只是阻塞了当前的应用进程,其他进程还可以执行,不消耗CPU时间,CPU的利用率较高。
Linux可以设置socket为非阻塞的,非阻塞模式下执行一个读操作流程如下:
当用户进程发出recvfrom系统调用时,如果kernel中的数据还没准备好,模斗recvfrom会立即返回一个error结果,不会阻塞用户进程,用户进程收到error时知道数据还没准备好,过一会再调用recvfrom,直到kernel中的数据准备好了,内核就立即将数据拷贝到用户内存然后返回ok,这个过程需要用户进程去轮询内核数据是否准备好。
非阻塞模型下由于要处理更多的系统调用,因此CPU利用率比较低。
应用进程使用sigaction系统调用,内核立即返回,等到kernel数据准备好时会给用户进程发送一个信号,告诉用户进程可以进行IO操作了,然后用户进程再调用IO系统调用如recvfrom,将数据从内核缓冲区拷贝到应用进程。流程如下:
相比于轮询的方式,不需要多次系统调用轮询,信号驱动IO的CPU利用率更高。
异步IO模型与其他模型更大的区别是,异步IO在系统调用返回的时候所有操作都已经完成,应用进程既不需要等待数据准备,也不需要在数据到来后等待数据从内核缓冲区拷贝到用户缓冲区,流程如下:
在数据拷贝完成后,kernel会给用户进程发送一个信号告诉其read操作完成了。
是用select、poll等待数据,可以等待多个socket中的任一个变为可读,这一过程会被阻塞,当某个套接字数据到来时返回,之后再用recvfrom系统调用把数据从内核缓存区复制到用户进程,流程如下:
流程类似阻塞IO,甚至比阻塞IO更差,多使用了一个系统调用,但是IO多路复用更大的侍兄特点是让单个进程能同时处理多个IO事件的能力,又被称为事件驱动IO,相比于多线程模型,IO复用模型不需要线程的创建、切换、销毁,系统开销更小,适合高并发的场景。
select是IO多路复用模型的一种实现,当select函数返回后可以通过轮询fdset来找到就绪的socket。
优点是几乎所有平台都支持,缺点在于能够监听的fd数量有限,Linux系统上一般为1024,是写死在宏定义中的,要修改需要重新编译内核。而且每次都要把所有的fd在用户空间和内核空间拷贝,这个操作是比较耗时的。
poll和select基本相同,不同的是poll没有更大fd数量限制(实际也会受到物理资源的限制,因为系统的fd数量是有限的),而且提供了更多的时间类型。
总结:select和poll都需要在返回后通过轮询的方式检查就绪的socket,事实上同时连的大量socket在一个时刻只有很少的处于就绪状态,因此随着监视的描述符数量的变多,其性能也会逐渐下降。
epoll是select和poll的改进版本,更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。
epoll_create()用来创建一个epoll句柄。
epoll_ctl() 用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个就绪链表中管理。
epoll_wait() 可以从就绪链表中得到事件完成的描述符,因此进程不需要通过轮询来获得事件完成的描述符。
当epoll_wait检测到描述符IO事件发生并且通知给应用程序时,应用程序可以不立即处理该事件,下次调用epoll_wait还会再次通知该事件,支持block和nonblocking socket。
当epoll_wait检测到描述符IO事件发生并且通知给应用程序时,应用程序需要立即处理该事件,如果不立即处理,下次调用epoll_wait不会再次通知该事件。
ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用nonblocking socket,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
【segmentfault】 Linux IO模式及 select、poll、epoll详解
【GitHub】 CyC2023/CS-Notes
linux 设置socket阻塞的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux 设置socket阻塞,Linux如何设置Socket阻塞模式:一步操作指南,Linux 怎样实现非阻塞connect,Linux系统I/O模型及select、poll、epoll原理和应用的信息别忘了在本站进行查找喔。
编辑:一起学习网
标签:数据,内核,模式,进程,系统