天天看點

Java網絡程式設計之簡單Socket和ServerSocket

常用基本類

InetAddress類是java中表示IP的類。主要用于Socket,ServerSocket,URL,DatagramSocket等

 建立InetAddress用三個靜态方法

getByName(String hostname)throws UnknownHostException

 getAllByName(String hostname)throws UnknownHostException

 getLocalHost()throws UnknownHostException

 使用這三個靜态方法建立InetAddress的時候,除了使用getByName中傳遞ip位址外(如果使用getByName傳遞一個ip會首先建立一個InetAddress不管是否存在,當顯示的調用getHostName或者toString的時候才會進行網絡連接配接),都會首先進行網絡連接配接,會根據主機名去連接配接DNS伺服器,來查找對應的主機名和ip,一定要捕獲異常處理。

 對于已經建立成功的InetAddress可以擷取基本屬性,getAddress()傳回一個位元組數組,可以利用

 int unsigned=signByte<0?signByte+256:signByte轉換為int。也可以利用它的長度來判斷是IPv4或者IPv6。

 兩個InetAddress對象equals的條件是具有相同的IP,不管主機名是否相等

 toString的表現形式為:hostname/ip

URL類,是一個final類,使用的是政策設計模式,協定就是政策。

 構造URL的基本方法URL url=new URL("http://www.baidu.com"),如果建立未支援的協定的URL則會出現MalformedURLException異常。

 URL主要由協定、授權機構、路徑、片段标示符和查詢字元串表示。

 openStream():連接配接URL所指向的資源,執行用戶端與伺服器之間的握手,傳回一個可讀取的資料

 openConnection():打開指定的URL的socket,傳回一個URLConnection(表示一個打開網絡資源的連接配接)

 getContent()

 在利用URL進行發送資料的時候,主要URL中隻需出現ASCII中的固定子集,未出現的必須進行編碼,利用URLEncoder來編碼,要對每一部分編碼,再用URLDecoder解析編碼

URI類是純粹的關于資源的辨別和URI的解析,它沒有網絡擷取功能,主要用于對于URL進行分析。

基于TCP的Socket程式設計(Socket和ServerSocket)

用戶端Socket建立步驟:

1 利用構造函數建立一個新Socket

2 Socket嘗試連接配接遠端主機(在構造函數建立後,該socket就會自動連接配接遠端主機)

3 一旦建立連接配接,此socket就會進行輸入流和輸出流操作。連接配接是全雙工,雙方可同時發送接受資料

4 當資料傳輸完畢後,一端就或兩端就會關閉連接配接。HTTP1.0,要求每次請求得到伺服器響應後就關閉連接配接。

建立Socket基本方法Socket socket=new Socket("www.ttt.com",80);一定要捕獲異常,當主機名不存在的時候,抛出UnknownHostException異常,其他情況會抛出IOException,可以用來測試某一些端口是否可利用。

 擷取Socket的輸入流,socket.getInputStream();可以通過這個來擷取伺服器傳回的結果,在其調用read的時候會一直阻塞着。一般均要将其連結到過濾器流或者閱讀器。

 寫入Socket的輸出流,socket.getOutputStream();向伺服器發送資料,但是這裡務必要每次寫入後加上out.write("\r\n\r\n"),out.flush(),關閉輸出流。對于每行都要加上"\r\n"

 判斷一個socket目前是否打開,需要用如下方式:

 isClose傳回false僅在已經被關閉或者從未打開的時候

 isConnected表示曾經打開過

boolean connected=socket.isConnected()&&! socket.isClosed();

SocketAddress類,主要用于存放暫時的Socket連接配接資訊,将上次的連接配接儲存後,下次直接利用connect來連接配接。建立一個SocketAddress,可以通過其子類InetSocketAddress來建立,

InetSocketAddress,此類實作 IP 套接字位址(IP 位址 + 端口号),

它還可以是一個對(主機名 + 端口号)。

ServerSocket建立步驟

1 使用ServerSocket()構造函數來在某個端口建立一個ServerSocket

2 ServerSocket使用其accept()監聽此端口的所有入站請求。在沒有請求的時候,該accept方法會一直阻塞,它會使得整個程式停止執行并且等待,直到有用戶端嘗試進行連接配接,此時就會傳回一個連接配接用戶端和伺服器的Socket對象

3 調用輸入輸出流類型

4 關閉該Socket對象

5 繼續等待

 建立一個ServerSocket的基本方法ServerSocket server=new ServerSocket(1888);會抛出IOException和BindException的異常。

 測試一個ServerSocket是否打開利用 ss.isBound()&& !ss.isClosed()來判斷。

簡單Socket和Server連接配接測試

用戶端Socket

package com.client;

import java.io.IOException;

import java.io.*;

import java.net.Socket;

import java.net.UnknownHostException;

publicclass FirstClient {

publicstaticvoid main(String[] args) {

// TODO Auto-generated method stub

   Socket client=null;

try{

     client=new Socket("localhost",6666);

     InputStream in=client.getInputStream();

     OutputStream outs=client.getOutputStream();

     BufferedOutputStream buffs=new BufferedOutputStream(outs);

     Writer out=new OutputStreamWriter(buffs);

     out.write("hello===");

//out.flush();

     out.close();

//一定要關閉輸出流,否則它會一直占用該端口以及資源

   }catch(UnknownHostException e)

   {

     e.printStackTrace();

   }catch(IOException e)

   }

 }

}

伺服器端Socket

package com.server;

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.net.ServerSocket;

