天天看點

Java Socket程式設計----通信是這樣煉成的

java最初是作為網絡程式設計語言出現的,其對網絡提供了高度的支援,使得用戶端和伺服器的溝通變成了現實,而在網絡程式設計中,使用最多的就是socket。像大家熟悉的qq、msn都使用了socket相關的技術。下面就讓我們一起揭開socket的神秘面紗。

socket程式設計

一、網絡基礎知識(參考計算機網絡)

關于計算機網絡部分可以參考相關部落格:

tcp/ip協定棧及osi參考模型詳解》

<a target="_blank" href="http://wangdy.blog.51cto.com/3845563/1588379">http://wangdy.blog.51cto.com/3845563/1588379</a>

1、兩台計算機間進行通訊需要以下三個條件:

ip位址、協定、端口号

2、tcp/ip協定:

是目前世界上應用最為廣泛的協定,是以tcp和ip為基礎的不同層次上多個協定的集合,也成tcp/ip協定族、或tcp/ip協定棧

tcp:transmission control protocol 傳輸控制協定

ip:internet protocol 網際網路協定

3、tcp/ip五層模型

應用層:http、ftp、smtp、telnet等

傳輸層:tcp/ip

網絡層:

資料鍊路層:

實體層:網線、雙絞線、網卡等

4、ip位址

為實作網絡中不同計算機之間的通信,每台計算機都必須有一個唯一的辨別---ip位址。

32位二進制

5、端口

區分一台主機的多個不同應用程式,端口号範圍為0-65535,其中0-1023位為系統保留。

如:http:80ftp:21 telnet:23

ip位址+端口号組成了所謂的socket,socket是網絡上運作的程式之間雙向通信鍊路的終結點,是tcp和udp的基礎

6、socket套接字:

網絡上具有唯一辨別的ip位址和端口組合在一起才能構成唯一能識别的辨別符套接字。

socket原理機制:

通信的兩端都有socket

網絡通信其實就是socket間的通信

資料在兩個socket間通過io傳輸     

7、java中的網絡支援

針對網絡通信的不同層次,java提供了不同的api,其提供的網絡功能有四大類:

inetaddress:用于辨別網絡上的硬體資源,主要是ip位址

url:統一資源定位符,通過url可以直接讀取或寫入網絡上的資料

sockets:使用tcp協定實作的網絡通信socket相關的類

datagram:使用udp協定,将資料儲存在使用者資料報中,通過網絡進行通信。

二、inetaddress

inetaddress類用于辨別網絡上的硬體資源,辨別網際網路協定(ip)位址。

該類沒有構造方法       

//擷取本機的inetaddress執行個體 

inetaddress address =inetaddress.getlocalhost(); 

address.gethostname();//擷取計算機名 

address.gethostaddress();//擷取ip位址 

byte[] bytes = address.getaddress();//擷取位元組數組形式的ip位址,以點分隔的四部分 

//擷取其他主機的inetaddress執行個體 

inetaddress address2 =inetaddress.getbyname("其他主機名"); 

inetaddress address3 =inetaddress.getbyname("ip位址"); 

 三、url類

//建立一個url的執行個體 

url baidu =new url("http://www.baidu.com"); 

url url =new url(baidu,"/index.html?username=tom#test");//?表示參數,#表示錨點 

url.getprotocol();//擷取協定 

url.gethost();//擷取主機 

url.getport();//如果沒有指定端口号,根據協定不同使用預設端口。此時getport()方法的傳回值為 -1 

url.getpath();//擷取檔案路徑 

url.getfile();//檔案名,包括檔案路徑+參數 

url.getref();//相對路徑,就是錨點,即#号後面的内容 

url.getquery();//查詢字元串,即參數 

2、使用url讀取網頁内容

通過url對象的openstream()方法可以得到指定資源的輸入流,通過流能夠讀取或通路網頁上的資源 

//使用url讀取網頁内容 

