Java Socket IO(BIO、NIO)


總結下Java socket IO。首先是各種IO的定義,這個定義似乎也是眾說紛紜。我按照stackoverflow上面的解釋:

IO有兩種分法:按照阻塞或者按照同步。按照阻塞,有阻塞IO和非阻塞IO。按照同步就是同步IO或者異步IO。我們可以認為阻塞IO和同步IO相等,而非阻塞IO和異步IO不同。

阻塞IO或者同步IO是指:IO的請求發出去之后,請求者一直在等待回復,當IO的數據回來到來之后,請求者就開始接受數據。阻塞的意思就是:IO請求發出去之后請求線程就停止在那里,一直等待數據到來。

非阻塞則是:IO請求發出去之后,會立刻收到回復,這個回復可能是IO可以立即進行,或者是無法進行IO的錯誤。所以非阻塞IO請求者必須一直調用那個API,一直到API返回可以進行IO的信號。

 

異步IO:IO請求發出之后,后台會建立一個進程,處理IO,當IO都處理完之后,把處理完的數據交給IO的請求者。

本文用Java socket實現阻塞和非阻塞IO(這里大部分內容都學習自千與的專欄,這是位大牛,居然hadoop源碼分析寫了19篇博客)。然后本文章的所有代碼我都放在Github上了。

阻塞IO

阻塞IO比較簡單,就是用普通的socket去寫,因為沒有什么太復雜的處理。建立一個socket,然后,獲取它的inputstream和outputstream,然后進行讀寫操作。

Server端主要代碼,handleSocket就是從socket里面讀取數據,然后向client寫數據:

	ServerSocket serverSocket = new ServerSocket(port); while (true) { socket = serverSocket.accept(); handleSocket(socket); }

Client只是簡單的發送一句消息:

	socket = new Socket(host, port); out = socket.getOutputStream(); out.write(data); out.flush(); in = socket.getInputStream(); byte[] buffer = new byte[128]; int receivedBytes; if((receivedBytes = in.read(buffer))!=-1){ System.out.println("Client: received msg from server: " + new String(buffer, 0, receivedBytes)); }

同時client5000個,使用了大概5s。然后還可以使用多線程Server,就是在每一個client到的時候分配一個線程來處理這個IO,理論上可以增加效率,但是似乎是因為每次處理時間太短,效果不明顯。主要代碼:

	boolean flag = false; int count = 0; ServerSocket serverSocket = new ServerSocket(port); Date start = null; while (true) { socket = serverSocket.accept(); if (!flag) { start = new Date(); flag = true; } pool.execute(new RequestHandler(socket)); if(++count== threadCount){ flag = false; Date end = new Date(); System.out.println(threadCount+" client requests spends: " + (end.getTime() -start.getTime())); } }

其中的pool是ExecutorService,提供線程池,然后RequestHandler是一個Runnable類,用來處理一個socket連接。簡單講來,就是把前面server處理socket的代碼放到了這個handler里面。

NIO,非阻塞IO

我認定Java的NIO包實現的是同步非阻塞IO,也有人說不是,至少我這里這么認為。在server端,首先打開一個channel,然后向其中注冊一個selector,這個selector會在channel中輪詢注冊的事件,然后根據事件的類型作出處理。一般事件的類型有Accept、read、write。

在Client端也可以是一樣的注冊,然后通過和server同樣的處理方式處理事件(但是沒有accept事件,因為只有server才能accept的)。但是我的例子里面只是簡單的發送了一條消息。

Server的主要代碼:

	Selector selector = Selector.open(); ServerSocketChannel serverSocketChannel = ServerSocketChannel .open(); serverSocketChannel.configureBlocking(false); serverSocketChannel.socket().bind(address); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); log.info("Server: socket server started!"); while (true) { int nKeys = selector.select(); if (nKeys > 0) { Set selectedKeys = selector.selectedKeys(); Iterator it = selectedKeys.iterator(); while (it.hasNext()) { SelectionKey key = (SelectionKey) it.next(); if (key.isAcceptable()) { log.info("Server: Selection key is acceptable"); handler.handleAccept(key); } else if (key.isReadable()) { log.info("Server: Selection key is readable"); handler.handleRead(key); } else if (key.isWritable()) { log.info("Server: Selection key is writable"); handler.handleWrite(key); } it.remove(); } } }

其中的handler就是處理每種類型的事件的類,舉個Read的例子:

public void handleRead(SelectionKey key) throws IOException { ByteBuffer byteBuffer = ByteBuffer.allocate(512); SocketChannel socketChannel = (SocketChannel) key .channel(); while (true) { int readBytes = socketChannel.read(byteBuffer); if (readBytes > 0) { log.info("Server: readBytes = " + readBytes); log.info("Server: data = " + new String(byteBuffer.array(), 0, readBytes)); byteBuffer.flip(); socketChannel.write(byteBuffer); break; } } socketChannel.close(); }

Client就比較簡單了,就是用channel直接發送了一句消息:

public void send(String data) { try { SocketChannel socketChannel = SocketChannel.open(inetSocketAddress); socketChannel.configureBlocking(false); ByteBuffer byteBuffer = ByteBuffer.allocate(512); socketChannel.write(ByteBuffer.wrap(data.getBytes())); while (true) { byteBuffer.clear(); int readBytes = socketChannel.read(byteBuffer); if (readBytes > 0) { byteBuffer.flip(); log.info("Client: readBytes = " + readBytes); log.info("Client: data = " + byteBuffer.toString()); socketChannel.close(); break; } } } catch (IOException e) { e.printStackTrace(); } }

最后,這些Java的socket編程在實際工程中已經很少使用了,由於Unix的poll等功能的出現,select相比較之下,有點弱了。接下來可以考慮研究下。

NIO包的API介紹:

http://wufan0023.iteye.com/blog/198722

http://wufan0023.iteye.com/blog/198710

    分享到:

This entry was posted in Java topic and tagged Javasocket by 柳浪聞鶯. Bookmark the permalink.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM