ServerSocket
構造方法
ServerSocket serverSocket = new ServerSocket();
ServerSocket(); //無參數
ServerSocket(int port); //指定端口
ServerSocket(int port,int backlog); //指定端口、隊列數
ServerSocket(int port,int backlog,InetAddress bindAddr); //指定端口、隊列數、綁定IP
注:當port為0時,隨機分配空閑端口。
無參數綁定端口
serverSocket.bind(SocketAddress endpoint); //指定端口
serverSocket.bind(SocketAddress endpoint,int backlog) //指定端口、隊列數
ServerSocket選項
SO_TIMEOUT:等待客戶連接的超時時間
serverSocket.setSoTimeout(int timeout); //設置(單位為毫秒,為0,則永不超時)
serverSocket.getSoTimeout(); //查看超時時間
SO_REUSEADDR:是否允許重用服務器所綁定的地址(需在連接前設置)
serverSocket.setResuseAddress(boolean on); //設置
serverSocket.getResuseAddress(); //查看是否開啟
SO_RCVBUF:接收數據的緩沖區大小
serverSocket.setReceiveBufferSize(int size); //設置
serverSocket.getReceiveBufferSize(); //查看緩沖區大小
設定連接時間、延遲和帶寬
參數(相對權重)
三個參數之間的相對大小決定相應參數的相對重要性。
- connectionTime:最少時間建立連接
- latency:最小延遲
- bandwidth:最高帶寬
serverSocket.setPerformancePreferences(int connectionTime,int latency,int bandwidth); //設置
ServerSocket信息
serverSocket.getInetAddress(); //獲取服務器綁定的IP
serverSocket.getLocalPort(); //獲取服務器綁定的端口
多線程
為每一個連接創建一個線程
重寫Runnable方法
Handler.java
package Network_3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class Handler implements Runnable {
private Socket socket = null; //初始化Socket
public Handler(Socket socket) {
this.socket = socket; //傳入參數
}
/*
* 輸出流
*/
public PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut,true);
}
/*
*輸入流
*/
public BufferedReader getReader(Socket socket) throws IOException {
InputStreamReader socketIn = new InputStreamReader(socket.getInputStream());
return new BufferedReader(socketIn);
}
//加工信息
public String Echo(String msg) {
return "Echo:"+msg;
}
public void run() {
try {
System.out.println("New Connection "+socket.getInetAddress()+":"+socket.getPort()); //打印新連接信息
BufferedReader br = getReader(socket); //輸入流
PrintWriter pw = getWriter(socket); //輸出流
String msg = null; //初始化msg
while((msg = br.readLine()) != null) { //循環讀取一行信息
System.out.println(msg); //打印信息
pw.println(Echo(msg)); //將信息加工發送回客戶端
if(msg.equals("exit")) { //結束條件
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(socket != null) { //如有連接,關閉
socket.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
Server.java
package Network_3;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private int port = 8000; //初始化port
private ServerSocket serverSocket = null; //初始化ServerSocket
public Server() {
try {
serverSocket = new ServerSocket(port); //啟動服務端
System.out.println("Server Up!");
} catch (IOException e) {
System.out.println("Server Up Error!");
}
}
public void service() {
while(true) {
Socket socket = null; //初始化Socket
try {
socket = serverSocket.accept(); //監聽連接隊列
Thread workThread = new Thread(new Handler(socket)); //創建線程
workThread.start(); //啟動線程
}catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Server().service();
}
}
Client.java
package Network_3;
import java.net.*;
import java.io.*;
public class Client {
/*
* 參數初始化
*/
private String host = "127.0.0.1";
private int port = 8000;
private Socket socket;
/*
* 建立連接
*/
public Client() throws IOException {
socket = new Socket(host,port);
}
/*
* 輸出流
*/
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut,true);
}
/*
* 輸入流
*/
private BufferedReader getReader(Socket socket) throws IOException {
InputStreamReader socketIn = new InputStreamReader(socket.getInputStream());
return new BufferedReader(socketIn);
}
/*
* 客戶程序
*/
public void Talk() {
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("exit")) {
break;
}
}
}catch (Exception e) {
e.printStackTrace();
}finally {
try {
socket.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws UnknownHostException, IOException {
new Client().Talk();
}
}
使用JDK類庫提供的線程池
java.util.concurrent包提供
Server.java
package Network_3;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server {
private int port = 8000; //初始化port
private ServerSocket serverSocket = null; //初始化ServerSocket
private ExecutorService executorService = null; //初始化線程池
private final int POOL_SIZE = 4; //單個CPU的線程數
public Server() throws IOException {
serverSocket = new ServerSocket(port);
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * POOL_SIZE); //創建線程池,Runtime.getRuntime().availableProcessors()用於返回當前工作環境的CPU數,將CPU數乘單個CPU線程數,得到最終的總線程數。
System.out.println("Server Up");
}
public void service() {
while(true) {
Socket socket = null;
try {
socket = serverSocket.accept();
executorService.execute(new Handler(socket)); //Handler與上面的一樣
}catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
new Server().service();
}
}
注意事項
死鎖
建議:盡量減少任務之間的依賴。
系統資源不足
建議:根據系統性能設定線程數,回收機制。
並發錯誤
建議:使用成熟的線程技術。
線程泄露
建議:執行線程任務時,減少與用戶的交互(使用超時機制)。
任務過載
建議:控制線程等待隊列中的線程數。
關閉服務器
開放一個管理服務器端口,啟動守護線程,供管理程序連接,當發送特定字符時,服務器停止向線程池添加任務並等待任務執行完畢或超時,關閉服務程序。
Server.java
package Network_3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
public class Server {
private int port = 8000; //服務端口
private ServerSocket serverSocket = null; //服務Socket
private ExecutorService executorService = null; //線程池
private final int POOL_SIZE = 4; //單個CPU的線程數
private int portForShutdown = 8001; //守護線程端口
private ServerSocket serverSocketForShutdown = null; //守護Socket
private boolean isShutdown = false; //服務器是否關閉
private Thread shutDownThread = new Thread() { //負責關閉服務器的線程
public void start() {
this.setDaemon(true); //設置為守護線程(后台線程)
super.start();
}
public void run() {
while(!isShutdown) {
Socket socketForShutdown = null;
try {
socketForShutdown = serverSocketForShutdown.accept(); //開啟監聽
//獲取輸入流
BufferedReader br = new BufferedReader(new InputStreamReader(socketForShutdown.getInputStream()));
String command = br.readLine(); //讀取一行字符
if(command.equals("shutdown")) { //判斷是否符合指定字符
long beginTime = System.currentTimeMillis(); //開始計數
//輸出流輸出字符
socketForShutdown.getOutputStream().write("Server Shutdowning\r\n".getBytes());
//服務器關閉
isShutdown = true;
//不再向線程池添加新線程
executorService.shutdown();
//所有任務是否已完成
while(!executorService.isTerminated()) {
//設置線程池中任務的完成超時時間
executorService.awaitTermination(30, TimeUnit.SECONDS);
}
//關閉服務器
serverSocket.close();
long endTime = System.currentTimeMillis(); //結束計數
//輸出流輸出字符
socketForShutdown.getOutputStream().write(("Server Shutdown\r\nTime:"+(endTime-beginTime)+"ms\r\n").getBytes());
//關閉守護線程
socketForShutdown.close();
serverSocketForShutdown.close();
}else {
//不符合特定字符
socketForShutdown.getOutputStream().write("Error".getBytes());
socketForShutdown.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
};
public Server() throws IOException {
//創建Socket
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(60000); //設置超時時間
//創建守護Socket
serverSocketForShutdown = new ServerSocket(portForShutdown);
//創建線程池
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * POOL_SIZE);
//運行守護線程
shutDownThread.start();
System.out.println("Server Up");
}
public void service() {
while(!isShutdown) {
Socket socket = null;
try {
socket = serverSocket.accept();
socket.setSoTimeout(60000);
executorService.execute(new Handler(socket));
}catch (SocketTimeoutException e) {
System.out.println("Client Timeout");
}catch (RejectedExecutionException e) {
try {
if(socket != null) {
socket.close();
}
}catch (IOException x) {
return;
}
}catch (SocketException e) {
if(e.getMessage().indexOf("socket closed") != -1) {
return;
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
new Server().service();
}
}
AdminClient.java
package Network_3;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class AdminClient {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("localhost", 8001);
OutputStream socketOut = socket.getOutputStream();
socketOut.write("shutdown\r\n".getBytes());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg = null;
while((msg = br.readLine()) != null) {
System.out.println(msg);
}
}catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(socket != null) {
socket.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
}