//建立一個url執行個體 

url url =new url("http://www.baidu.com"); 

inputstream is = url.openstream();//通過openstream方法擷取資源的位元組輸入流 

inputstreamreader isr =newinputstreamreader(is,"utf-8");//将位元組輸入流轉換為字元輸入流,如果不指定編碼,中文可能會出現亂碼 

bufferedreader br =newbufferedreader(isr);//為字元輸入流添加緩沖,提高讀取效率 

string data = br.readline();//讀取資料 

while(data!=null){ 

system.out.println(data);//輸出資料 

data = br.readerline(); 

br.close(); 

isr.colose(); 

is.close(); 

四、tcp程式設計

1、tcp協定是面向連接配接的、可靠的、有序的、以位元組流的方式發送資料,通過三次握手方式建立連接配接,形成傳輸資料的通道,在連接配接中進行大量資料的傳輸,效率會稍低

2、java中基于tcp協定實作網絡通信的類

用戶端的socket類

伺服器端的serversocket類

Java Socket程式設計----通信是這樣煉成的

3、socket通信的步驟

① 建立serversocket和socket

② 打開連接配接到socket的輸入/輸出流

③ 按照協定對socket進行讀/寫操作

④ 關閉輸入輸出流、關閉socket

4、伺服器端:

① 建立serversocket對象,綁定監聽端口

② 通過accept()方法監聽用戶端請求

③ 連接配接建立後,通過輸入流讀取用戶端發送的請求資訊

④ 通過輸出流向用戶端發送鄉音資訊

⑤ 關閉相關資源

/** 

 * 基于tcp協定的socket通信,實作使用者登入,服務端 

*/ 

//1、建立一個伺服器端socket,即serversocket,指定綁定的端口,并監聽此端口 

serversocket serversocket =newserversocket(10086);//1024-65535的某個端口 

//2、調用accept()方法開始監聽,等待用戶端的連接配接 

socket socket = serversocket.accept(); 

//3、擷取輸入流,并讀取用戶端資訊 

inputstream is = socket.getinputstream(); 

inputstreamreader isr =newinputstreamreader(is); 

bufferedreader br =newbufferedreader(isr); 

string info =null; 

while((info=br.readline())!=null){ 

system.out.println("我是伺服器,用戶端說:"+info); 

socket.shutdowninput();//關閉輸入流 

//4、擷取輸出流,響應用戶端的請求 

outputstream os = socket.getoutputstream(); 

printwriter pw = new printwriter(os); 

pw.write("歡迎您!"); 

pw.flush(); 

//5、關閉資源 

pw.close(); 

os.close(); 

isr.close(); 

socket.close(); 

serversocket.close(); 

 5、用戶端:

① 建立socket對象,指明需要連接配接的伺服器的位址和端口号

② 連接配接建立後,通過輸出流想伺服器端發送請求資訊

③ 通過輸入流擷取伺服器響應的資訊

④ 關閉響應資源

//用戶端 

//1、建立用戶端socket,指定伺服器位址和端口 

socket socket =newsocket("localhost",10086); 

//2、擷取輸出流,向伺服器端發送資訊 

outputstream os = socket.getoutputstream();//位元組輸出流 

printwriter pw =newprintwriter(os);//将輸出流包裝成列印流 

pw.write("使用者名:admin;密碼:123"); 

socket.shutdownoutput(); 

//3、擷取輸入流,并讀取伺服器端的響應資訊 

bufferedreader br = new bufferedreader(new inputstreamreader(is)); 

string info = null; 

while((info=br.readline())!null){ 

 system.out.println("我是用戶端,伺服器說:"+info); 

//4、關閉資源 

6、應用多線程實作伺服器與多用戶端之間的通信

① 伺服器端建立serversocket,循環調用accept()等待用戶端連接配接

② 用戶端建立一個socket并請求和伺服器端連接配接

③ 伺服器端接受苦讀段請求,建立socket與該客戶建立專線連接配接

④ 建立連接配接的兩個socket在一個單獨的線程上對話

⑤ 伺服器端繼續等待新的連接配接       

//伺服器線程處理 

//和本線程相關的socket 

socket socket =null; 

// 

public serverthread(socket socket){ 

this.socket = socket; 

publicvoid run(){ 

//伺服器處理代碼 

//============================================ 

//伺服器代碼 

serversocket serversocket =newserversocket(10086); 

int count =0;//記錄用戶端的數量 

while(true){ 

socket = serverscoket.accept(); 

serverthread serverthread =newserverthread(socket); 

 serverthread.start(); 

 count++; 

system.out.println("用戶端連接配接的數量:"+count); 

五、udp程式設計

udp協定(使用者資料報協定)是無連接配接的、不可靠的、無序的,速度快

進行資料傳輸時,首先将要傳輸的資料定義成資料報(datagram),大小限制在64k,在資料報中指明資料索要達到的socket(主機位址和端口号),然後再将資料報發送出去

datagrampacket類:表示資料報包

datagramsocket類:進行端到端通信的類

1、伺服器端實作步驟

① 建立datagramsocket,指定端口号

② 建立datagrampacket

③ 接受用戶端發送的資料資訊

④ 讀取資料

//伺服器端,實作基于udp的使用者登入 

//1、建立伺服器端datagramsocket,指定端口 

datagramsocket socket =new datagramsocket(10010); 

//2、建立資料報,用于接受用戶端發送的資料 

byte[] data =newbyte[1024];// 

datagrampacket packet =newdatagrampacket(data,data.length); 

//3、接受用戶端發送的資料 

socket.receive(packet);//此方法在接受資料報之前會一緻阻塞 

//4、讀取資料 

string info =newstring(data,o,data.length); 

system.out.println("我是伺服器,用戶端告訴我"+info); 

//========================================================= 

//向用戶端響應資料 

//1、定義用戶端的位址、端口号、資料 

inetaddress address = packet.getaddress(); 

int port = packet.getport(); 

byte[] data2 = "歡迎您!".geybytes(); 

//2、建立資料報,包含響應的資料資訊 

datagrampacket packet2 = new datagrampacket(data2,data2.length,address,port); 

//3、響應用戶端 

socket.send(packet2); 

2、用戶端實作步驟

① 定義發送資訊

② 建立datagrampacket,包含将要發送的資訊

③ 建立datagramsocket

④ 發送資料

//1、定義伺服器的位址、端口号、資料 

inetaddress address =inetaddress.getbyname("localhost"); 

int port =10010; 

byte[] data ="使用者名:admin;密碼:123".getbytes(); 

//2、建立資料報,包含發送的資料資訊 

datagrampacket packet = newdatagrampacket(data,data,length,address,port); 

//3、建立datagramsocket對象 

datagramsocket socket =newdatagramsocket(); 

//4、向伺服器發送資料 

socket.send(packet); 

//接受伺服器端響應資料 

//====================================== 

//1、建立資料報,用于接受伺服器端響應資料 

byte[] data2 = new byte[1024]; 

datagrampacket packet2 = new datagrampacket(data2,data2.length); 

//2、接受伺服器響應的資料 

socket.receive(packet2); 

string raply = new string(data2,0,packet2.getlenth()); 

system.out.println("我是用戶端,伺服器說:"+reply); 

六、注意問題:

 1、多線程的優先級問題:

根據實際的經驗,适當的降低優先級,否側可能會有程式運作效率低的情況

 2、是否關閉輸出流和輸入流:

對于同一個socket,如果關閉了輸出流,則與該輸出流關聯的socket也會被關閉,是以一般不用關閉流,直接關閉socket即可

 3、使用tcp通信傳輸對象,io中序列化部分

 4、socket程式設計傳遞檔案,io流部分

作者:rocomp

來源:51cto