熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

用Java實現多線程服務器程序

2013-11-23 19:55:19  來源: Java高級技術 

  摘要在Java出現之前編寫多線程程序是一件煩瑣且伴隨許多不安全因素的事情利用Java編寫安全高效的多線程程序變得簡單而且利用多線程和Java的網絡包我們可以方便的實現多線程服務器程序

  Java是伴隨Internet的大潮產生的對網絡及多線程具有內在的支持具有網絡時代編程語言的一切特點從Java的當前應用看Java主要用於在Internet或局域網上的網絡編程而且將Java作為主流的網絡編程語言的趨勢愈來愈明顯實際工作中我們除了使用商品化的服務器軟件外時常需要按照實際環境編寫自己的服務器軟件以完成特定任務或與特定客戶端軟件實現交互在實現服務器程序時為提高程序運行效率降低用戶等待時間我們應用了在Java Applet中常見的多線程技術

   Java中的服務器程序與多線程

  在Java之前沒有一種主流編程語言能夠提供對高級網絡編程的固有支持在其他語言環境中實現網絡程序往往需要深入依賴於操作平台的網絡API的技術中去而Java提供了對網絡支持的無平台相關性的完整軟件包使程序員沒有必要為系統網絡支持的細節而煩惱

  Java軟件包內在支持的網絡協議為TCP/IP也是當今最流行的廣域網/局域網協議Java有關網絡的類及接口定義在javanet包中客戶端軟件通常使用javanet包中的核心類Socket與服務器的某個端口建立連接而服務器程序不同於客戶機它需要初始化一個端口進行監聽遇到連接呼叫才與相應的客戶機建立連接Javanet包的ServerSocket類包含了編寫服務器系統所需的一切下面給出ServerSocket類的部分定義

  public class ServerSocket
  {
   public ServerSocket(int port) throws IOException
   public Socket accept() throws IOException
   public InetAddress getInetAddress()
   public int getLocalPort()
   public void close() throws IOException
   public synchronized void setSoTimeout (int timeout) throws SocketException
   public synchronized int getSoTimeout() throws IOException
  }

  ServerSocket構造器是服務器程序運行的基礎它將參數port指定的端口初始化作為該服務器的端口監聽客戶機連接請求Port的范圍是是標准Internet協議保留端口而且在Unix主機上這些端口只有root用戶可以使用一般自定義的端口號在之間僅初始化了ServerSocket還是遠遠不夠的它沒有同客戶機交互的套接字(Socket)因此需要調用該類的accept方法接受客戶呼叫Accept()方法直到有連接請求才返回通信套接字(Socket)的實例通過這個實例的輸入輸出流服務器可以接收用戶指令並將相應結果回應客戶機ServerSocket類的getInetAddress和getLocalPort方法可得到該服務器的IP地址和端口setSoTimeout和getSoTimeout方法分別是設置和得到服務器超時設置如果服務器在timout設定時間內還未得到accept方法返回的套接字實例則拋出IOException的異常

  Java的多線程可謂是Java編程的精華之一運用得當可以極大地改善程序的響應時間提高程序的並行性在服務器程序中由於往往要接收不同客戶機的同時請求或命令因此可以對每個客戶機的請求生成一個命令處理線程同時對各用戶的指令作出反應在一些較復雜的系統中我們還可以為每個數據庫查詢指令生成單獨的線程並行對數據庫進行操作實踐證明采用多線程設計可以很好的改善系統的響應並保證用戶指令執行的獨立性由於Java本身是線程安全因此有一條編程原則是能夠獨立在一個線程中完成的操作就應該開辟一個新的線程

  Java中實現線程的方式有兩種一是生成Thread類的子類並定義該子類自己的run方法線程的操作在方法run中實現但我們定義的類一般是其他類的子類而Java又不允許多重繼承因此第二種實現線程的方法是實現Runnable接口通過覆蓋Runnable接口中的run方法實現該線程的功能本文例子采用第一種方法實現線程

  多線程服務器程序舉例

  以下是我們在項目中采用的多線程服務器程序的架構可以在此基礎上對命令進行擴充本例未涉及數據庫如果在線程運行中需要根據用戶指令對數據庫進行更新操作則應注意線程間的同步問題使同一更新方法一次只能由一個線程調用這裡我們有兩個類receiveServer包含啟動代碼(main())並初始化ServerSocket的實例在accept方法返回用戶請求後將返回的套接字(Socket)交給生成的線程類serverThread的實例直到該用戶結束連接

  file://類receiveServer
  import javaio*;
  import javautil*;
  import javanet*;
 
  public class receiveServer
  {
   final int RECEIVE_PORT=;
   file://該服務器的端口號
   file://receiveServer的構造器
   public receiveServer()
   {
    ServerSocket rServer=null; file://ServerSocket的實例
    Socket request=null; file://用戶請求的套接字
    Thread receiveThread=null;
    try
    {
     rServer=new ServerSocket(RECEIVE_PORT); file://初始化ServerSocket
     Systemoutprintln(Welcome to the server!);
     Systemoutprintln(new Date());
     Systemoutprintln(The server is ready!);
     Systemoutprintln(Port: +RECEIVE_PORT);
     while(true)
     {
      file://等待用戶請求
      request=rServeraccept();
      file://接收客戶機連接請求
      receiveThread=new serverThread(request);
      file://生成serverThread的實例
      receiveThreadstart();
      file://啟動serverThread線程
     }
    }
    catch(IOException e)
    {
     Systemoutprintln(egetMessage());
    }
   }
   public static void main(String args[])
   {
    new receiveServer();
   } file://end of main
  } file://end of class

  file://類serverThread
  import javaio*;
  import javanet*;

  class serverThread extends Thread
  {
   Socket clientRequest; file://用戶連接的通信套接字
   BufferedReader input; file://輸入流
   PrintWriter output; file://輸出流
   public serverThread(Socket s)
   {
    file://serverThread的構造器
    thisclientRequest=s;
    file://接收receiveServer傳來的套接字
    InputStreamReader reader;
    OutputStreamWriter writer;
    try
    {
     file://初始化輸入輸出流
     reader=new InputStreamReader(clientRequestgetInputStream());
     writer=new OutputStreamWriter(clientRequestgetOutputStream());
     input=new BufferedReader(reader);
     output=new PrintWriter(writertrue);
    }
    catch(IOException e)
    {
     Systemoutprintln(egetMessage());
    }
    outputprintln(Welcome to the server!); file://客戶機連接歡迎詞
    outputprintln(Now is: +new javautilDate()+ + Port:+clientRequestgetLocalPort());
    outputprintln(What can I do for you?);
   }
   public void run()
   {
    file://線程的執行方法
    String command=null; file://用戶指令
    String str=null;
    boolean done=false;
    while(!done)
    {
     try
     {
      str=inputreadLine(); file://接收客戶機指令
     }
     catch(IOException e)
     {
      Systemoutprintln(egetMessage());
     }
     command=strtrim()toUpperCase();
     if(str==null || commandequals(QUIT)) file://命令quit結束本次連接
     done=true;
     else if(commandequals(HELP))
     {
      file://命令help查詢本服務器可接受的命令
      outputprintln(query);
      outputprintln(quit);
      outputprintln(help);
     }
     else if(commandstartsWith(QUERY))
     {
      file://命令query
      outputprintln(OK to query something!);
     }
     //else if ……//在此可加入服務器的其他指令
     else if(!commandstartsWith(HELP) && !commandstartsWith(QUIT) && !commandstartsWith(QUERY))
     {
      outputprintln(Command not Found! Please refer to the HELP!);
     }
    }//end of while
    try
    {
     clientRequestclose(); file://關閉套接字
    }
    catch(IOException e)
    {
     Systemoutprintln(egetMessage());
    }
    command=null;
   }//end of run

  啟動該服務器程序後可用telnet machine port命令連接其中machine為本機名或地址port為程序中指定的端口也可以編寫特定的客戶機軟件通過TCP的Socket套接字建立連接


From:http://tw.wingwit.com/Article/program/Java/gj/201311/27667.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.