套接字
①網絡通信使用IP地址標識Internet上的計算機,使用端口號標識服務器上的進程(程序)。
②服務器上的每個程序都佔用一個端口號,用於找到程序和與程序交互信息。
③端口號被規定爲一個16位的0~65535之間的整數,其中**,0-1023被預先定義的服務通信佔用**(如telnet佔用端口23,http佔用端口80等),除非我們需要訪問這些特定服務,否則,就應該使用1024–65535這些端口中的某一個進行通信,以免發生端口衝突。
④當兩個程序需要通訊時,它們可以通過使用socket類建立套接字對象並連接在一起(端口號與IP地址的組合得出一個網絡套接字)。
客戶端套接字
①客戶端的程序使用Socket類建立負責連接到服務器的套接字對象。
②Socket的構造方法是Socket(String host,int port),參數host是服務器的IP地址,port是一個端口號。建立套接字對象可能會發生IOException異常:
try{
Socket mysocket = new Socket("http://192.168.0.78",2010);
}catch(IOException e){
}
③當套接字對象mysocket建立後,mysocket可以使用方法getInputStream()獲得一個輸入流,這個輸入流的源和服務器端的一個輸入流的目的地剛好相同,因此客戶端用輸入流可以讀取服務器寫入到輸出流中的數據。
④mysocket使用方法getOutputStream()獲得一個輸出流,這個輸出流的目的地和服務器端的一個輸入流的源剛好相同,因此服務器用輸入流可以讀取客戶寫入到輸出流中的數據。
ServerSocket對象與服務器端套接字
①客戶負責建立連接到服務器的套接字對象,即客戶負責呼叫。爲了能使客戶成功地連接到服務器,服務器必須建立一個ServerSocket對象,該對象通過將客戶端的套接字對象和服務器端的一個套接字對象連接起來,從而達到連接的目的。
②ServerSocket的構造方法是ServerSocket(int port),port是一個端口號。port必須和客戶呼叫的端口號相同。建立ServerSocket對象可能發生IOException異常:
try{
ServerSocket serverForClient = new ServerSocket(2010);
}catch(IOException e){
}
例如:2010端口已被佔用時,就會發生IOException異常。
③當服務器的ServerSocket對象serverForClient建立後,就可以使用方法**accept()**將客戶的套接字和服務器端的套接字連接起來。
try{
Socket sc = serverForClient.accept();
}catch(IOException e){
}
④所謂"接收"客戶的套接字連接是指服務器端的ServerSocket對象:serverForClient調用accept()方法會返回一個與客戶端Socket對象相連接的Socket對象sc。
⑤駐留在服務器端的這個Socket對象sc調用getOutputStream()獲得的輸出流,將指向客戶端Socket對象mysocket調用getInputStream()獲得的那個輸入流,即服務器端的輸出流的目的地和客戶端輸入流的源剛好相同 ;同樣,服務器端的這個Socket對象sc調用getInputStream()獲得的輸入流,將指向客戶端Socket對象mysocket調用getOutputStream()獲得的那個輸出流,即服務器端的輸入流的源和客戶端輸出流的源剛好相同
⑥accept()方法也會阻塞線程的繼續執行,直到接收到客戶的呼叫。也就是說,如果沒有客戶呼叫服務器,那麼下述代碼中的System.out.println(“hello”)不會被執行:
try{
Socket sc = server_socket.accept();
System.out.println("hello");
}catch(IOException e){
}
⑦連接建立後,服務器端的套接字對象調用getInetAddress()方法可以獲取一個InetAddress對象,該對象含有客戶端的IP地址和域名,同樣,客戶端的套接字對象調用getInputStream()方法可以獲取一個InetAddress對象,該對象含有服務器端的IP地址和域名。
⑧雙方通信完畢後,套接字應使用close()方法關閉套接字連接。
⑨ServerSocket對象可以調用setTimeout(int timeout)方法設置超時值(ms),timeout是一個正值,當ServerSocket對象調用accept方法堵塞的時間一旦超過timeout時,將觸發SocketTimeoutException。
Client.java
package SocketClass;
import java.io.*;
import java.net.*;
public class Client {
public static void main(String []args){
String []mess = {"1+1在什麼情況下不等於2","狗爲什麼不生跳蚤","什麼東西能看、能吃、能坐"};
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try{
mysocket = new Socket("127.0.0.1",2010);
in = new DataInputStream(mysocket.getInputStream());
out = new DataOutputStream(mysocket.getOutputStream());
for(int i = 0;i < mess.length;i++){
out.writeUTF(mess[i]);
String s = in.readUTF();//in讀取信息,堵塞狀態
System.out.println("客戶收到服務器的回答"+s);
Thread.sleep(500);
}
}catch (Exception e){
System.out.println("服務器已斷開"+e);
}
}
}
Server.java
package SocketClass;
import java.io.*;
import java.net.*;
public class Server {
public static void main(String args[]){
String []answer = {"在算錯的情況下","狗只能生狗","電視、麪包、沙發"};
ServerSocket serverForClient = null;
Socket socketOnServer = null;
DataOutputStream out = null;
DataInputStream in = null;
try{
serverForClient = new ServerSocket(2010);
}catch (Exception e){
System.out.println(e);
}
try{
System.out.println("等待客戶呼叫");
socketOnServer = serverForClient.accept();//堵塞狀態,除非有客戶呼叫
out = new DataOutputStream(socketOnServer.getOutputStream());
in = new DataInputStream(socketOnServer.getInputStream());
for(int i = 0;i<answer.length;i++){
String s = in.readUTF();//in讀取信息,堵塞狀態
System.out.println("服務器收到客戶的提問:"+s);
out.writeUTF(answer[i]);
Thread.sleep(500);
}
}catch (Exception e){
System.out.println("客戶已斷開"+e);
}
}
}
使用多線程技術
①需要注意的是,從套接字連接中讀取數據與從文件中讀取數據有着很大的不同。儘管二者都是輸入流,但從文件中讀取數據時,所有的數據都已經在文件中了,而使用套接字連接時,可能在另一端數據發送出來之前,就已經開始試着讀取了,這時就會堵塞本線程,直到該讀取方法成功讀取到信息,本線程才能繼續執行後序的操作。因此,服務器端接收到一個客戶的套接字後,就應該啓動一個專門爲該客戶服務的線程。
可以使用Socket類的不帶參數的構造方法Socket()創建一個套接字對象,該對象再調用
public void connect(SocketAddress endpoint) throws IOException
請求和參數SocketAddress指定地址的服務器端的套接字建立連接。
爲了使用connect()方法,可以使用SocketAddress的子類InetSocketAddress創建一個對象,InetSocketAddress的構造方法是:
public InetSocketAddress(InetAddress addr ,int port)