什么是套接字Socket
Socket本身就是網(wǎng)絡(luò)上的兩個程序可以互相發(fā)送請求和接收請求耸弄,應(yīng)用程序可以利用套接字在網(wǎng)絡(luò)上進行數(shù)據(jù)傳輸。
Socket本身作為應(yīng)用程序和網(wǎng)絡(luò)層TCP/UDP之間的一個抽象層闺属,在TCP中主要采用流套接字,采用的方式就是點對點通過字節(jié)流傳輸怨咪;UDP主要采用數(shù)據(jù)報套接字屋剑。
面向連接的入門例子
功能:客戶端發(fā)送請求润匙,服務(wù)器接收請求
服務(wù)端:
public class MyServer {
class HandleTask {
private Socket socket;
public HandleTask() {}
public HandleTask(Socket socket) {
this.socket = socket;
}
public void handle() {
StringBuilder sb = new StringBuilder("Hello: ");
InputStream is = null;
BufferedReader br = null;
try {
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
sb.append(br.readLine());
System.out.println(sb.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != br) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (null != is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(sb.toString() + "-- 結(jié)束執(zhí)行 --");
}
}
public static void main(String[] args) throws IOException{
ServerSocket serverSocket = null;
Socket socket = null;
try {
serverSocket = new ServerSocket(9999);
while (true) {
socket = serverSocket.accept();
new MyServer().new HandleTask(socket).handle();
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (null != socket) {
socket.close();
}
if (null != serverSocket) {
serverSocket.close();
}
}
}
}
思路:
① 服務(wù)器端啟動了ServerSocket诗眨,然后綁定了端口9999。
② 調(diào)用accept方法孕讳,阻塞等待客戶端連接匠楚。
③ 當(dāng)接收到客戶端的請求之后,通過字節(jié)流獲取客戶端發(fā)來的信息厂财。
多個客戶端:
public class MyClient {
public static void main(String[] args) {
Socket socket = null;
BufferedWriter bw = null;
Scanner scanner = new Scanner(System.in);
try {
socket = new Socket("localhost", 9999);
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(scanner.nextLine());
bw.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
scanner.close();
try {
if (null != bw) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (null != socket) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class MyClient2 {
public static void main(String[] args) {
Socket socket = null;
BufferedWriter bw = null;
Scanner scanner = new Scanner(System.in);
try {
socket = new Socket("localhost", 9999);
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(scanner.nextLine());
bw.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
scanner.close();
try {
if (null != bw) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (null != socket) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
結(jié)果:
解釋:
上面這個例子的缺陷就是如果是多個請求去等待服務(wù)器端去處理芋簿,那么服務(wù)器端會按照收到請求的順序執(zhí)行,這樣如果第一個請求執(zhí)行時間很長璃饱,那么第二個請求就很長時間等不到執(zhí)行与斤。
多線程執(zhí)行多個客戶端
場景:
基于上面的例子,思考:服務(wù)器在很多情況下是需要接收來自很多個客戶端的請求的荚恶。
解決辦法:
根據(jù)多線程時間分片方式來解決問題:多個客戶端的請求撩穿,那么服務(wù)器采用每次接收到一個請求就創(chuàng)建一個線程來執(zhí)行。
采用多線程改進的服務(wù)端:
public class MyThreadServer {
class HandleTask extends Thread{
private Socket socket;
public HandleTask() {}
public HandleTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
StringBuilder sb = new StringBuilder("Hello: ");
InputStream is = null;
BufferedReader br = null;
try {
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
sb.append(br.readLine());
System.out.println(sb.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != br) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (null != is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(sb.toString() + "-- 結(jié)束執(zhí)行 --");
}
}
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
try {
serverSocket = new ServerSocket(9999);
while (true) {
socket = serverSocket.accept();
new MyThreadServer().new HandleTask(socket).start();
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (null != socket) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (null != serverSocket) {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
方案使用的模型:
上面的程序?qū)iT使用一個接收者線程來專門負責(zé)監(jiān)聽客戶端的請求谒撼,接收到客戶端的請求食寡,就為其創(chuàng)建一個新的線程去執(zhí)行業(yè)務(wù)處理,最后通過字節(jié)流進行返回響應(yīng)廓潜。這種模型就是BIO(阻塞IO)抵皱。
方案的缺點:
這種工作的模型有一個問題,就是當(dāng)請求數(shù)很多時辩蛋,就會創(chuàng)建和請求數(shù)一樣多匹配線程呻畸。最終當(dāng)并發(fā)量上來,那么系統(tǒng)的性能將會下降悼院。
線程池執(zhí)行多個客戶端
場景:
基于上面的例子擂错,使用多線程方式來處理請求,每個請求都創(chuàng)建一個匹配的線程樱蛤,浪費線程資源钮呀。采用線程池管理線程剑鞍,可以充分利用線程。
采用線程池改進的服務(wù)端:
public class MyThreadPoolServer {
private static ExecutorService es = Executors.newCachedThreadPool();
class HandleTask extends Thread {
private Socket socket;
public HandleTask() {}
public HandleTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
StringBuilder sb = new StringBuilder("Hello: ");
InputStream is = null;
BufferedReader br = null;
try {
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
sb.append(br.readLine());
System.out.println(sb.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != br) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (null != is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(sb.toString() + "-- 結(jié)束執(zhí)行 --");
}
}
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
try {
serverSocket = new ServerSocket(9999);
while (true) {
socket = serverSocket.accept();
es.execute(new MyThreadServer().new HandleTask(socket));
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (null != socket) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (null != serverSocket) {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
方案使用的模型: