成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

使用Java建立穩定的多線程服務器

開發 前端
要建立穩定的服務器程序,消息隊列和線程池是很重要的。此外,也要考慮到很多的意外情況的發生。一般的程序員在寫完線程的run()方法的循環后就不管了,其實還應該考慮跳出循環后的資源釋放等問題。本文將講述如何使用Java建立穩定的多線程服務器。

Java語言是完全面向對象的,它的線程機制和對象序列化特別容易使用,使用Java來建立一套多線程服務器要比使用其它語言方便的多,如果你再把它的異常處理機制利用好,那么你就可以建立一個商業級的多線程服務器了。由于采用了消息隊列和Socket傳輸方式,所以不會出現丟消息的問題。這套服務器可以作為實時聊天服務器、多人協同的協作服務器等。

消息系統的建立

這套服務器的消息系統采用的是對象傳輸的機制,而不是以前常常使用的字符串傳輸。采用對象傳輸的好處是擴展方便,如需要建立一個新的消息只需要從一個統一的基類繼承下來,然后再寫自己實現的方法就行了。這樣也符合面向對象領域里一條重要的原則: OCP(open_closed Principle),即一個好的設計應該能夠容納新的功能的增加,但是增加的方式不是修改原有的類,而是添加新的類。

首先建立一個基類:Msg,該抽象類中有兩個域sender和receiver分別紀錄消息的發送者和接收者。這兩個域是在構造消息類時就填寫的,receiver域可以為空,空表示發給誰都可以,由轉發服務器來決定。該類的方法包括取得這兩個域的值和消息的處理函數。消息的處理函數process()是空函數,供繼承者重載。

建立了這個抽象基類后,你就可以繼承它完成你自己的類。舉個例子,假如我要建立一個分組協同工作的繪圖系統,而且支持組員之間的對話,那么我可以建立如下的類集合:

SendTextMsg(String sender,String receiver,String info)//向指定的人發送對話。
  AddLineMsg(String sender,Point a,Point b)//在指定的點之間繪制一條直線
  AddRectangle(String sender,point start,Point end)//建立指定的矩形
  AddRotundaMsg(String sender,Point center,int radius)//建立指定的圓
  RemoveObjectMsg(String sender,int ID)//刪除指定編號的圖形對象
  ……

以此類推,可以建立很多的消息類。在每個類的內部都由一個處理該類的方法process(),填寫該方法就可以實現對消息類的處理,而服務器只負責完成消息的轉發功能。這樣,一套消息系統就建立了。

服務器的結構

如果要服務器實現同時為每個客戶端服務,就要使用多線程,建立一個線程池,當有客戶端連接時就在池中開辟一個線程為它服務。同樣,要避免大量消息到達時處理不過來而導致丟失的情況,就要使用消息隊列。這個服務器是分層的處理的。

類關系圖如下所示:

 

服務器的工作過程是這樣的,建立了一個Server類作為主類,它含有程序的入口函數main()。在構造函數中初始化一個數組存放ClientSingle類,它其實就是單獨處理一個連接用戶的類。然后啟動一個線程PORTListenThread,該線程的作用就是監聽端口上有沒有人登陸,當有人連接時交給Server的addClient()處理。Server的addClient()方法會在剛才那個數組中建立一個ClientSingle對象,然后把剩下的事都交給它做。

#p#

端口監聽線程類PORTListenThread

該線程類在run()函數的開始部分首先要檢查serverScoket是否為空,保證循環開始時不要出錯。然后進入一個死循環的監聽:

 while(true) { //死循環監
   try{Socket clientSocket=null;
       clientSocket=serverSocket.accept();
       server.addClient(clientSocket);//轉交Server處理
   }
   catch (IOException e){System.out.println("監聽端口時出錯"+e);}//顯示錯誤
 }

單個客戶端在連接池中的映像類ClientSingle

每一個客戶端連接到服務器后,服務器會自動在連接池中建立該客戶端的一個映像,所有的操作都交給這個映像去具體執行,所以ClientSingle中一定要包含客戶端的一些基本的信息。比如客戶端的名稱、登陸時間等等。在該類中有兩個消息隊列sendQueue(發送隊列)和receiveQueue(接收隊列)緩存消息。

ClientSingle類是繼承自Thread的,它還是一個調用者。在初始化的時候啟動兩個子線程類SingleSender和SingleListener運行。SingleSender負責監聽指令發送隊列中有沒有指令,有則發送;SingleListener負責監聽有沒有消息到達,有則把這些消息加入到接收隊列中去,由ClientSingle處理。所以ClientSingle的主要任務就是對這兩個隊列的處理。這兩個隊列可以用Vector實現,非常地簡單。

 //-------將消息加入發送隊列中------------
 synchronized void send(Object o)
 { sendQueue.add(o);
 }

為了穩定控制子線程的運行,并不鼓勵在run()方法的死循環標志都用true,而是使用了一個布爾型的變量finish。外部可以通過把這個標志置為假而停止線程的運行。

發送子線程類啟動后執行run()中的循環(以finish為結束標志),在該循環內首先判斷ClientSingle中的發送隊列是否為空,為空時睡眠一定的時間再重新判斷,這也是一個while循環。不為空則開始處理隊列中的消息,把它取出后放入輸出流中發送。

  public void run(){
     while (!father.finish){ //循環監聽
       while(father.v.isEmpty()){ //當發送隊列為空的時候線程睡眠500毫秒
         try{Thread.sleep(500);}
 catch(InterruptedException e){System.out.println(e);}
       }
       if (!father.v.isEmpty()){  //發送隊列不為空時
        try{
           Object a=father.v.firstElement();//取出隊列中的***個消息
           father.v.removeElementAt(0);//從隊列中刪除
           oos.writeObject(a);//發送該消息
           oos.flush();
         }catch(IOException e){
           displayMessage(" 傳輸失敗 !");
           father.finish=false;
         }
       }
     }
}

接收子線程SingleListener類和發送子線程是類似的,它們的run()方法都差不多。不同的是接收子線程把收到的消息加入到ClientSingle的接收隊列中去,由它處理。

ClientSingle類的run()方法就在循環地讀取接收隊列receiveQueue中的內容,為空時等待;不為空時依次取出處理和轉發。處理消息的函數是processMsg(),它只是執行消息類自己的process()方法罷了。在處理完后,會調用Server類的方法進行各種類型的轉發。

#p#

分組轉發的實現類Group

為了實現對客戶端分組,我建立了Group類。在這個類中有一個列表存放已經存在于連接池中的那些ClientSingle類的引址。只要遍歷整個列表就能訪問所有組中的成員。這個列表可以用Vector實現,也可以用哈希表,我推薦后者,主要是為了能夠按名字存取。

組對象本身也是可以存在Server類的組列表中的。

分組功能對多人的協同系統來說是非常重要的,特別是分組對某一個共享空間操作的時候。就以上面的協同繪圖系統為例,如果10個人里有三個人要另起爐灶,那么他們三個的畫板就不能讓其他人看到,這就必須有"組"個劃分。

主服務器類Server

Server類是最核心的類,它在這個框架中起到調度全局的作用,上面介紹的那些類都由它來統一的構造和調用。

Server類的域包括一個定長的數組存放ClientSingle實例,它就是連接池的實現。還要有一個哈希表存放Group實例。Server類的方法都是對這兩個類的操作。

建立ClientSingle數組的目的是保證服務器的穩定性。其實,你也可以選擇不建立它,只是動態地構造對象,但是那樣不好管理連接的用戶,而且由于各種操作系統對進程的處理不同,動態建立服務線程會很不穩定。所以我先建立一個數組作為這些對象的容器,在開始時就估計好連接者的***數量。Server類的addClient()函數:

void addClient(Socket socket){
     int c=0;
     try{while (sch[c]!=null) c++;}//搜索數組中的空余空間
     catch(ArrayIndexOutOfBoundsException e){
       try{ socket.close();}//出現異常關閉槽連接
       catch(IOException ee){ System.out.println("數組溢出");}
       return;
    }
     sch[c]=new ClientSingle(c,socket,father,this);//在搜索到的位置建立ClientSingle對象
 }

erver類中轉發的方法有:sendToAll()、sendToOne()、sendToGroup()等等。這些方法都是對線程池中的方法的操作,比較簡單,不外乎都是找到線程池中的某個ClientSingle對象,然后調用它的send()方法罷了。

注意:這些轉發的方法可能被很多子線程同時調用,所以為了保持線程的穩定,千萬記住要在方法前加synchronized關鍵字。

【編輯推薦】

  1. Java EE開發三劍客現狀及發展淺析
  2. Java EE的Web服務原理和體系結構
  3. 三步學會Java Socket編程
責任編輯:楊鵬飛 來源: IBM
相關推薦

2009-02-27 11:15:00

多線程服務器MTS專用服務器

2011-12-08 13:04:06

JavaNIO

2010-03-17 17:54:25

java Socket

2010-03-19 14:01:55

Java Socket

2010-03-16 10:50:21

Java多線程服務器

2009-11-23 17:23:59

DNS服務器內部建立

2010-08-03 11:49:26

Ubuntu nfs服

2010-03-16 13:47:48

Java多線程服務器

2018-12-20 09:36:24

2011-06-30 18:03:58

QT 多線程 服務器

2010-09-06 17:08:23

2010-08-26 13:04:06

DHCP服務器

2010-03-24 11:49:37

Turbo Linux

2010-07-01 09:47:18

DNS服務器BIND

2015-10-27 09:40:31

TCPIP網絡協議

2010-09-02 14:56:03

建立DHCP服務器

2011-12-07 17:05:45

JavaNIO

2010-08-06 13:12:55

NFS服務器

2010-08-23 17:31:23

LinuxDHCP服務器

2010-07-21 11:29:24

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 九色视频网站 | 色男人的天堂 | 精品久久久久久久久久久久久久 | 国产在线精品一区 | 久久久久国产一区二区三区 | 国产成人一区二 | 久草视频观看 | 久久最新 | 色网站在线免费观看 | 国产色婷婷精品综合在线手机播放 | 国产三级 | 国产精品一区二区三级 | 9191成人精品久久 | 午夜视频免费在线观看 | 久久一区二区免费视频 | 激情国产| 国产精品99精品久久免费 | 欧美一区二区在线播放 | 国产成人精品一区二区三区四区 | 亚洲草草视频 | 日韩国产在线观看 | 玖玖操| 亚洲国产精品久久久久 | 日本网站免费在线观看 | 日本午夜网站 | cao在线| 狠狠干狠狠插 | 日韩在线观看一区 | 九九综合九九 | 国产精品视频观看 | 欧美视频在线一区 | 一级毛片视频 | 亚洲精品99 | 免费在线观看毛片 | 欧美国产日韩一区二区三区 | 日日夜夜91 | www.4567| 天天综合网7799精品 | 日韩精品久久一区 | 一区二区三区高清 | av特级毛片 |