當前位置:律師網大全 - 專利申請 - Java異步編程

Java異步編程

用異步iostream編寫Socket進程通信程序

在Merlin中增加了實現異步輸入輸出機制的應用程序接口包:java.nio(壹個新的輸入輸出包,定義了很多基本類型的緩沖區),java.nio.channels(通道和選擇器等。,用於異步輸入和輸出),以及java.nio.charset(字符的編碼和解碼)。壹個通道首先在選擇器中註冊它感興趣的事件,當相應的事件發生時,選擇器通過SelectionKey通知註冊的通道。然後通道通過緩沖區將待處理的信息打包,進行編碼/解碼,完成輸入輸出控制。

渠道介紹:

本文主要介紹ServerSocketChannel和SocketChannel。兩者都是可選通道,可以分別工作在同步和異步兩種模式(註意,這裏的可選不是指可以選擇兩種工作模式,而是可以選擇性地註冊自己感興趣的事件)。妳可以使用頻道。ConfigureBlocking (Boolean)設置其工作模式。與以前版本的API相比,ServerSocketChannel相當於ServerSocket,SocketChannel相當於Socket。當通道工作在同步模式時,編程方法基本和前面類似,這裏主要介紹異步工作模式。

所謂異步I/O機制,就是在處理I/O的時候,我不用等到I/O完成了再返回。因此,異步同義詞沒有阻塞。在服務器端,ServerSocketChannel通過靜態函數open()返回壹個實例serverChl。然後通道調用serverChl.socket()。bind()綁定到服務器端口,並調用register (selector sel,selectionkey。OP_ACCEPT)在選擇器中註冊OP_ACCEPT事件(ServerSocketChannel只能註冊op _ accept事件)。當有客戶請求連接時,選擇器會通知通道有客戶連接請求,從而進行相應的輸入輸出控制;在客戶端,clientChl實例註冊了自己感興趣的事件(可以是op _ connect、op _ read和op _ write的組合)後,調用客戶端Chl。Connect (inetsocketaddress)連接到服務器,然後對其進行相應的處理。註意,這裏的連接是異步的,也就是說,它會立即返回並繼續執行下面的代碼。

選擇器和選擇鍵介紹:

選擇器的作用是將通道感興趣的事件放入隊列,而不是立即提交給應用程序,等待註冊的通道請求處理這些事件。換句話說,選擇器將隨時報告準備好的通道,並且按照先進先出的順序。那麽,選擇器通過什麽來報告呢?選擇鍵。選擇鍵的功能是指示哪個通道準備好了以及做什麽。妳可能會馬上想到,這壹定是註冊頻道感興趣的事件。可以,比如對於服務器端的serverChl,可以調用key.isAcceptable()通知serverChl有客戶端連接請求。對應的功能有:選擇鍵。isreadable(),selectionkey。isreatable()。通常,感興趣的事件在壹個循環中被輪詢(有關詳細信息,請參考下面的代碼)。如果選擇器中沒有發生通道註冊事件,調用Selector.select()將被阻塞,直到事件發生。此外,還可以調用selectNow()或select(長超時)。前者立即返回,無事件時返回值0;後者等待超時並返回。壹個選擇器可以被多達63個通道同時使用。

應用示例:

下面是壹個異步輸入/輸出機制實現的客戶機/服務器示例程序——程序列表1(限於篇幅,僅給出服務器端實現,讀者可參考客戶端代碼):

程序類圖

公共類NBlockingServer {

int port = 8000

int buffer size = 1024;

選擇器selector = null

serversocket channel server channel = null;

HashMap clientChannelMap = null//用於存儲每個客戶端連接對應的套接字和通道。

公共NBlockingServer( int端口){

this . clientchannelmap = new HashMap();

this.port = port

}

公共void initialize()引發IOException {

//初始化:分別實例化壹個選擇器,壹個服務器可以選擇頻道。

this.selector =選擇器. open();

this . server channel = serversocketchannel . open();

this . server channel . configure blocking(false);

inet address localhost = inet address . get localhost();

InetSocketAddress isa = new InetSocketAddress(localhost,this . port);

this.serverChannel.socket()。bind(isa);//將套接字綁定到服務器的可用端口。

}

//最後釋放資源

公共void finalize()引發IOException {

this . server channel . close();

this.selector.close()。

}

//解碼讀入字節緩沖區的信息

公共字符串解碼(ByteBuffer byteBuffer)引發

字符編碼異常{

charset charset = charset . forname(" ISO-8859-1 ");

charset decoder decoder = charset . new decoder();

char buffer char buffer = decoder . decode(byte buffer);

字符串result = char buffer . tostring();

返回結果;

}

//監聽端口,當通道準備好時執行相應的操作。

public void portListening()拋出IOException,InterruptedException {

//服務器端通道註冊OP_ACCEPT事件。

selection key accept key = this . server channel . register(this . selector,

選擇鍵。OP _ ACCEPT);

//當註冊的事件發生時,select()的返回值將大於0。

while (acceptKey.selector()。select()& gt;0 ) {

System.out.println("事件發生");

//獲取所有準備好的選擇鍵。

set readyKeys = this . selector . selected keys();

//使用叠代器輪詢選擇鍵。

叠代器I = readykeys . iterator();

而(我

Else if (key.isReadable()) {//如果是通道讀就緒事件,

System.out.println("可讀");

//獲取選擇鍵對應的通道和套接字。

SelectableChannel nextReady =

(SelectableChannel)key . channel();

Socket Socket =(Socket)key . attachment();

//處理此事件,處理方法已經封裝在ClientChInstance類中。

this . readfromchannel(socket . get channel(),

(客戶端實例)

this . clientchannelmap . get(socket));

}

else If(key . is write able()){//如果是通道寫就緒事件,

system . out . println(" writeable ");

//套接字獲取後的後處理,如上。

Socket Socket =(Socket)key . attachment();

socket channel channel =(socket channel)

socket . get channel();

this.writeToChannel( channel,“這是來自服務器的!”);

}

}

}

}

//對通道的寫操作

public void writeToChannel(socket channel通道,字符串消息)

引發IOException {

byte buffer buf = byte buffer . wrap(message . getbytes());

int nbytes = channel . write(buf);

}

//通道上的讀取操作

public void readFromChannel(socket channel channel,clientchininstance client instance)

引發IOException,InterruptedException {

byte buffer byte buffer = byte buffer . allocate(buffer size);

int nbytes = channel . read(byte buffer);

byte buffer . flip();

字符串結果= this . decode(byte buffer);

//當客戶端發出“@exit”退出命令時,關閉其通道。

if(result . index of(" @ exit ")& gt;= 0 ) {

channel . close();

}

否則{

client instance . append(result . tostring());

//讀取壹行後,執行相應的操作。

if(result . index of(" \ n ")& gt;= 0 ){

System.out.println("客戶端輸入"+結果);

client instance . execute();

}

}

}

//這個類封裝了如何操作客戶端的通道,可以通過重載execute()方法來實現。

公共類ClientChInstance {

SocketChannel通道;

string buffer buffer = new string buffer();

公共客戶端實例(SocketChannel通道){

this.channel =頻道;

}

public void execute()拋出IOException {

String message = "這是從通道讀取後的響應!";

writeToChannel( this.channel,message);

buffer = new string buffer();

}

//當壹行沒有結束時,將當前字放在緩沖區的末尾。

公共void追加(字符串值){

buffer.append(值);

}

}

//主程序

公共靜態void main( String[] args ) {

NBlockingServer nbServer = new NBlockingServer(8000);

嘗試{

nbserver . initialize();

} catch(異常e ) {

e . printstacktrace();

system . exit(-1);

}

嘗試{

nbserver . port listening();

}

捕捉(異常e ) {

e . printstacktrace();

}

}

}

節目列表1

總結:

從上面的程序段可以看出,服務器在沒有引入冗余線程的情況下,完成了多客戶端的客戶端/服務器模式。在這個程序中使用了回調模式。需要註意的是,請不要把原來的I/O包和新添加的I/O包混在壹起,因為兩個包由於某些原因是不兼容的。即在使用通道時,請使用緩沖區來完成輸入輸出控制。該程序在Windows 2000和J2SE1.4下通過telnet測試成功。

  • 上一篇:沒量產就開店,賈躍亭何時能靠FF91翻身?
  • 下一篇:Mammut(猛獁象)比arcteryx(始祖鳥)更厲害。求專家解答。
  • copyright 2024律師網大全