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類
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