激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java NIO服務器端開發詳解

Java NIO服務器端開發詳解

2021-03-02 10:44溫布利往事 Java教程

這篇文章主要介紹了Java NIO服務器端開發詳解,具有一定借鑒價值,需要的朋友可以參考下。

一、nio類庫簡介

  1、緩沖區buffer

  buffer是一個對象,包含一些要寫入和讀出的數據。

  在nio中,所有的數據都是用緩沖區處理的,讀取數據時,它是從通道(channel)直接讀到緩沖區中,在寫入數據時,也是從緩沖區寫入到通道。

  緩沖區實質上是一個數組,通常是一個字節數組(bytebuffer),也可以是其它類型的數組,此外緩沖區還提供了對數據的結構化訪問以及維護讀寫位置等信息。

  buffer類的繼承關系如下圖所示:

Java NIO服務器端開發詳解

2、通道channel

  channel是一個通道,網絡數據通過channel讀取和寫入。通道和流的不同之處在于通道是雙向的(通道可以用于讀、寫后者二者同時進行),流只是在一個方向上移動。

  channel大體上可以分為兩類:用于網絡讀寫的selectablechannel(serversocketchannel和socketchannel就是其子類)、用于文件操作的filechannel。

  下面的例子給出通過filechannel來向文件中寫入數據、從文件中讀取數據,將文件數據拷貝到另一個文件中:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public class niotest
{
  public static void main(string[] args) throws ioexception
  {
    copyfile();
  }
  //拷貝文件
  private static void copyfile()
  {
    fileinputstream in=null;
    fileoutputstream out=null;
    try
    {
      in=new fileinputstream("src/main/java/data/in-data.txt");
      out=new fileoutputstream("src/main/java/data/out-data.txt");
      filechannel inchannel=in.getchannel();
      filechannel outchannel=out.getchannel();
      bytebuffer buffer=bytebuffer.allocate(1024);
      int bytesread = inchannel.read(buffer);
      while (bytesread!=-1)
      {
        buffer.flip();
        outchannel.write(buffer);
        buffer.clear();
        bytesread = inchannel.read(buffer);
      }
    }
    catch (filenotfoundexception e)
    {
      // todo auto-generated catch block
      e.printstacktrace();
    } catch (ioexception e)
    {
      // todo auto-generated catch block
      e.printstacktrace();
    
  }
  //寫文件
  private static void writefilenio()
  {
    try
    {
      randomaccessfile fout = new randomaccessfile("src/main/java/data/nio-data.txt", "rw");
      filechannel fc=fout.getchannel();
      bytebuffer buffer=bytebuffer.allocate(1024);
      buffer.put("hi123".getbytes());
      buffer.flip();
      try
      {
        fc.write(buffer);
      } catch (ioexception e)
      {
        // todo auto-generated catch block
        e.printstacktrace();
      }
    }
    catch (filenotfoundexception e)
    {
      // todo auto-generated catch block
      e.printstacktrace();
    }
  }
  //讀文件
  private static void readfilenio()
  {
    fileinputstream fileinputstream;
    try
    {
      fileinputstream = new fileinputstream("src/main/java/data/nio-data.txt");
      filechannel filechannel=fileinputstream.getchannel();//從 fileinputstream 獲取通道
      bytebuffer bytebuffer=bytebuffer.allocate(1024);//創建緩沖區
      int bytesread=filechannel.read(bytebuffer);//將數據讀到緩沖區
      while(bytesread!=-1)
      {
        /*limit=position
         * position=0;
         */
        bytebuffer.flip();
        //hasremaining():告知在當前位置和限制之間是否有元素
        while (bytebuffer.hasremaining())
        {
          system.out.print((char) bytebuffer.get());
        }
        /*
         * 清空緩沖區
         * position=0;
         * limit=capacity;
         */
        bytebuffer.clear();
        bytesread = filechannel.read(bytebuffer);
      }
    } catch (filenotfoundexception e)
    {
      // todo auto-generated catch block
      e.printstacktrace();
    } catch (ioexception e)
    {
      // todo auto-generated catch block
      e.printstacktrace();
    }
  }
}

3、多路復用器selector

  多路復用器提供選擇已經就緒的任務的能力。selector會不斷的輪詢注冊在其上的channel,如果某個channel上面發送讀或者寫事件,這個channel就處于就緒狀態,會被selector輪詢出來,然后通過selectionkey可以獲取就緒channel的集合,進行后續的i/o操作。

  一個多路復用器selector可以同時輪詢多個channel,由于jdk使用了epoll代替了傳統的select實現,所以它沒有最大連接句柄1024/2048的限制,意味著只需要一個線程負責selector的輪詢,就可以接入成千上萬的客戶端。其模型如下圖所示:

Java NIO服務器端開發詳解

用單線程處理一個selector。要使用selector,得向selector注冊channel,然后調用它的select()方法。這個方法會一直阻塞到某個注冊的通道有事件就緒。一旦這個方法返回,線程就可以處理這些事件,事件的例子有如新連接進來,數據接收等。

  注:

  1、什么select模型?

  select是事件觸發機制,當等待的事件發生就觸發進行處理,多用于linux實現的服務器對客戶端的處理。

  可以阻塞地同時探測一組支持非阻塞的io設備,是否有事件發生(如可讀、可寫,有高優先級錯誤輸出等),直至某一個設備觸發了事件或者超過了指定的等待時間。也就是它們的職責不是做io,而是幫助調用者尋找當前就緒的設備。

  2、什么是epoll模型?

  epoll的設計思路,是把select/poll單個的操作拆分為1個epoll_create+多個epoll_ctrl+一個wait。此外,內核針對epoll操作添加了一個文件系統”eventpollfs”,每一個或者多個要監視的文件描述符都有一個對應的eventpollfs文件系統的inode節點,主要信息保存在eventpoll結構體中。而被監視的文件的重要信息則保存在epitem結構體中。所以他們是一對多的關系。

二、nio服務器端開發

  功能說明:開啟服務器端,對每一個接入的客戶端都向其發送hello字符串。

  使用nio進行服務器端開發主要有以下幾個步驟:

  1、創建serversocketchannel,配置它為非阻塞模式

?
1
2
serversocketchannel = serversocketchannel.open();
serversocketchannel.configureblocking(false);

  2、綁定監聽,配置tcp參數,如backlog大小

?
1
serversocketchannel.socket().bind(new inetsocketaddress(8080));

  3、創建一個獨立的i/o線程,用于輪詢多路復用器selector

  4、創建selector,將之前創建的serversocketchannel注冊到selector上,監聽selectionkey.accept

?
1
2
selector=selector.open();
serversocketchannel.register(selector, selectionkey.op_accept);

  5、啟動i/o線程,在循環體內執行selector.select()方法,輪詢就緒的channel

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while(true)
  {
    try
    {
      //select()阻塞到至少有一個通道在你注冊的事件上就緒了
      //如果沒有準備好的channel,就在這一直阻塞
      //select(long timeout)和select()一樣,除了最長會阻塞timeout毫秒(參數)。
      selector.select();
    }
    catch (ioexception e)
    {
      // todo auto-generated catch block
      e.printstacktrace();
      break;
     }
 }

  6、當輪詢到了處于就緒狀態的channel時,需對其進行判斷,如果是op_accept狀態,說明是新的客戶端接入,則調用serversocketchannel.accept()方法接受新的客戶端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//返回已經就緒的selectionkey,然后迭代執行
      set<selectionkey> readkeys=selector.selectedkeys();
      for(iterator<selectionkey> it=readkeys.iterator();it.hasnext();)
      {
        selectionkey key=it.next();
        it.remove();
        try
        {
          if(key.isacceptable())
          {
            serversocketchannel server=(serversocketchannel) key.channel();
            socketchannel client=server.accept();
            client.configureblocking(false);
            client.register(selector,selectionkey.op_write);
          }
          else if(key.iswritable())
          {
            socketchannel client=(socketchannel) key.channel();
            bytebuffer buffer=bytebuffer.allocate(20);
            string str="hello";
            buffer=bytebuffer.wrap(str.getbytes());
            client.write(buffer);
            key.cancel();
          }
        }catch(ioexception e)
        {
          e.printstacktrace();
          key.cancel();
          try
          {
            key.channel().close();
          } catch (ioexception e1)
          {
            // todo auto-generated catch block
            e1.printstacktrace();
          }
          
        }
      }

 7、設置新接入的客戶端鏈路socketchannel為非阻塞模式,配置其他的一些tcp參數

?
1
2
3
4
5
6
7
if(key.isacceptable())
  {
    serversocketchannel server=(serversocketchannel) key.channel();
    socketchannel client=server.accept();
    client.configureblocking(false);
    ...
  }

  8、將socketchannel注冊到selector,監聽op_write

?
1
client.register(selector,selectionkey.op_write);

  9、如果輪詢的channel為op_write,則說明要向sockchannel中寫入數據,則構造bytebuffer對象,寫入數據包

?
1
2
3
4
5
6
7
8
9
else if(key.iswritable())
  {
    socketchannel client=(socketchannel) key.channel();
    bytebuffer buffer=bytebuffer.allocate(20);
    string str="hello";
    buffer=bytebuffer.wrap(str.getbytes());
    client.write(buffer);
    key.cancel();
  }

 完整代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import java.io.ioexception;
import java.net.inetsocketaddress;
import java.nio.bytebuffer;
import java.nio.channels.selectionkey;
import java.nio.channels.selector;
import java.nio.channels.serversocketchannel;
import java.nio.channels.socketchannel;
import java.util.iterator;
import java.util.set;
public class serversocketchanneldemo
{
    public static void main(string[] args)
      {
        serversocketchannel serversocketchannel;
        selector selector=null;
        try
            {
            serversocketchannel = serversocketchannel.open();
            serversocketchannel.configureblocking(false);
            serversocketchannel.socket().bind(new inetsocketaddress(8080));
            selector=selector.open();
            serversocketchannel.register(selector, selectionkey.op_accept);
        }
        catch (ioexception e)
            {
            // todo auto-generated catch block
            e.printstacktrace();
        }
        while(true)
            {
            try
                  {
                //select()阻塞到至少有一個通道在你注冊的事件上就緒了
                //如果沒有準備好的channel,就在這一直阻塞
                //select(long timeout)和select()一樣,除了最長會阻塞timeout毫秒(參數)。
                selector.select();
            }
            catch (ioexception e)
                  {
                // todo auto-generated catch block
                e.printstacktrace();
                break;
            }
            //返回已經就緒的selectionkey,然后迭代執行
            set<selectionkey> readkeys=selector.selectedkeys();
            for (iterator<selectionkey> it=readkeys.iterator();it.hasnext();)
                  {
                selectionkey key=it.next();
                it.remove();
                try
                        {
                    if(key.isacceptable())
                              {
                        serversocketchannel server=(serversocketchannel) key.channel();
                        socketchannel client=server.accept();
                        client.configureblocking(false);
                        client.register(selector,selectionkey.op_write);
                    } else if(key.iswritable())
                              {
                        socketchannel client=(socketchannel) key.channel();
                        bytebuffer buffer=bytebuffer.allocate(20);
                        string str="hello";
                        buffer=bytebuffer.wrap(str.getbytes());
                        client.write(buffer);
                        key.cancel();
                    }
                }
                catch(ioexception e)
                        {
                    e.printstacktrace();
                    key.cancel();
                    try
                              {
                        key.channel().close();
                    }
                    catch (ioexception e1)
                              {
                        // todo auto-generated catch block
                        e1.printstacktrace();
                    }
                }
            }
        }
    }
}

我們用telnet localhost 8080模擬出多個客戶端:

Java NIO服務器端開發詳解

程序運行結果如下:

Java NIO服務器端開發詳解

總結

以上就是本文關于java nio服務器端開發詳解的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

原文鏈接:http://www.cnblogs.com/xujian2014/p/5657540.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品二区在线观看 | 久久久久女人精品毛片 | 色阁五月| 亚州精品在线视频 | www.54271.com | 91婷婷射 | 欧美性生活视频免费看 | 成人免费毛片一 | v片在线看| 亚洲无线看 | 久久久久成人精品免费播放 | 日本网站一区 | 日韩男女在线 | 一级毛片免费版 | www国产成人免费观看视频,深夜成人网 | 免费一区区三区四区 | 19禁国产精品福利视频 | 日韩大片在线永久观看视频网站免费 | 国产二区三区在线播放 | 成人免费福利网站 | 亚洲电影在线观看高清免费 | 免费观看一级淫片 | 午夜在线视频一区二区三区 | 久久不雅视频 | 狼伊千合综网中文 | 爱高潮www亚洲精品 chengrenzaixian | 色淫视频 | 日韩蜜桃视频 | 成人午夜一区二区 | 黑色丝袜美美女被躁视频 | av日韩在线免费观看 | 久久久视频免费观看 | 欧美一级视频 | 91成人一区 | 免费观看一级 | 一级片久久免费 | 国产成年人视频网站 | 欧美成人黄色小视频 | 九九热精品视频在线免费观看 | 国产亚洲精品久久午夜玫瑰园 | 久久aⅴ国产欧美74aaa |