協(xié)議相當于相互通信的程序間達成的一種約定,它規(guī)定了分組報文的結(jié)構(gòu)、交換方式、包含的意義以及怎樣對報文所包含的信息進行解析,TCP/IP 協(xié)議族有 IP 協(xié)議、TCP 協(xié)議和 UDP 協(xié)議?,F(xiàn)在 TCP/IP 協(xié)議族中的主要 socket 類型為流套接字(使用 TCP 協(xié)議)和數(shù)據(jù)報套接字(使用 UDP 協(xié)議)。
TCP 協(xié)議提供面向連接的服務(wù),通過它建立的是可靠地連接。Java 為 TCP 協(xié)議提供了兩個類:Socke 類和 ServerSocket 類。一個 Socket 實例代表了 TCP 連接的一個客戶端,而一個 ServerSocket 實例代表了 TCP 連接的一個服務(wù)器端,一般在 TCP Socket 編程中,客戶端有多個,而服務(wù)器端只有一個,客戶端 TCP 向服務(wù)器端 TCP 發(fā)送連接請求,服務(wù)器端的 ServerSocket 實例則監(jiān)聽來自客戶端的 TCP 連接請求,并為每個請求創(chuàng)建新的 Socket 實例,由于服務(wù)端在調(diào)用 accept()等待客戶端的連接請求時會阻塞,直到收到客戶端發(fā)送的連接請求才會繼續(xù)往下執(zhí)行代碼,因此要為每個 Socket 連接開啟一個線程。服務(wù)器端要同時處理 ServerSocket 實例和 Socket 實例,而客戶端只需要使用 Socket 實例。另外,每個 Socket 實例會關(guān)聯(lián)一個 InputStream 和 OutputStream 對象,我們通過將字節(jié)寫入套接字的 OutputStream 來發(fā)送數(shù)據(jù),并通過從 InputStream 來接收數(shù)據(jù)。
客戶端向服務(wù)器端發(fā)送連接請求后,就被動地等待服務(wù)器的響應(yīng)。典型的 TCP 客戶端要經(jīng)過下面三步操作:
服務(wù)端的工作是建立一個通信終端,并被動地等待客戶端的連接。
典型的 TCP 服務(wù)端執(zhí)行如下兩步操作:
創(chuàng)建一個 ServerSocket 實例并指定本地端口,用來監(jiān)聽客戶端在該端口發(fā)送的 TCP 連接請求;
重復(fù)執(zhí)行:
調(diào)用 ServerSocket 的 accept()方法以獲取客戶端連接,并通過其返回值創(chuàng)建一個 Socket 實例;
下面給出一個客戶端服務(wù)端 TCP 通信的 Demo,該客戶端在 20006 端口請求與服務(wù)端建立 TCP 連接,客戶端不斷接收鍵盤輸入,并將其發(fā)送到服務(wù)端,服務(wù)端在接收到的數(shù)據(jù)前面加上“echo”字符串,并將組合后的字符串發(fā)回給客戶端,如此循環(huán),直到客戶端接收到鍵盤輸入“bye”為止。
客戶端代碼如下:
package zyb.org.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
public class Client1 {
public static void main(String[] args) throws IOException {
//客戶端請求與本機在20006端口建立TCP連接
Socket client = new Socket("127.0.0.1", 20006);
client.setSoTimeout(10000);
//獲取鍵盤輸入
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
//獲取Socket的輸出流,用來發(fā)送數(shù)據(jù)到服務(wù)端
PrintStream out = new PrintStream(client.getOutputStream());
//獲取Socket的輸入流,用來接收從服務(wù)端發(fā)送過來的數(shù)據(jù)
BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
boolean flag = true;
while(flag){
System.out.print("輸入信息:");
String str = input.readLine();
//發(fā)送數(shù)據(jù)到服務(wù)端
out.println(str);
if("bye".equals(str)){
flag = false;
}else{
try{
//從服務(wù)器端接收數(shù)據(jù)有個時間限制(系統(tǒng)自設(shè),也可以自己設(shè)置),超過了這個時間,便會拋出該異常
String echo = buf.readLine();
System.out.println(echo);
}catch(SocketTimeoutException e){
System.out.println("Time out, No response");
}
}
}
input.close();
if(client != null){
//如果構(gòu)造函數(shù)建立起了連接,則關(guān)閉套接字,如果沒有建立起連接,自然不用關(guān)閉
client.close(); //只關(guān)閉socket,其關(guān)聯(lián)的輸入輸出流也會被關(guān)閉
}
}
}
服務(wù)端需要用到多線程,這里單獨寫了一個多線程類,代碼如下:
package zyb.org.server;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
/**
* 該類為多線程類,用于服務(wù)端
*/
public class ServerThread implements Runnable {
private Socket client = null;
public ServerThread(Socket client){
this.client = client;
}
@Override
public void run() {
try{
//獲取Socket的輸出流,用來向客戶端發(fā)送數(shù)據(jù)
PrintStream out = new PrintStream(client.getOutputStream());
//獲取Socket的輸入流,用來接收從客戶端發(fā)送過來的數(shù)據(jù)
BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
boolean flag =true;
while(flag){
//接收從客戶端發(fā)送過來的數(shù)據(jù)
String str = buf.readLine();
if(str == null || "".equals(str)){
flag = false;
}else{
if("bye".equals(str)){
flag = false;
}else{
//將接收到的字符串前面加上echo,發(fā)送到對應(yīng)的客戶端
out.println("echo:" + str);
}
}
}
out.close();
client.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
服務(wù)端處理 TCP 連接請求的代碼如下:
package zyb.org.server;
import java.net.ServerSocket;
import java.net.Socket;
public class Server1 {
public static void main(String[] args) throws Exception{
//服務(wù)端在20006端口監(jiān)聽客戶端請求的TCP連接
ServerSocket server = new ServerSocket(20006);
Socket client = null;
boolean f = true;
while(f){
//等待客戶端的連接,如果沒有獲取連接
client = server.accept();
System.out.println("與客戶端連接成功!");
//為每個客戶端連接開啟一個線程
new Thread(new ServerThread(client)).start();
}
server.close();
}
}
執(zhí)行結(jié)果截圖如下:
http://wiki.jikexueyuan.com/project/java-socket/images/tcpresult.jpg" alt="" />
http://wiki.jikexueyuan.com/project/java-socket/images/tcpresult1.jpg" alt="" />