在網(wǎng)絡(luò)通訊中,第一次主動發(fā)起通訊的程序被稱作客戶端(Client)程序,簡稱客戶端,而在第一次通訊中等待連接的程序被稱作服務(wù)器端(Server)程序,簡稱服務(wù)器。一旦通訊建立,則客戶端和服務(wù)器端完全一樣,沒有本質(zhì)的區(qū)別。
“請求-響應(yīng)”模式:
1. Socket類:發(fā)送TCP消息。
2. ServerSocket類:創(chuàng)建服務(wù)器。
套接字是一種進程間的數(shù)據(jù)交換機制。這些進程既可以在同一機器上,也可以在通過網(wǎng)絡(luò)連接的不同機器上。換句話說,套接字起到通信端點的作用。單個套接字是一個端點,而一對套接字則構(gòu)成一個雙向通信信道,使非關(guān)聯(lián)進程可以在本地或通過網(wǎng)絡(luò)進行數(shù)據(jù)交換。一旦建立套接字連接,數(shù)據(jù)即可在相同或不同的系統(tǒng)中雙向或單向發(fā)送,直到其中一個端點關(guān)閉連接。套接字與主機地址和端口地址相關(guān)聯(lián)。主機地址就是客戶端或服務(wù)器程序所在的主機的IP地址。端口地址是指客戶端或服務(wù)器程序使用的主機的通信端口。
在客戶端和服務(wù)器中,分別創(chuàng)建獨立的Socket,并通過Socket的屬性,將兩個Socket進行連接,這樣,客戶端和服務(wù)器通過套接字所建立的連接使用輸入輸出流進行通信。
TCP/IP套接字是最可靠的雙向流協(xié)議,使用TCP/IP可以發(fā)送任意數(shù)量的數(shù)據(jù)。
實際上,套接字只是計算機上已編號的端口。如果發(fā)送方和接收方計算機確定好端口,他們就可以通信了。
如圖所示為客戶端與服務(wù)器端的通信關(guān)系圖:
TCP/IP通信連接的簡單過程:
位于A計算機上的TCP/IP軟件向B計算機發(fā)送包含端口號的消息,B計算機的TCP/IP軟件接收該消息,并進行檢查,查看是否有它知道的程序正在該端口上接收消息。如果有,他就將該消息交給這個程序。
要使程序有效地運行,就必須有一個客戶端和一個服務(wù)器。
通過Socket的編程順序:
1. 創(chuàng)建服務(wù)器ServerSocket,在創(chuàng)建時,定義ServerSocket的監(jiān)聽端口(在這個端口接收客戶端發(fā)來的消息)。
2. ServerSocket調(diào)用accept()方法,使之處于阻塞狀態(tài)。
3. 創(chuàng)建客戶端Socket,并設(shè)置服務(wù)器的IP及端口。
4. 客戶端發(fā)出連接請求,建立連接。
5. 分別取得服務(wù)器和客戶端Socket的InputStream和OutputStream。
6. 利用Socket和ServerSocket進行數(shù)據(jù)傳輸。
7. 關(guān)閉流及Socket。
【示例12-7】TCP:單向通信Socket之服務(wù)器端
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 最簡單的服務(wù)器端代碼
* @author Administrator
*/
public class BasicSocketServer {
public static void main(String[] args) {
Socket socket = null;
BufferedWriter bw = null;
try {
// 建立服務(wù)器端套接字:指定監(jiān)聽的接口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println('服務(wù)端建立監(jiān)聽');
// 監(jiān)聽,等待客戶端請求,并愿意接收連接
socket = serverSocket.accept();
// 獲取socket的輸出流,并使用緩沖流進行包裝
bw = new BufferedWriter(new
OutputStreamWriter(socket.getOutputStream()));
// 向客戶端發(fā)送反饋信息
bw.write('hhhh');
} catch (IOException e) {
e.printStackTrace();
} finally {
// 關(guān)閉流及socket連接
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
【示例12-8】TCP:單向通信Socket之客戶端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
/**
* 最簡單的Socket客戶端
* @author Administrator
*/
public class BasicSocketClient {
public static void main(String[] args) {
Socket socket = null;
BufferedReader br = null;
try {
/*
* 創(chuàng)建Scoket對象:指定要連接的服務(wù)器的IP和端口而不是自己機器的
* 端口。發(fā)送端口是隨機的。
*/
socket = new Socket(InetAddress.getLocalHost(), 8888);
//獲取scoket的輸入流,并使用緩沖流進行包裝
br = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
//接收服務(wù)器端發(fā)送的信息
System.out.println(br.readLine());
} catch (Exception e) {
e.printStackTrace();
} finally {
// 關(guān)閉流及socket連接
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
【示例12-9】TCP:雙向通信Socket之服務(wù)器端
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args){
Socket socket = null;
BufferedReader in = null;
BufferedWriter out = null;
BufferedReader br = null;
try {
//創(chuàng)建服務(wù)器端套接字:指定監(jiān)聽端口
ServerSocket server = new ServerSocket(8888);
//監(jiān)聽客戶端的連接
socket = server.accept();
//獲取socket的輸入輸出流接收和發(fā)送信息
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new BufferedWriter(new
OutputStreamWriter(socket.getOutputStream()));
br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
//接收客戶端發(fā)送的信息
String str = in.readLine();
System.out.println('客戶端說:' + str);
String str2 = '';
//如果客戶端發(fā)送的是“end”則終止連接
if (str.equals('end')){
break;
}
//否則,發(fā)送反饋信息
str2 = br.readLine(); // 讀到\n為止,因此一定要輸入換行符!
out.write(str2 + '\n');
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//關(guān)閉資源
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
【示例12-10】TCP:雙向通信Socket之客戶端
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public static void main(String[] args) {
Socket socket = null;
BufferedReader in = null;
BufferedWriter out = null;
BufferedReader wt = null;
try {
//創(chuàng)建Socket對象,指定服務(wù)器端的IP與端口
socket = new Socket(InetAddress.getLocalHost(), 8888);
//獲取scoket的輸入輸出流接收和發(fā)送信息
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new BufferedWriter(new
OutputStreamWriter(socket.getOutputStream()));
wt = new BufferedReader(new InputStreamReader(System.in));
while (true) {
//發(fā)送信息
String str = wt.readLine();
out.write(str + '\n');
out.flush();
//如果輸入的信息為“end”則終止連接
if (str.equals('end')) {
break;
}
//否則,接收并輸出服務(wù)器端信息
System.out.println('服務(wù)器端說:' + in.readLine());
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 關(guān)閉資源
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (wt != null) {
try {
wt.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
執(zhí)行結(jié)果如圖所示
菜鳥雷區(qū)
運行時,要先啟動服務(wù)器端,再啟動客戶端,才能得到正常的運行效果。
但是,上面這個程序,必須按照安排好的順序,服務(wù)器和客戶端一問一答!不夠靈活!!可以使用多線程實現(xiàn)更加靈活的雙向通訊!!
服務(wù)器端:一個線程專門發(fā)送消息,一個線程專門接收消息。
客戶端:一個線程專門發(fā)送消息,一個線程專門接收消息。
【示例12-11】TCP:聊天室之服務(wù)器端
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ChatServer {
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
BufferedReader in = null;
try {
server = new ServerSocket(8888);
socket = server.accept();
//創(chuàng)建向客戶端發(fā)送消息的線程,并啟動
new ServerThread(socket).start();
// main線程負(fù)責(zé)讀取客戶端發(fā)來的信息
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
String str = in.readLine();
System.out.println('客戶端說:' + str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 專門向客戶端發(fā)送消息的線程
*
* @author Administrator
*
*/
class ServerThread extends Thread {
Socket ss;
BufferedWriter out;
BufferedReader br;
public ServerThread(Socket ss) {
this.ss = ss;
try {
out = new BufferedWriter(new OutputStreamWriter(ss.getOutputStream()));
br = new BufferedReader(new InputStreamReader(System.in));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
while (true) {
String str2 = br.readLine();
out.write(str2 + '\n');
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(out != null){
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(br != null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
【示例12-12】TCP:聊天室之客戶端
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class ChatClient {
public static void main(String[] args) {
Socket socket = null;
BufferedReader in = null;
try {
socket = new Socket(InetAddress.getByName('127.0.1.1'), 8888);
// 創(chuàng)建向服務(wù)器端發(fā)送信息的線程,并啟動
new ClientThread(socket).start();
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// main線程負(fù)責(zé)接收服務(wù)器發(fā)來的信息
while (true) {
System.out.println('服務(wù)器說:' + in.readLine());
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 用于向服務(wù)器發(fā)送消息
*
* @author Administrator
*
*/
class ClientThread extends Thread {
Socket s;
BufferedWriter out;
BufferedReader wt;
public ClientThread(Socket s) {
this.s = s;
try {
out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
wt = new BufferedReader(new InputStreamReader(System.in));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
while (true) {
String str = wt.readLine();
out.write(str + '\n');
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (wt != null) {
wt.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
執(zhí)行結(jié)果如圖所示: