1. 程式人生 > >java網路程式設計(一)使用TCP協議完成客戶端與服務端的資料傳遞

java網路程式設計(一)使用TCP協議完成客戶端與服務端的資料傳遞

在正式進入網路程式設計之前先簡單介紹一個網路程式設計的基本概念。
網路程式設計就是兩個或多個裝置之間的資料交換,其實更具體的說,網路程式設計就是兩個或多個程式之間的資料交換即可以理解為一次網路通訊過程。在網路通訊中,第一次主動發起通訊的程式被稱作客戶端(Client)程式,簡稱客戶端,而在第一次通訊中等待連線的程式被稱作伺服器端(Server)程式,簡稱伺服器。一旦通訊建立,則客戶端和伺服器端完全一樣,沒有本質的區別。
由此,網路程式設計中的兩種程式就分別是客戶端和伺服器端,例如QQ程式,每個QQ使用者安裝的都是QQ客戶端程式,而QQ伺服器端程式則執行在騰訊公司的機房中,為大量的QQ使用者提供服務。這種網路程式設計的結構被稱作客戶端/伺服器結構,也叫做Client/Server結構,簡稱C/S結構。而一般如J2EE等web開發,則是瀏覽器/伺服器,簡稱B/S結構。
使用B/S結構的程式,在開發時只需要開發伺服器端即可,這種結構的優勢在於開發的壓力比較小,不需要維護客戶端。但是這種結構也存在著很多不足,例如瀏覽器的限制比較大,表現力不強,無法進行系統級操作等。
總之C/S結構和B/S結構是現在網路程式設計中常見的兩種結構,B/S結構其實也就是一種特殊的C/S結構。
使用B/S結構的程式,在開發時只需要開發伺服器端即可,這種結構的優勢在於開發的壓力比較小,不需要維護客戶端。但是這種結構也存在著很多不足,例如瀏覽器的限制比較大,表現力不強,無法進行系統級操作等。

總之C/S結構和B/S結構是現在網路程式設計中常見的兩種結構,B/S結構其實也就是一種特殊的C/S結構。
最後再介紹一個網路程式設計中最重要,也是最複雜的概念——協議(Protocol)。按照前面的介紹,網路程式設計就是執行在不同計算機中兩個程式之間的資料交換。在實際進行資料交換時,為了讓接收端理解該資料,計算機比較笨,什麼都不懂的,那麼就需要規定該資料的格式,這個資料的格式就是協議。
那麼如何來編寫協議格式呢?答案是隨意。只要按照這種協議格式能夠生成唯一的編碼,按照該編碼可以唯一的解析出傳送資料的內容即可。也正因為各個網路程式之間協議格式的不同,所以才導致了客戶端程式都是專用的結構。
在實際的網路程式程式設計中,最麻煩的內容不是資料的傳送和接收,因為這個功能在幾乎所有的程式語言中都提供了封裝好的API進行呼叫,最麻煩的內容就是協議的設計以及協議的生產和解析,這個才是網路程式設計中最核心的內容。

在現有的網路中,網路通訊的方式主要有兩種:

  1、 TCP(傳輸控制協議)方式

  2、 UDP(使用者資料報協議)方式
  關於這兩個協議的區別這裡不過多介紹。這裡我們採用TCP來進行傳輸。

在客戶端網路程式設計中,首先需要建立連線,在Java API中以java.net.Socket類的物件代表網路連線,所以建立客戶端網路連線,也就是建立Socket型別的物件,該物件代表網路連線,示例如下:

          Socket socket1 = new Socket(“192.168.1.103”,10000);

          Socket socket2 = new Socket
(“www.sohu.com”,80);

注:Socket的引數一表示伺服器ip地址或者域名或者主機名。(寫主機名獲取域名最後都被解析為ip地址),引數二表示埠號。

連線一旦建立,則完成了客戶端程式設計的第一步,緊接著的步驟就是按照“請求-響應”模型進行網路資料交換,在Java語言中,資料傳輸功能由Java IO實現,也就是說只需要從連線中獲得輸入流和輸出流即可,然後將需要傳送的資料寫入連線物件的輸出流中,在傳送完成以後從輸入流中讀取資料即可。示例程式碼如下:

         OutputStream os = socket1.getOutputStream(); //獲得輸出流

         InputStream is = socket1.getInputStream();     //獲得輸入流

最後當資料交換完成以後,關閉網路連線,釋放網路連線佔用的系統埠和記憶體等資源,完成網路操作,示例程式碼如下:

        socket1.close();

現在我們編寫一個完整客戶段的Demo來向伺服器傳送字串 “Hello World”

public class SocketClient {
     public static void main(String[] args) {
         try{
     //建立Socket例項
          Socket socket = new Socket("192.168.1.8",8888);
      //獲取輸出流
          OutputStream outputStream = socket.getOutputStream();
         outputStream.write("hello,world".getBytes());
        //釋放資源
       outputStream.close();
       socket.close();
         }catch(Exception e){
           e.printStackTrace();
         }
    }
}

服務端的Demo:

public class SocketService {

    public static void main(String[] args) {
         InputStream inputStream;
       Socket socket;
        StringBuilder s = new StringBuilder();
        try {
            //伺服器端接收訊息的類。定製埠號為8888
            ServerSocket serviceSocket = new ServerSocket(8888);
            //獲取socket。這個方法是阻塞式的
            socket = serviceSocket.accept();
          inputStream = socket.getInputStream();
         byte buf[] = new byte[1024];
        int len = 0;
        while((len=(inputStream.read(buf)))>0){
                    s.append(new String(buf,0,len));
             }
        //列印客戶端的訊息
        System.out.println(s.toString());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

先執行服務端的Demo,在執行客戶端的。即可看到在服務端接受到了客戶端傳遞過來的字串HelloWorld.

服務端接受到客戶端的資訊後,向客戶端迴應一句話:

客戶端:

public class SocketClient {
    public static void main(String[] args) {
        Socket socket =null;
        InputStream inputStream =null;
         OutputStream outputStream =null;
         try{
             //建立Socket例項
             socket = new Socket("hadoop",10000);
            //獲取輸出流,向伺服器發生資料
             outputStream =     socket.getOutputStream();
         outputStream.write("hello world:".getBytes());
           ///獲取輸入流,獲取伺服器的響應
         inputStream = socket.getInputStream();
         byte[] buff = new byte[1024];
         int len = 0;
         len =  inputStream.read(buff);
         //列印服務端的相應
         System.out.println(new String(buff,0,len)); 
         }catch(Exception e){
             e.printStackTrace();
         }finally {
             //釋放資源
               try {
                inputStream.close();
                outputStream.close();
                   socket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
        }
    }
    }
}

服務端:

public class SocketService {

    public static void main(String[] args) {
         InputStream inputStream =null;
         OutputStream outputStream =null;
       Socket socket = null;
       ServerSocket serviceSocket =null;
        try {
            //伺服器端接收訊息的類。定製埠號為8888
             serviceSocket = new ServerSocket(10000);
            //獲取socket。這個方法是阻塞式的
            socket = serviceSocket.accept();
          inputStream = socket.getInputStream();
         byte buf[] = new byte[1024];
        int len = 0;
        len =inputStream.read(buf);
        //列印客戶端的訊息
        System.out.println(new String(buf,0,len));
        //向客戶端生成響應
        outputStream = socket.getOutputStream();
       outputStream.write("收到".getBytes());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
             //釋放資源
              try {
                     outputStream.close();
                     inputStream.close();
                socket.close();
                serviceSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }