1.服務器端
1.創建ServerSocket對象,可在構造子中指定監聽的端口;
private int port = 8000;
private ServerSocket serverSocket;
……
serverSocket = new ServerSocket(port);
2.服務器端調用ServerSocket對象的accept()方法,該方法一直監聽端口,等待客戶的連接請求,如 果接收到一個連接請求,accept()方法就會返回一個Socket對象,這個Socket對象與客戶端的Socket對象 將形成一條通信線路;
Socket socket = null;
socket = serverSocket.accept(); // 等待客戶連接
3.Socket類提供了getInputStream()方法和getOutputStream()方法。
InputStream socketIn = socket.getInputStream();
OutputStream socketOut = socket.getOutputStream();
源代碼EchoServer.java
public class EchoServer {
private int port = 8000;
private ServerSocket serverSocket;
public EchoServer() throws IOException {
serverSocket = new ServerSocket(port);
System.out.println("Server Start");
}
public String echo(String msg) {
return "echo:" + msg;
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut, true);
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
public void service() {
while (true) {
Socket socket = null;
try {
socket = serverSocket.accept(); // 等待客戶連接
System.out.println("New connection accepted "
+ socket.getInetAddress() + ":" + socket.getPort());
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
String msg = null;
while ((msg = br.readLine()) != null) {
System.out.println(msg);
pw.println(echo(msg));
if (msg.equals("bye")) // 如果客戶發送的消息為“bye”,就結束通信
break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null)
socket.close(); // 斷開連接
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String args[]) throws IOException {
new EchoServer().service();
}
}
2.客戶端
1.創建一個Socket對象,指定服務器端的地址和端口;
private String host = "localhost";
private int port = 8000;
private Socket socket;
……
socket = new Socket(host, port);
這裡作為客戶端,它的端口是由操作系統隨機產生的。
2.Socket類提供了getInputStream()方法和getOutputStream()方法。
InputStream socketIn = socket.getInputStream();
OutputStream socketOut = socket.getOutputStream();
源代碼EchoClient.java
public class EchoClient {
private String host = "localhost";
private int port = 8000;
private Socket socket;
public EchoClient() throws IOException {
socket = new Socket(host, port);
}
public static void main(String args[]) throws IOException {
new EchoClient().talk();
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut, true);
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
public void talk() throws IOException {
try {
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
BufferedReader localReader = new BufferedReader(
new InputStreamReader(System.in));
String msg = null;
while ((msg = localReader.readLine()) != null) {
pw.println(msg);
System.out.println(br.readLine());
if (msg.equals("bye"))
break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.關閉Socket
1.關閉Socket的代碼;
try {
……
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Socket類提供3個狀態測試方法。
-isClosed():如果Socket已經連接到遠程主機,並且還沒有關閉,則返回true;
-isConnected():如果Socket曾經連接到遠程主機,則返回true;
-isBound():如果Socket已經與一個本地端口綁定,則返回true。
判斷一個Socket對象當前是否處於連接狀態,
Boolean isConnected = socket.isConnected() && !socket.isClosed();
2.處理關閉
(1)當進程A與進程B交換的是字符流,並且是一行一行地讀寫數據時,可以事先約定一個特殊的標志 。
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
String msg = null;
while ((msg = br.readLine()) != null) {
System.out.println(msg);
pw.println(echo(msg));
if (msg.equals("bye")) // 如果客戶發送的消息為“bye”,就結束通信
break;
}
(2)進程A先發送一個消息,告訴進程B所發送的正文長度,然後發送正文。進程B只要讀取完該長度 的數據就可以停止讀數據。
(3)進程A發送完所有數據後,關閉Socket。當進程B讀入進程A發送的所有數據後,再次執行輸入流 的read()方法時,該方法返回-1.
InputStream socketIn = socket.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len = -1;
while ((len = socketIn.read(buff)) != -1) {
buffer.write(buff, 0, len);
}
System.out.println(new String(buffer.toByteArray()));
(4)當調用Socket的close()方法關閉Socket時,它的輸入流和輸出流都被關閉。如果僅僅希望關閉 輸入或輸出流其中之一,可調用半關閉方法:shutdownInput()和shutdownOutput()。先後調用Socket的 shutdownInput()和shutdownOutput()方法,僅僅關閉輸入流和輸出流,並不等價於調用close()方法。在 通信結束後仍然需要調用close()方法,因為該方法才會釋放Socket占用的資源。
4.多線程服務器
EchoServer只能順序的處理Client端的請求,這裡使用ExecutorService指定一個線程池用於處理連接 請求。
private ExecutorService executorService; // 線程池
private final int POOL_SIZE = 4; // 單個CPU時線程池中工作線程的數目
…….
executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors()* POOL_SIZE);
……
try {
socket = serverSocket.accept();
executorService.execute(new Handler(socket));
} catch (IOException e) {
e.printStackTrace();
}
Hander類封裝了原來處理連接請求的邏輯,只要當前線程池中有空閒的線程,就可以用於處理請求。
源代碼MultiEchoServer.java
public class MultiEchoServer {
private int port = 8000;
private ServerSocket serverSocket;
private ExecutorService executorService; // 線程池
private final int POOL_SIZE = 4; // 單個CPU時線程池中工作線程的數目
public MultiEchoServer() throws IOException {
serverSocket = new ServerSocket(port);
executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors()
* POOL_SIZE);
System.out.println("Server Start");
}
public void service() {
while (true) {
Socket socket = null;
try {
socket = serverSocket.accept();
executorService.execute(new Handler(socket));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) throws IOException {
new MultiEchoServer().service();
}
}
class Handler implements Runnable {
private Socket socket;
public Handler(Socket socket) {
this.socket = socket;
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut, true);
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
public String echo(String msg) {
return "echo:" + msg;
}
public void run() {
try {
System.out.println("New connection accepted "
+ socket.getInetAddress() + ":" + socket.getPort());
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
String msg = null;
while ((msg = br.readLine()) != null) {
System.out.println(msg);
pw.println(echo(msg));
if (msg.equals("bye"))
break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
參考 孫衛琴,《Java網絡編程精解》