Socket程式設計
一、網絡基礎知識(參考計算機網絡)
關于計算機網絡部分可以參考相關部落格:
《TCP/IP協定棧及OSI參考模型詳解》
http://wangdy.blog.51cto.com/3845563/1588379
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:80 FTP: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)位址。
該類沒有構造方法
1 //擷取本機的InetAddress執行個體
2 InetAddress address =InetAddress.getLocalHost();
3 address.getHostName();//擷取計算機名
4 address.getHostAddress();//擷取IP位址
5 byte[] bytes = address.getAddress();//擷取位元組數組形式的IP位址,以點分隔的四部分
6
7 //擷取其他主機的InetAddress執行個體
8 InetAddress address2 =InetAddress.getByName(“其他主機名”);
9 InetAddress address3 =InetAddress.getByName(“IP位址”);
複制代碼
三、URL類
1、URL(Uniform Resource Locator)統一資源定位符,表示Internet上某一資源的位址,協定名:資源名稱
1 //建立一個URL的執行個體
2 URL baidu =new URL(“http://www.baidu.com“);
3 URL url =new URL(baidu,”/index.html?username=tom#test”);//?表示參數,#表示錨點
4 url.getProtocol();//擷取協定
5 url.getHost();//擷取主機
6 url.getPort();//如果沒有指定端口号,根據協定不同使用預設端口。此時getPort()方法的傳回值為 -1
7 url.getPath();//擷取檔案路徑
8 url.getFile();//檔案名,包括檔案路徑+參數
9 url.getRef();//相對路徑,就是錨點,即#号後面的内容
10 url.getQuery();//查詢字元串,即參數
2、使用URL讀取網頁内容
通過URL對象的openStream()方法可以得到指定資源的輸入流,通過流能夠讀取或通路網頁上的資源
1 //使用URL讀取網頁内容
2 //建立一個URL執行個體
3 URL url =new URL(“http://www.baidu.com“);
4 InputStream is = url.openStream();//通過openStream方法擷取資源的位元組輸入流
5 InputStreamReader isr =newInputStreamReader(is,”UTF-8”);//将位元組輸入流轉換為字元輸入流,如果不指定編碼,中文可能會出現亂碼
6 BufferedReader br =newBufferedReader(isr);//為字元輸入流添加緩沖,提高讀取效率
7 String data = br.readLine();//讀取資料
8 while(data!=null){
9 System.out.println(data);//輸出資料
10 data = br.readerLine();
11 }
12 br.close();
13 isr.colose();
14 is.close();
四、TCP程式設計
1、TCP協定是面向連接配接的、可靠的、有序的、以位元組流的方式發送資料,通過三次握手方式建立連接配接,形成傳輸資料的通道,在連接配接中進行大量資料的傳輸,效率會稍低
2、Java中基于TCP協定實作網絡通信的類
用戶端的Socket類
伺服器端的ServerSocket類
3、Socket通信的步驟
① 建立ServerSocket和Socket
② 打開連接配接到Socket的輸入/輸出流
③ 按照協定對Socket進行讀/寫操作
④ 關閉輸入輸出流、關閉Socket
4、伺服器端:
① 建立ServerSocket對象,綁定監聽端口
② 通過accept()方法監聽用戶端請求
③ 連接配接建立後,通過輸入流讀取用戶端發送的請求資訊
④ 通過輸出流向用戶端發送鄉音資訊
⑤ 關閉相關資源
1
4 //1、建立一個伺服器端Socket,即ServerSocket,指定綁定的端口,并監聽此端口
5 ServerSocket serverSocket =newServerSocket(10086);//1024-65535的某個端口
6 //2、調用accept()方法開始監聽,等待用戶端的連接配接
7 Socket socket = serverSocket.accept();
8 //3、擷取輸入流,并讀取用戶端資訊
9 InputStream is = socket.getInputStream();
10 InputStreamReader isr =newInputStreamReader(is);
11 BufferedReader br =newBufferedReader(isr);
12 String info =null;
13 while((info=br.readLine())!=null){
14 System.out.println(“我是伺服器,用戶端說:”+info);
15 }
16 socket.shutdownInput();//關閉輸入流
17 //4、擷取輸出流,響應用戶端的請求
18 OutputStream os = socket.getOutputStream();
19 PrintWriter pw = new PrintWriter(os);
20 pw.write(“歡迎您!”);
21 pw.flush();
22
23
24 //5、關閉資源
25 pw.close();
26 os.close();
27 br.close();
28 isr.close();
29 is.close();
30 socket.close();
31 serverSocket.close();
5、用戶端:
① 建立Socket對象,指明需要連接配接的伺服器的位址和端口号
② 連接配接建立後,通過輸出流想伺服器端發送請求資訊
③ 通過輸入流擷取伺服器響應的資訊
④ 關閉響應資源
1 //用戶端
2 //1、建立用戶端Socket,指定伺服器位址和端口
3 Socket socket =newSocket(“localhost”,10086);
4 //2、擷取輸出流,向伺服器端發送資訊
5 OutputStream os = socket.getOutputStream();//位元組輸出流
6 PrintWriter pw =newPrintWriter(os);//将輸出流包裝成列印流
7 pw.write(“使用者名:admin;密碼:123”);
8 pw.flush();
9 socket.shutdownOutput();
10 //3、擷取輸入流,并讀取伺服器端的響應資訊
11 InputStream is = socket.getInputStream();
12 BufferedReader br = new BufferedReader(new InputStreamReader(is));
13 String info = null;
14 while((info=br.readLine())!null){
15 System.out.println(“我是用戶端,伺服器說:”+info);
16 }
17
18 //4、關閉資源
19 br.close();
20 is.close();
21 pw.close();
22 os.close();
23 socket.close();
6、應用多線程實作伺服器與多用戶端之間的通信
① 伺服器端建立ServerSocket,循環調用accept()等待用戶端連接配接
② 用戶端建立一個socket并請求和伺服器端連接配接
③ 伺服器端接受苦讀段請求,建立socket與該客戶建立專線連接配接
④ 建立連接配接的兩個socket在一個單獨的線程上對話
⑤ 伺服器端繼續等待新的連接配接
1 //伺服器線程處理
2 //和本線程相關的socket
3 Socket socket =null;
4 //
5 public serverThread(Socket socket){
6 this.socket = socket;
7 }
8
9 publicvoid run(){
10 //伺服器處理代碼
11 }
12
13 //============================================
14 //伺服器代碼
15 ServerSocket serverSocket =newServerSocket(10086);
16 Socket socket =null;
17 int count =0;//記錄用戶端的數量
18 while(true){
19 socket = serverScoket.accept();
20 ServerThread serverThread =newServerThread(socket);
21 serverThread.start();
22 count++;
23 System.out.println(“用戶端連接配接的數量:”+count);
24 }
五、UDP程式設計
UDP協定(使用者資料報協定)是無連接配接的、不可靠的、無序的,速度快
進行資料傳輸時,首先将要傳輸的資料定義成資料報(Datagram),大小限制在64k,在資料報中指明資料索要達到的Socket(主機位址和端口号),然後再将資料報發送出去
DatagramPacket類:表示資料報包
DatagramSocket類:進行端到端通信的類
1、伺服器端實作步驟
① 建立DatagramSocket,指定端口号
② 建立DatagramPacket
③ 接受用戶端發送的資料資訊
④ 讀取資料
1 //伺服器端,實作基于UDP的使用者登入
2 //1、建立伺服器端DatagramSocket,指定端口
3 DatagramSocket socket =new datagramSocket(10010);
4 //2、建立資料報,用于接受用戶端發送的資料
5 byte[] data =newbyte[1024];//
6 DatagramPacket packet =newDatagramPacket(data,data.length);
7 //3、接受用戶端發送的資料
8 socket.receive(packet);//此方法在接受資料報之前會一緻阻塞
9 //4、讀取資料
10 String info =newString(data,o,data.length);
11 System.out.println(“我是伺服器,用戶端告訴我”+info);
12
13
14 //=========================================================
15 //向用戶端響應資料
16 //1、定義用戶端的位址、端口号、資料
17 InetAddress address = packet.getAddress();
18 int port = packet.getPort();
19 byte[] data2 = “歡迎您!”.geyBytes();
20 //2、建立資料報,包含響應的資料資訊
21 DatagramPacket packet2 = new DatagramPacket(data2,data2.length,address,port);
22 //3、響應用戶端
23 socket.send(packet2);
24 //4、關閉資源
25 socket.close();
2、用戶端實作步驟
① 定義發送資訊
② 建立DatagramPacket,包含将要發送的資訊
③ 建立DatagramSocket
④ 發送資料
1 //用戶端
2 //1、定義伺服器的位址、端口号、資料
3 InetAddress address =InetAddress.getByName(“localhost”);
4 int port =10010;
5 byte[] data =”使用者名:admin;密碼:123”.getBytes();
6 //2、建立資料報,包含發送的資料資訊
7 DatagramPacket packet = newDatagramPacket(data,data,length,address,port);
8 //3、建立DatagramSocket對象
9 DatagramSocket socket =newDatagramSocket();
10 //4、向伺服器發送資料
11 socket.send(packet);
12
13
14 //接受伺服器端響應資料
15 //======================================
16 //1、建立資料報,用于接受伺服器端響應資料
17 byte[] data2 = new byte[1024];
18 DatagramPacket packet2 = new DatagramPacket(data2,data2.length);
19 //2、接受伺服器響應的資料
20 socket.receive(packet2);
21 String raply = new String(data2,0,packet2.getLenth());
22 System.out.println(“我是用戶端,伺服器說:”+reply);
23 //4、關閉資源
24 socket.close();
六、注意問題:
1、多線程的優先級問題:
根據實際的經驗,适當的降低優先級,否側可能會有程式運作效率低的情況
2、是否關閉輸出流和輸入流:
對于同一個socket,如果關閉了輸出流,則與該輸出流關聯的socket也會被關閉,是以一般不用關閉流,直接關閉socket即可
3、使用TCP通信傳輸對象,IO中序列化部分
4、socket程式設計傳遞檔案,IO流部分