java socket系列文章
java socket 单线程echo服务器
java socket 多线程echo服务器
java socket 线程池echo服务器
在java socket 多线程echo服务器这篇文章中,改善了单线程echo服务器中只能处理一个客户端请求的弊端,利用一个客户端对应一个线程的方法,达到处理多个客户端的目的。
但是,每个新线程都会消耗系统资源:
- 创建一个线程会占用 CPU 周期,而且每个线程都会建立自己的数据结构(如,栈),也要消耗系统内存。
- 当一个线程阻塞时,JVM 将保存其状态,选择另外一个线程运行,并在上下文转换(context switch)时恢复阻塞线程的状态。随着线程数的增加,线程将消耗越来越多的系统资源,这将最终导致系统花费更多的时间来处理上下文转换盒线程管理,更少的时间来对连接进行服务。
在这种情况下,加入一个额外的线程实际上可能增加客户端总服务的时间。当线程数目过多的时候,必然导致每个客户端请求的处理时间增长,使用户体验明显变差。
我们可以通过限制线程总数并重复使用线程来避免这个问题。我们让服务器在启动时创建一个由固定线程数量组成的线程池,线程池的工作原理:
- 当一个新的客户端连接请求传入服务器,它将交给线程池中的一个线程处理,
- 该线程处理完这个客户端之后,又返回线程池,继续等待下一次请求。
- 如果连接请求到达服务器时,线程池中所有的线程都已经被占用,它们则在一个队列中等待,直到有空闲的线程可用。
与一客户一线程服务器一样,线程池服务器首先创建一个 ServerSocket 实例。
然后创建 N 个线程,每个线程反复循环,从(共享的)ServerSocket 实例接收客户端连接。当多个线程同时调用一个 ServerSocket 实例的 accept()方法时,它们都将阻塞等待,直到一个新的连接成功建立,然后系统选择一个线程,为建立起的连接提供服务,其他线程则继续阻塞等待。
线程在完成对一个客户端的服务后,继续等待其他的连接请求,而不终止。如果在一个客户端连接被创建时,没有线程在 accept()方法上阻塞(即所有的线程都在为其他连接服务),系统则将新的连接排列在一个队列中,直到下一次调用 accept()方法。
客户端代码:
public class Client {
public static void main(String[] args) throws IOException {
Socket client = new Socket("127.0.0.1", 20012);
//客户端请求与本机在20011端口建立TCP连接
client.setSoTimeout(10000);
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
//获取键盘输入
PrintStream out = new PrintStream(client.getOutputStream());
//获取Socket的输出流,用来发送数据到服务端
BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
//获取Socket的输入流,用来接收从服务端发送过来的数据
boolean flag = true;
while(flag){
System.out.print("输入信息:");
String str = input.readLine();
out.println(str);
//发送数据到服务端
if("bye".equals(str)){
flag = false;
}else{
try{
//从服务器端接收数据有个时间限制(系统自设,也可以自己设置),超过了这个时间,便会抛出该异常
String echo = buf.readLine();
System.out.println(echo);
}catch(SocketTimeoutException e){
System.out.println("Time out, No response");
}
}
}
input.close();
if(client != null){
//如果构造函数建立起了连接,则关闭套接字,如果没有建立起连接,自然不用关闭
client.close(); //只关闭socket,其关联的输入输出流也会被关闭
}
}
}
服务器端代码
public class ThreadPoolServer {
public static void main(String[] args) {
ServerSocket server;
ExecutorService executor=Executors.newFixedThreadPool(2);
try {
server = new ServerSocket(20012);
Socket client = null;
while(true){
System.out.println("服务器端等待客户端发起连接请求");
client = server.accept();
System.out.println("客户端向服务器端发起了连接请求,且连接成功");
executor.execute(new Handler(client));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
线程处理代码
public class Handler extends Thread{
private Socket client;
PrintStream out;
BufferedReader buf;
public Handler(Socket client){
this.client = client;
try {
out = new PrintStream(client.getOutputStream());
buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run(){
try{
boolean flag =true;
while(flag){
String str = buf.readLine();
if(str == null || "".equals(str)){
flag = false;
}else{
if("bye".equals(str)){
flag = false;
}else{
System.out.println("服务器从客户端接受到的数据:"+str);
out.println("echo:" + str);
}
}
}
out.close();
client.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
此项目的完整代码可以到我的github,java-socket进行下载。