python3socket非阻塞在linux里无效_网络编程Socket的阻塞和非阻塞IO

网络应用程序一个很重要的工作是传输数据。传输数据的过程不一样取决于使用哪种“交通工具“,但是传输的方式都是一样的:都是以字节码传输。JAVA开发网络程序传输数据的过程和方式是被抽象了的,我们不需要关注底层接口,只需要使用Java API 或其他网络框架就能达到数据传输的目的。发送数据和接收数据都是字节码。

Socket网络编程我就不多啰嗦了,这里我通过两个简单的示例比较下阻塞式IO(OIO)和非阻塞式IO(NIO)。

OIO中,每个线程只能处理一个channel,该线程和该channel绑定。也就是同步的,客户端在发送请求后,必须得在服务端有回应后才发送下一个请求。所以这个时候的所有请求将会在服务端得到同步。

NIO中,每个线程可以处理多个channel。也就是异步的,客户端在发送请求后,不必等待服务端的回应就可以发送下一个请求,这样对于所有的请求动作来说将会在服务端得到异步,这条请求的链路就象是一个请求队列,所有的动作在这里不会得到同步的。

你可能使用过Java提供的网络接口工作过,遇到过想从阻塞传输切换到非阻塞传输的情况,这种情况是比较困难的,因为阻塞IO和非阻塞IO使用的API有很大的差异。当我们想切换传输方式时要花很大的精力和时间来重构代码。

先看一个传统的阻塞IO传输实现的Socket服务端:

/**

*    传统阻塞IO(OIO),原始socket

*

*   

Title: PlainOioServer

*    @author wyx

*    @date 2016-6-15 下午1:36:04

*/

public class PlainOioServer {

public void server(int port) throws Exception{

// bind server to port

final ServerSocket socket = new ServerSocket(port);

while(true){

// accept connection

final Socket clientSocket = socket.accept();

System.out.println("Accepted connection form " + clientSocket);

// create new thread to handle connection

new Thread(new Runnable() {

@Override

public void run() {

OutputStream out;

try {

out = clientSocket.getOutputStream();

// write  message to connected client

out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8")));

out.flush();

// close connection once message written and flushed

clientSocket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}).start(); // start thread to begin handling

}

}

}

上面的方式很简洁,但是这种阻塞模式在大连接的情况就会有严重的问题,如:客户端连接超时,服务器响应严重延迟等。为了解决这一问题,我们可以使用异步网络处理所有的并发连接,但问题在于NIO和OIO的API是完全不同的,所以一个用OIO开发的网络应用程序想要使用NIO重构代码几乎是重新开发。

下面代码是使用Java NIO实现的例子:

/**

*    传统非阻塞式IO(NIO),原始socket

*

*   

Title: PlainNioServer

*    @author wyx

*    @date 2016-6-15 下午1:46:09

*/

public class PlainNioServer {

public void server(int port) throws Exception{

System.out.println("Listening for connections on port " + port);

// open selector that handles channels

Selector selector = Selector.open();

// open ServerSocketChannel

ServerSocketChannel serverChannel = ServerSocketChannel.open();

// get ServerSocket

ServerSocket serverSocket = serverChannel.socket();

// bind server to port

serverSocket.bind(new InetSocketAddress(port));

// set to non-blocking

serverChannel.configureBlocking(false);

// register ServerSocket to selector and specify than it is interested in new accepted clients

serverChannel.register(selector, SelectionKey.OP_ACCEPT);

final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());

while(true){

// Wait for new events that are ready for process. this will block until something happens

int n = selector.select();

if(n > 0){

// Obtain all SelectionKey instances that received enents

Iterator iter = selector.selectedKeys().iterator();

while(iter.hasNext()){

SelectionKey key = iter.next();

iter.remove();

//Check if event was because new client ready to get accepted

if(key.isAcceptable()){

ServerSocketChannel server = (ServerSocketChannel) key.channel();

SocketChannel client = server.accept();

System.out.println("Accepted connection from " + client);

client.configureBlocking(false);

// Accept client and register it to seletor

client.register(selector, SelectionKey.OP_WRITE, msg.duplicate());

}

// Check if event was because socket is ready to write data

if(key.isWritable()){

SocketChannel client = (SocketChannel) key.channel();

ByteBuffer buff = (ByteBuffer) key.attachment();

// Write date to connected client

while(buff.hasRemaining()){

if(client.write(buff) == 0){

break;

}

}

client.close();

}

}

}

}

}

}

如你所见,即使它们实现的功能时候一样的,但是代码完全不同。根据不同需求选用不同的实现方式,当然,也可以直接选择流行的网络传输框架实现,如:Netty。以便于后期维护。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值