这次文章中,是在上篇的基础上,在服务端使用了多线程的方式来管理连接,主线程负责接收连接,在接到连接后变创建新的线程,每个线程负责与自己的客户端进行通信。
与单线程阻塞的例子相比来说,服务端可以与多个客户端进行通信了,不过多线程频繁的创建与销毁便会带来很大的资源开销,而系统的网络资源等都是有限的;因此便可以引入线程池,可以在某种程度上重用线程,减少线程的创建和销毁的次数以减少开销。
下例代码中包含了使用和不使用线程池(针对Server端)的两种方式,如果测试的时候,解开注释的代码即可以。
Server端代码:
package com.henushang.socket.chapter2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.henushang.socket.util.SocketUtils;
public class EchoServer {
private int port = 8000;
private ServerSocket serverSocket;
private ExecutorService executorService ; // 连接池
private final int POOL_SIZE = 4;// 连接池大小
public EchoServer() throws Exception{
serverSocket = new ServerSocket(port);
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * POOL_SIZE);
System.out.println("waitting connet...");
}
/**
* 接受连接
*
* @author henushang
*/
public void service() {
Socket socket = null;
while (true) {
try {
socket = serverSocket.accept();
executorService.execute(new Handler(socket));// 使用连接池
// new Thread(new Handler(socket)).start();// 不使用连接池
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
new EchoServer().service();
}
/**
* 线程类,负责维持与一个客户端的通信
*
* @author henushang
*/
class Handler implements Runnable{
private Socket socket = null;
public Handler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
System.out.println("new connection accepted:" + socket.getInetAddress() + ":" + socket.getPort());
try {
BufferedReader reader = SocketUtils.getReader(this.socket);
PrintWriter writer = SocketUtils.getWriter(this.socket);
String msg = null;
while ((msg = reader.readLine()) != null) {
System.out.println(msg);
// 因为客户端接收消息的时候使用的是readline()方法,如果消息没有以\r\n 结尾的话,客户端则接收不到消息
// writer.write(SocketUtils.echo(msg) + "\r\n");
writer.println(SocketUtils.echo(msg));
writer.flush();
if ("bye".equals(msg)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally{
SocketUtils.close(socket);
}
}
}
}
Client端代码(与上次代码一样):
package com.henushang.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import com.henushang.socket.util.SocketUtils;
public class EchoClient {
private String host = "127.0.0.1";
private int port = 8000;
private Socket socket ;
public EchoClient() throws Exception {
socket = new Socket(host, port);
}
public void talk() throws IOException {
try {
BufferedReader reader = SocketUtils.getReader(socket);
PrintWriter writer = SocketUtils.getWriter(socket);
// 读取本地控制台的消息
BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while ((msg = localReader.readLine()) != null) {
writer.println(msg);
writer.flush();
System.out.println(reader.readLine());
if ("bye".equals(msg)) {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
SocketUtils.close(socket);
}
}
public static void main(String[] args) throws Exception{
new EchoClient().talk();
}
}
SocketUtils代码:
package com.henushang.socket.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.channels.SocketChannel;
public class SocketUtils {
public static PrintWriter getWriter(Socket socket) throws IOException {
OutputStream os = socket.getOutputStream();
return new PrintWriter(os);
}
public static PrintWriter getWriter(SocketChannel socketChannel) throws IOException {
return getWriter(socketChannel.socket());
}
public static BufferedReader getReader(Socket socket) throws IOException{
InputStream is = socket.getInputStream();
return new BufferedReader(new InputStreamReader(is, "UTF-8"));
}
public static BufferedReader getReader(SocketChannel socketChannel) throws IOException{
return getReader(socketChannel.socket());
}
public static void close(Socket socket) {
try {
if (socket != null) {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(SocketChannel socketChannel) {
try {
if (socketChannel != null) {
socketChannel.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String echo(String msg) {
return "echo:" + msg;
}
}