`

Socket I/O

    博客分类:
  • Java
 
阅读更多

代表IP位址的類別 -  InetAddress
以往我們在做Java Network程式設計時慣用java.net.InetAddress類別來
代表IP位址。我們只要呼叫InetAddress.getByName()這個方法,參數傳入
對方的IP或hostname就可以得到InetAddress的instance。

InetAddress ipAddr = InetAddress.getByName(“www.nccu.edu.tw”);

-JDK1.4的新類別 - InetSocketAddress

JDK1.4中,在java.net這個Package中新出現了一個類別,叫做InetSocketAddress。
根據JavaDoc的說明,它其實就是「IP位址+Port number」。我們可以看一下
InetSocketAddress的原始碼中在Class scope中宣告了三個屬性

public class InetSocketAddress extends SocketAddress {

    private String hostname = null;

    private InetAddress addr = null;

    private int port;
    .......(下略).......
}

從這裏可以發現,原來InetSocketAddress由真如JavaDoc所言,它只是把
InetAddress包起來,外加port number。InetSocketAddress是extend
SocketAddress類別,其實SocketAddress這個class中什麼也沒有,它所代
表的意思是說,如果要支援什麼協定,只要extend SocketAddress類別來
實作即可。如InetSocketAddress就是專門用在TCP/IP協定來定址的類別。

-InetSocketAddress的建構子有三個

InetSocketAddress(InetAddress addr, int port)
InetSocketAddress(String hostname, int port)
InetSocketAddress(int port)

所以我們在產生InetSocketAddress時,除了直接傳入InetAddress及port之
外,也可以直接傳入hostname。第三個因為沒有指定連結對象,所以是在寫
erver時才能用。

-利用SocketChannel連結伺服器
一般Network Programming會有二種方式,一種是我們寫作client side程式
去和已存在的server溝通。另一種就是我們寫server,再bind到某個port等
待別人來連(listen)。這裏先探討比較簡單的client side情況。

JDK1.4之前我們都是先從IP/Hostname得到一個InetAddress如

InetAddress ipAddr = InetAddress.getByName(“www.nccu.edu.tw”);

得到ip後,再把它和當做Socket類別建構子的參數傳入,這個時候才指定port
number(在這個例子中是8080)。

Socket socket = new Socket(ipAddr,8080);


這樣就可以和Server連上,連上後要取得資料的話,呼叫

socket.getInputStream();

這個method即可取得InputStream。
要用New I/O的SocketChannel來做的話,必須經過以下步驟
  
一、建立一個InetSocketAddress的instance。

InetSocketAddress socketAddr = new InetSocketAddress( www.nccu.edu.tw ,80);

二、利用SocketChannel.open();這個static method,建立一個SocketChannel的instance。

SocketChannel socketChannel = SocketChannel.open();

三、建立一個ByteBuffer的instance。

ByteBuffer buf = ByteBuffer.allocateDirect(1024);

四、利用SocketChannel的connect()方法來建立連結。

socketChannel.connect(socketAddr);

五、利用SocketChannel的read()方法把資料讀入ByteBuffer中。

socketChannel.read(buf);

再來就用(上)篇所提到的Buffer家族操作方式去操作即可。

Selector及SelectorProvider
在JDK1.4之後,總算可以達到Non-blocking的功能了。在前面我們其實是
利用SocketChannel類別以blocking方式來實作。SocketChannel類別還支
援Non-blocking的寫法,為什麼呢,因為這個類別除了implement ByteChannel
(可讀可寫)、ScatteringByteChannel(可讀區塊)、GatheringByteChannel
(可寫區塊)這三個類別之外,它本身是extend自AbstractSelectableChannel
這個類別。AbstractSelectableChannel是implement SelectableChannel。
SelectableChannel的子類別,都有一個method叫做register(),可以向
Selector註冊。
  
Selector在non-blocking中扮演關鍵性的角色。因為non-blocking的方式
之下,我們可以不用一直block在那邊等待網路另一端的回應,可以先做別
的事。當然網路另一端有回應時,必須有人通知我們。我們可以向Selector
註冊Channel及我們有興趣的事件。當這些事件發生時,Selector就會通知
我們。它傳回一組SelectionKey,這些key有一個method叫channel(),我們
從這個method就可以再度取我們剛註冊的channel,並加以處理了。

Selector這個類別是Abstract,這暗示我們,其實可能還有很多種Selector,
可以利用extend Selector類別的方式去擴充它的功能,我們在下面會印証
這個推論。

    取得Selector instance的方式是
  
Selector selector = Selector.open();

  利用open()這個靜態方法可以取得Selector的instance。
  觀察open()方法的內部,它其實是
  
  return SelectorProvider.provider().openSelector();
  

看到這裏,我們發現,其實為了可擴充性,它另外還制定了SelectorProvider
這個類別,來提供不同種類的Selector,果然印証之前的推論。

如果看SelectorProvider的原始碼,就會發現真象。它的過程如下:
Selector的open()呼叫SelectorProvider,要求它提供一個Selector的instance。
這時候SelectorProvider會檢查系統參數是否有

java.nio.channels.spi.SelectorProvider=???

這一個參數被定義,如果有,就laod那個SelectorProvider的類別來提供Selector。
如果沒有,就load一個名叫「DefaultSelectorProvider」的SelectorProvider。
其實DefaultSelectorProvider在原始碼中是設成「PollSelectorProvider」,這
代表很重要的意義,因為我們發現原來預設的SelectorProvider就是PollSelectorProvider,
而且從它的名字也可以推論出它的行為是「Poll(輪詢)」,而它提供的正是
「PollSelector」。更明確地說,PollSelector是JDK1.4目前唯一提供的Selector。

由上可知Seletor本身是一個Abstract類別,我們真正用到的類別其實是
PollSelectorImpl這個子類別。它們的關係如下。
  
public int select(long timeout)探討

Selector要怎麼通知我們說某某event已經發生呢?Selector有一個method叫做
select(),這個method的傳回值是int。如果有我們註冊的事情發生了,它的
傳回值就會大於0。說得明確一點,它傳回的是事件的數目。所以通常我們會
寫一個while迴圈來偵測。

while(selector.select(500)>0)
{
.......(do something)...
}

各位一定注意到了select()這個method有一個參數,我們傳了500進去,這個參
數是long,代表timeout。要確實了解這個timeout的意思,必須深入探討select()
這一個method,不要小看這個timeout,它可是被原封不動一直傳到最底層的Win32
API去哦,而且這個select()method是non-blocking I/O的關鍵所在。

不能免俗,我們還是要追查JDK的原始碼,由於PollSelector是目前唯一有實作的

Selector,所以以下都是以PollSelector的做法為主。我們發現,Select()這個
method在做完前置處理後,會call一個叫做doSelect(long  timeout)的method,
順便直接把timeout傳下去。再深入追查下去,會發現doSelect(long  timeout)
其實是call PollWrapper類別中一個名叫poll()的method。這個poll()類別對映
到Native Call,它呼叫的是WSAWaitForMultipleEvent()這個Win32 API(為了簡
化起見,這裏只討論Windows平台上的請況)。
  
查詢MSDN,發現WSAWaitForMultipleEvent()這個Win32 API有五個參數,第四個
就是timeout。因為timeout這個參數是一路傳下來,所以看到這裏,大家應該了
解它其實和這個Windows API的timeout的意義一樣,timeout之後,它就會直接
return WSA_WAIT_TIMEOUT這個傳回值(DWORD)。在java的觀點來看,在timeout時
間過了之後,它會直接進行下一行,並傳回0。
  
但是有個例外,JavaDoc上說,timeout的參數不能是負數,如果我們傳負數會怎
麼樣?會throw一個IllegalArgumentException。如果我們傳0的話,它會自動幫我
們改成-1,再傳到doSelect()中。在PollArrayWrapper.c中有一行是這樣寫的
  
  if (timeout < 0) {
          timeout = WSA_INFINITE;
  }
  
嗯,相信大家應該已經看出來,我們傳0的會發生什麼事吧?


利用SocketChannel連結伺服器(Non-Blocking)

討論完selector之後,我們要開始寫Non-Blocking的網路應用程式了,下面有幾
個步驟要注意一下。

前半段的寫法大致都一樣,利用SocketChannel連結到你的InetSocketAddress上。
但是要利用configureBlocking()告訴系統說我們要用non-blocking的方式。

InetSocketAddress socketAddr = new InetSocketAddress( www.nccu.edu.tw ,80);
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(socketAddr);

再來要得到一個Selector的instance,然後向這個selector註冊你的channel。

Selector selector = selector.open();
socketChannel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);

  發生事件之後,selector會把事件以SelectionKey的方式傳回來,
因為可能會有好幾個,所以它是放到一個Set中。因此我們會有二層while迴圈,
外層是等待event發生,內層是發生之後利用iterator來一一檢查SelectionKey,
針對這些event,我們再一一取出channel來做處理。由於我們註冊了OP_CONNECT
事件及OP_READ事件,所以利用if判別出來之後,就可以利用 key.channel得到
的SocketChannel去處理了。其它部份就比照前面所提Blocking方式處理即可。

while (selector.select(500) > 0)
{
  //取得SelectionKeys
  Set readyKeys = selector.selectedKeys();
  Iterator readyItor = readyKeys.iterator();
 //利用iterator來一一檢查SelectionKey
  while (readyItor.hasNext())
  {
     SelectionKey key = (SelectionKey)readyItor.next();
     readyItor.remove();//務必記得這一行
   //得到SocketChannel了!!
     SocketChannel keyChannel = (SocketChannel)key.channel();
    if (key.isConnectable()) {
       //在這裏處理OP_CONNECT事件
    } else if (key.isReadable()) {
       //在這裏處理OP_READ事件
    }
  }
}

結論

在這一篇文章中我們詳盡地介紹了Seletor,也實作了一個Blocking及non-blocking
的Client Networking程式。為了簡化討論範圍,Server的寫法請各位自行參考相關
資料,但基本的觀念都是雷同的。Java.nio Package也許是JDK1.4中最引人注目的
新功能,藉由native methd的實作,再加上non-blocking方式的支援,在效能上應有
不錯的表現。比較令人擔心的是,native call的大量引用,代表的是要JDK支援特定
的平台時,很可能要針該平台改寫的Code就變多了。目前JDK有win32、Solaris、linux
三種版本。如果別的平台(像FreeBSD)要支援的話,要port的人可就累了~~^.^。

分享到:
评论

相关推荐

    windows下六种socket I/O模型示例

    在Windows操作系统中,进行网络编程时,我们常常会遇到多种Socket I/O模型。这些模型决定了如何处理输入/输出操作,从而影响程序的性能和效率。本文将深入探讨六种主要的Socket I/O模型,并通过实例解析它们的工作...

    Socket I/O 模型的使用示例

    在本文中,我们将深入探讨异步Socket I/O模型的几种常见实现方式,包括选择(select)、异步选择(asynchronous select)、事件选择(poll)、重叠I/O(overlapped I/O)以及完成端口(completion ports)。...

    Winodws Socket I/O模型 电子书及附套代码

    在Windows操作系统中,Socket I/O模型是网络编程的重要组成部分,它允许应用程序进行网络通信。本资源包含一本关于“Windows Socket I/O模型”的电子书以及配套的源代码,旨在帮助开发者深入理解并掌握在Windows环境...

    介绍当前Windows支持的各种Socket I/O模型

    ### 当前Windows支持的各种Socket I/O模型 在现代操作系统中,网络编程是不可或缺的一部分,而Socket I/O模型则是实现高效网络通信的关键技术之一。本文将详细介绍当前Windows系统中支持的各种Socket I/O模型,包括...

    实验8-Windows Socket I/O模型

    1) 掌握Winsock I/O模型工作原理; 2) 熟悉I/O模型中使用的Winsock接口函数; 3) 掌握使用I/O模型进行网络程序设计的编程步骤; 实验要求: 在上述I/O模型中自选一个I/O模型,构建一个TCP服务器,该服务器能: 能显示...

    socket I/O模型源代码

    本文将深入探讨五种主要的套接字I/O模型:阻塞I/O、非阻塞I/O、I/O复用(select/poll/epoll)、信号驱动I/O以及异步I/O,同时通过提供的源代码文件,我们可以更直观地理解这些模型的工作原理。 1. **阻塞I/O模型**...

    SOCKET I/O模式分析比较

    对SOCKET的I/O模式进行代码级分析 并进行性能差异比较

    Windows Socket五种I/O模型——代码全攻略

    ### Windows Socket五种I/O模型详解 在计算机网络编程中,Windows Socket(Winsock)是实现网络通信的重要机制之一。为了高效地处理网络数据传输,Winsock提供了多种输入/输出(I/O)模型来满足不同场景下的需求。...

    windows下7种winsocket的I/O编程模型

    windows下7种winsocket的I/O编程模型的源代码,均为可直接运行代码,有详细注释。 每一种I/O模型的详细编程步骤可以参考配套文章:http://blog.csdn.net/u011787119/article/details/46848391

    C++实现采用事件通知的socket重叠I/O模型代码

    本文将深入探讨如何使用C++实现采用事件通知的socket重叠I/O模型,这一模型在处理大量并发连接时具有显著优势。 首先,我们需要理解“重叠I/O”(又称异步I/O)的概念。在传统的同步I/O模型中,一个调用会阻塞直到I...

    Java I/O, 2nd Edition

    《Java I/O, 2nd Edition》是一本深入探讨Java输入/输出系统的重要书籍,针对Java平台的I/O系统提供了全面且深入的指南。这本书在第二版中对Java I/O进行了更新,涵盖了从Java 5到Java 8的最新发展,包括NIO.2(New ...

    幽默Socket+IO模型.doc幽默Socket+IO模型.doc

    从给定的文档片段来看,主要讨论了网络编程中的Socket与不同I/O模型的应用,特别是对Windows环境下Socket的I/O模型进行了深入探讨。下面将详细解释文档中提及的关键知识点: ### Socket与I/O模型 #### Socket简介 ...

    cpp-httplib

    NOTE: This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want. A C++11 single-file header-only cross platform ...

    基于Java的socket I_O流技术.pdf

    Java网络编程中的Socket I/O流技术是一门关键技术,其涉及到网络数据的传输、服务器和客户端的交互,以及如何高效地利用资源。在传统的BIO(Blocking I/O)技术中,服务器为了处理每个客户端的请求,都必须为每个...

    详解socket阻塞与非阻塞,同步与异步、I/O模型

    这里我们将详细探讨这两个概念以及I/O模型。 首先,同步与异步是客户端(C端)调用服务端(S端)时的行为模式。同步模式下,客户端在调用一个功能后,必须等待该功能执行完毕并返回结果,才能进行下一步操作。这就...

Global site tag (gtag.js) - Google Analytics