Linux Socket阻塞与非阻塞:网络编程中的关键区别 (linux socket 阻塞 非阻塞)

在网络编程中,使用Socket是一种很常见的方式。无论是在Windows还是Linux下,Socket都是开发网络应用的一个基础。而其中一个关键的区别就是阻塞和非阻塞。

阻塞式 Socket

阻塞式 Socket 是最常用的 Socket 编程模型,这种模型下应用程序在调用 Socket API 时,如果数据没有到来或被发送出去,它会一直等待,直到有数据或发送返回结果。这种模型相对来说较为简单,容易实现。但是却有一个缺点,就是当 Socket 没有收到数据时,线程会被挂起等待,这样会影响应用程序性能。

对于阻塞式 Socket 在 Linux 内核中的实现原理,主要是使用了 select 系统调用,并且在调用过程中将线程挂起。当有数据可以读取时, select 会将挂起的线程唤起,这时候应用程序就可以读取数据了。这种实现方式虽然简单,但是并发性相对来说比较差,因为它会占用一个线程。

非阻塞式 Socket

与阻塞式 Socket 相比,非阻塞式 Socket 相对来说更加灵活、高效。它不是一直等待数据到来或发送出去,而是通过轮询的方式等待,这样应用程序就可以继续执行其他任务。如果没有数据到来或被发送出去,直接返回,并不会挂起线程。这是非常适合于多线程或多进程的应用程序。

在 Linux 内核中,如何实现非阻塞式 Socket 呢?主要是通过设置 socket 的属性为非阻塞模式(O_NONBLOCK)。当应用程序调用 Socket API 时,如果没有数据到来或被发送出去, select 函数将返回 -1,表示没有 I/O 事件发生。此时线程可以做其他的事情,而不是一直等待。

在实际的应用中,阻塞式 Socket 和非阻塞式 Socket 都有自己的优缺点。阻塞式 Socket 因其简单的实现方式,适用于一些较小的应用程序。而非阻塞式 Socket 则适用于大型的应用程序,如多线程的 Web 服务器,它可以更好的处理并发请求。

另外,在实际的应用中,开发人员可以将阻塞和非阻塞式 Socket 结合起来使用,以实现更高效的应用程序。例如,在一个多线程的服务器应用中,可以使用非阻塞式 Socket 来监听客户端连接,当有连接到来时可以将连接交给一个专门的线程来处理。

在网络编程中,Socket 是一个关键的组件。而阻塞式 Socket 和非阻塞式 Socket 则是应用程序的两种主要模式。阻塞式 Socket 适用于一些简单的应用程序,而非阻塞式 Socket 更适用于处理并发请求的大型应用程序。在实际的应用中,我们可以将阻塞和非阻塞式 Socket 结合起来使用,以达到更高效的应用程序。

相关问题拓展阅读:

非阻塞socket总结

非阻塞socket设置方法:

fcntl(socket_fd, F_SETFL,  fcntl (socket_fd, F_GETFL,0) | O_NONBLOCK);

非阻塞模式下错误处理:

EAGAIN和EWOULDBLOCK(windows下)错误,这表明你在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,关于此错误一种说法是此错误表示目前无端口可用,另一种说法说的是发送缓冲区已满,遇到这两种错误不能当作错误处理,一种处理方法是采用延时处理稍后发送/接收,另一种是在类似poll/select/epoll中继续监听下次继续发送/接收,很显然之一种方法不可取,影响庆码竖性能。当发送大量数据时,可以通过缓存保存数据。如果出现EINTR错误,错误描述为Interrupted system call,操作也应该继续。如果recv的返回值为0,那表明连接已经断开,我们的接收操作也应该结束。

发送数据:

阻塞与非阻塞send返回值没有区分,

0,发送数据大小。

非阻塞模式下返回值 0,接收到数据大小,

非阻塞模式下返回 值 

阻塞模式下recv会阻塞着接收数据,非阻塞模式下如果没有数据会返回,不会阻塞着读,因此需要 循环读取。

连接:

TCP socket 被设为非阻塞后调用 connect ,connect 函数如果没有马上成功,会立即返回EINPROCESS(如果被中断模肢返回EINTR) ,但 TCP 的 3 次握手还在继续进行。之后可以用 select /epoll检查连接是否建立成功(但不能再次调用connect,这样会返回错误EADDRINUSE)。

非阻塞 connect 有誉大3 种用途:

(1). 在3 次握手的同时做一些其他的处理。

(2). 可以同时建立多个连接。

(3). 在利用 select/epoll 等待的时候,可以给 select/epoll 设定一个时间,从而可以缩短 connect 的超时时间。

使用非阻塞 connect 需要注意的问题是:

(1). 很可能 调用 connect 时会立即建立连接(比如,客户端和服务端在同一台机子上),必须处理这种情况。

(2). Posix 定义了两条与 select/epoll 和 非阻塞 connect 相关的规定:

连接成功建立时,socket 描述字变为可写。(连接建立时,写缓冲区空闲,所以可写)

连接建立失败时,socket 描述字既可读又可写。 (由于有未决的错误,从而可读又可写)

另外对于无连接的socket类型(SOCK_DGRAM),客户端也可以调用connect进行连接,此连接实际上并不建立类似SOCK_STREAM的连接,而仅仅是在本地保存了对端的地址,这样后续的读写操作可以默认以连接的对端为操作对象。

linux socket 连接超时 怎么解决

今天发现自己的系统存在很严重缺陷,当前台关闭的时候后台就无法正常工作,原因很好定位,后台的socket连接超时时间过长,系统默认时间好像是75秒,于是找资料,根据下边文章中的内容解决了,把超时时间设为5秒后,感觉好多了。看来还有好多东西需要慢慢挖掘阿!

如何设置socket的Connect超时(linux)

1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数

2.调用connect,正常情况下,因为TCP三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回EINPROGRESS,表示在建立连接但还没有世晌完成。

3.在读套接口描述符集(fd_set rset)和写套接口描述符集(fd_set wset)中将当前套接口置位(用FD_ZERO()、FD_SET()宏),并设置好超时时间(struct timeval *timeout)

4.调用select( socket, &rset, &wset, NULL, timeout )

返回0表示connect超时

如果你设置的超时时间大于75秒就没有必要这样做了,因为内核中对connect有超时限制就是75秒。

网络编程中socket的分量我想大家都很清楚了,socket也就是套接口,在套接口编程中,提到超时的概念,我们一下子就能想到3个:发送超时,接收超时,以及select超时(注: select函数并不是只用于套接口的,但是套接口编程中用的比较多),在connect到目标主机的时候,这个超时是不由我们来设置的。不过正常情况下这个超时都很长,并且connect又是一个阻塞方法,一个主机不能连接,等着connect返回还能忍受,你的程序要是要试图连接多个主机,恐怕遇到多个不能连接的主机的时候,会塞得你受不了的。我也废话少说,先说说我的方法,如果搜胡锋你觉得你已掌握这种方法,你就不用再看下去了,如果你还不了解,我愿意与你分享。本文是已在Linux下的程序为例子,不过拿到Windows中方法也是一样,无非是换几个函数名字罢了。

Linux中要给connect设置超时,应该是有两种方法的。一种是该系统的一些参数,这个方法我不讲,因为我讲不清楚:P,它也不是编程实现的。另外一种方法就是变相的实现connect的超时,我要讲的就是这个方法,原理上是这样的:

1.建立socket

2.将该socket设置为非阻塞模式

3.调用connect()

4.使用select()检查该socket描述符是否可写(注意,是可写)

5.根据select()返回的结果判断connect()结果

6.将socket设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)

如果你对网络编程很熟悉的话,其实我一说出这个过程你就知道怎么写你的程序了,下面给出我写的一段程序,仅供参考。

/******************************

* Time out for connect()

* Write by Kerl W

******************************/

#include 做液

#include

#define TIME_OUT_TIME 20 //connect超时时间20秒

int main(int argc , char **argv)

{

………………

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

if(sockfd 0)

{

getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);

if(error == 0) ret = true;

else ret = false;

} else ret = false;

}

else ret = true;

ul = 0;

ioctl(sockfd, FIONBIO, &ul); //设置为阻塞模式

if(!ret)

{

close( sockfd );

fprintf(stderr , “Cannot Connect the server!n”);

return;

}

fprintf( stderr , “Connected!n”);

//下面还可以进行发包收包操作

……………

}

以上代码片段,仅供参考,也是为初学者提供一些提示,主要用到的几个函数,select, ioctl, getsockopt都可以找到相关资料,具体用法我这里就不赘述了,你只需要在linux中轻轻的敲一个man 就能够看到它的用法。

此外我需要说明的几点是,虽然我们用ioctl把套接口设置为非阻塞模式,不过select本身是阻塞的,阻塞的时间就是其超时的时间由调用select 的时候的最后一个参数timeval类型的变量指针指向的timeval结构变量来决定的,timeval结构由一个表示秒数的和一个表示微秒数(long类型)的成员组成,一般我们设置了秒数就行了,把微妙数设为0(注:1秒等于100万微秒)。而select函数另一个值得一提的参数就是上面我们用到的fd_set类型的变量指针。调用之前,这个变量里面存了要用select来检查的描述符,调用之后,针对上面的程序这里面是可写的描述符,我们可以用宏FD_ISSET来检查某个描述符是否在其中。由于我这里只有一个套接口描述符,我就没有使用FD_ISSET宏来检查调用select之后这个sockfd是否在set里面,其实是需要加上这个判断的。不过我用了getsockopt来检查,这样才可以判断出这个套接口是否是真的连接上了,因为我们只是变相的用select来检查它是否连接上了,实际上select检查的是它是否可写,而对于可写,是针对以下三种条件任一条件满足时都表示可写的:

1)套接口发送缓冲区中的可用控件字节数大于等于套接口发送缓冲区低潮限度的当前值,且或者i)套接口已连接,或者ii)套接口不要求连接(UDP方式的)

2)连接的写这一半关闭。

3)有一个套接口错误待处理。

这样,我们就需要用getsockopt函数来获取套接口目前的一些信息来判断是否真的是连接上了,没有连接上的时候还能给出发生了什么错误,当然我程序中并没有标出那么多状态,只是简单的表示可连接/不可连接。

下面我来谈谈对这个程序测试的结果。我针对3种情形做了测试:

1. 目标机器网络正常的情况

可以连接到目标主机,并能成功以阻塞方式进行发包收包作业。

2. 目标机器网络断开的情况

在等待设置的超时时间(上面的程序中为20秒)后,显示目标主机不能连接。

3. 程序运行前断开目标机器网络,超时时间内,恢复目标机器的网络

在恢复目标主机网络连接之前,程序一只等待,恢复目标主机后,程序显示连接目标主机成功,并能成功以阻塞方式进行发包收包作业。

关于linux socket 阻塞 非阻塞的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » Linux Socket阻塞与非阻塞:网络编程中的关键区别 (linux socket 阻塞 非阻塞)