publicclass FirstServer {

try {

     ServerSocket server = new ServerSocket(6666);

while (true) {

       Socket client = null;

         client = server.accept();

         InputStream ins = client.getInputStream();

         BufferedReader read = new BufferedReader(

new InputStreamReader(ins));

         String res = read.readLine();

         read.close();

         System.out.println("client request:" + res);

       } catch (IOException e) {

         e.printStackTrace();

       } finally {

if (client != null)

             client.close();

         } catch (IOException e) {

           e.printStackTrace();

         }

       }

     }

   } catch (IOException e) {

用戶端伺服器端發送和接受流

不管是用戶端Socket處理流或者伺服器端獲得Socket處理流,不要随便關閉它們獲得的輸入流和輸出流,因為關閉這些流的時候會将該Socket也給關閉了,這是由于流在關閉的時候會釋放它所占有的資源,如句柄或端口。

輸入流處理

輸入流處理主要就是處理來自socket發送的請求資訊,不管是用戶端還是伺服器端都需要先利用getInputstream獲得一個socket傳輸的流。

下面是輸入流中最關鍵的部分就是分析請求的頭部,主要是在伺服器端

   Socket socket=null;

   BufferedReader netwin=null;//一般在網絡中不用這種方式,僅僅一般測試用

     socket=new Socket("localhost",2222);

     String header="HTTP/1.0 200 OK\r\n"+

"Server:Apache 7.0\r\n"+

"Content-length:"+107+"\r\n"+

"Content-type: text/html\r\n\r\n";

//輸出流

     OutputStream outfile=new BufferedOutputStream(socket.getOutputStream());

     Writer write=new OutputStreamWriter(outfile);

//輸入流

     InputStream inputstream=socket.getInputStream();

     BufferedInputStream in=new BufferedInputStream(inputstream);

     InputStreamReader inr=new InputStreamReader(in);

//對于http請求的需要先判斷是否是http協定

//判斷請求是否是http協定主要就是處理輸入流中的第一行

//第一種處了解析請求頭部

     StringBuffer request1=new StringBuffer(100);

while(true)

     {

int c=in.read();

if(c=='\r' || c=='\n' || c==-1)

break;

       request1.append((char)c);

     String requestHeader1=request1.toString();

if(requestHeader1.indexOf("HTTP/")!=-1)

       write.write(header);

     write.flush();

     write.close();

//第二種處了解析請求頭部

     StringBuffer request2=new StringBuffer(100);

       request2.append((char)c);

//截取第一行每一部分

//一般http請求格式GET /index.html HTTP/1.0

     String requestHeader2=request2.toString();

int firstSpace=requestHeader2.indexOf(' ');

int secondSpace=requestHeader2.indexOf(' ', firstSpace+1);

     String fileName=requestHeader2.substring(firstSpace+1,secondSpace);

       write.flush();

       write.close();

//第三種處了解析請求頭部

     StringBuffer request3=new StringBuffer(100);

       request3.append((char)c);

     String requestHeader3=request3.toString();

//使用預設分隔符,包括空白字元,制表符,換行符,回車符,換頁符

     StringTokenizer st=new StringTokenizer(requestHeader3);

     String method=st.nextToken();

     String version="";

     String filename=st.nextToken();

     String httpT="";

if(st.hasMoreTokens())

       httpT=st.nextToken();

if(httpT.indexOf("HTTP/")!=-1)

輸出流處理

在利用http發送資料的時候,每一行都應該由\r\n結束,一般不要使用pringln這種,因為java在linux中換行是以\n結束,windows下換行是以\r\n。但是在利用http進行請求與響應的過程中,兩個都必須每次寫入後最後要寫入\r\n\r\n,再要将輸出流flush以及關閉該輸出流。如果不是用http請求一般隻需要在每行加入\r\n

不管是用戶端還是伺服器端要發送流,就必須先getOutputStream來擷取一個寫入流,用于發送到另一端的Socket中。一般均要放到輸出流或者書寫器中。

輸出流是以位元組為機關寫的,這樣會加快資料的傳輸速度,便于傳輸。

書寫器是以字元為機關寫的,友善寫入資料。

基本的輸出流寫入方式:

       socket=new Socket("localhost",2222);

       OutputStream outs=socket.getOutputStream();

//利用緩沖來

       BufferedOutputStream buffer=new BufferedOutputStream(outs);

       writer=new OutputStreamWriter(buffer); 

//在寫入的過程中每一行必須要以\r\n結束

       writer.write("hello");

//這裡是一般請求則,隻需要加上\r\n即可

       writer.write("\r\n");

//當是http請求的時候需要這樣處理,請求最後加上\r\n

//writer.write("GET /index.html http/1.0 ")

//writer.write("\r\n\r\n")

       writer.flush();

       writer.close();

當是對于http請求進行響應的時候就需要寫入MIME首部,且傳回檔案的時候,該代碼執行個體基本功能是傳輸用戶端請求的頁面,并且根據http協定傳送首部,錯誤處理

//這裡寫入的隻是一些小型檔案的處理

     InputStream in=new FileInputStream("f:\\t.txt");

     ByteArrayOutputStream out=new ByteArrayOutputStream();

int b;

while((b=in.read())!=-1)out.write(b);

byte[] data=out.toByteArray();

//下面的是讀取大檔案處理的方式

//      File file=new File("f:\\t.txt");

//      DataInputStream fis=new DataInputStream(

//          new BufferedInputStream(new FileInputStream(file)));

//      //存放資料

//      byte[] data=new byte[(int)file.length()];

//      fis.readFully(data);//将檔案讀入到位元組數組中

//      fis.close();//直接在輸出流中加入該data

//編寫MIME首部的格式

"Content-length:"+data.length+"\r\n"+

//開始寫入輸出流

//這裡一般要先判斷是否是http協定,

//if(request.indexof("HTTP")!=-1)

//write.write(header);

//發送檔案一般直接用輸出流,不要用書寫器

     outfile.write(data);

     outfile.flush();

     outfile.close();

//當檔案不存在的時候

//這裡還是要判斷是否是http協定,如果是先要添加MIME首部

       write.write("HTTP/1.0 404 File Not Found\r\n");

       write.write("Server:Apache 7.0\r\n");

       write.write("Content-type:text/html");

       write.write("\r\n\r\n");

//當不是http協定還要輸出顯示

       write.write("<HTML>\r\n");

       write.write("<HEAD><TITLE>File Not Found</TITLE></HEAD>\r\n");

       write.write("<BODY>HTTP ERROR 404 :File Not Found\r\n");

       write.write("</BODY></HTML>\r\n");

本文轉自 zhao_xiao_long 51CTO部落格,原文連結:http://blog.51cto.com/computerdragon/1192073