一、服务器状态--繁忙
每个客户端都需要服务器进行双通等待
双通:客户端发送数据到服务器的接收通道
双通:服务器回送消息的发送通道
每条通道因为堵塞只能使用异步线程实现
二、服务器线程数量
一个客户端:双通->2条线程
n个客户端:2n条线程
服务器实际线程数量:2n+(服务器创建时的线程、垃圾回收的线程、监听客户端socket的线程、读取到内容后的转发线程)
三、服务器测试
想测试服务器,需要给服务器压力。服务器是进行客户端连接、发送、转发的地方。可以通过创建n个客户端,并且实时向客户端发送消息,然后测试服务器的压力状态。
四、测试代码
package net.qiujuer.lesson.sample.client;
import net.qiujuer.lesson.sample.client.bean.ServerInfo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 这个测试类应该写什么东西呢?
* 首先,需要测试服务器的性能,所以需要压力测试。需要进行构建非常多的客户端,并且连接到服务器端。
* 然后使用客户端频繁地发送信息,测试服务器的压力状态。
* 1、通过UDP拿到服务器信息
* 2、开始循环建立1000个客户端,如果连接成功加到连接池
* 3、当键盘输入任何内容后每隔1s对每个连接的客户端进行输出的操作
* 4、当键盘再次输入任何内容后进行结束操作
*/
public class ClientTest {
private static boolean done;
public static void main(String[] args) throws IOException {
ServerInfo info = UDPSearcher.searchServer(10000);
System.out.println("Server:"+info);
if(info == null){
return;
}
//当前连接数量
int size = 0;
List<TCPClient> tcpClients = new ArrayList<>();
for(int i=0;i<1000;i++){
try{
TCPClient tcpClient = TCPClient.startWith(info);
if(tcpClient==null){
System.out.println("连接异常");
}
tcpClients.add(tcpClient);
System.out.println("连接成功:"+(++size));
}catch (IOException e){
System.out.println("连接异常");
}
//当进行循环的时候,必须等待一点时间,因为服务器默认有阈值。当当前正在连接的客户端达到50以上,那么就会拒绝后面等待连接的客户端
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.in.read();
Runnable runnable = new Runnable() {
@Override
public void run() {
while (!done){
for(TCPClient tcpClient:tcpClients){
tcpClient.send("Hello~~~");
}
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();
System.in.read();
//等待线程完成
done = true;
try{
thread.join();
}catch (Exception e){
e.printStackTrace();
}
//客户端结束操作
for(TCPClient tcpClient : tcpClients){
tcpClient.exit();
}
}
}
}
package net.qiujuer.lesson.sample.client;
import net.qiujuer.lesson.sample.client.bean.ServerInfo;
import net.qiujuer.library.clink.utils.CloseUtils;
import java.io.*;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
public class TCPClient {
private final Socket socket;
private final ReadHandler readHandler;
private final PrintStream printStream;
public TCPClient(Socket socket,ReadHandler readHandler) throws IOException {
this.socket = socket;
this.readHandler = readHandler;
this.printStream = new PrintStream(socket.getOutputStream());
}
public void exit(){
readHandler.exit();
CloseUtils.close(printStream);
CloseUtils.close(socket);
}
public void send(String msg){
printStream.println(msg);
}
public static TCPClient startWith(ServerInfo info) throws IOException {
Socket socket = new Socket();
// 超时时间
socket.setSoTimeout(3000);
// 连接本地,端口2000;超时时间3000ms
socket.connect(new InetSocketAddress(Inet4Address.getByName(info.getAddress()), info.getPort()), 3000);
System.out.println("已发起服务器连接,并进入后续流程~");
System.out.println("客户端信息:" + socket.getLocalAddress() + " P:" + socket.getLocalPort());
System.out.println("服务器信息:" + socket.getInetAddress() + " P:" + socket.getPort());
try {
ReadHandler readHandler = new ReadHandler(socket.getInputStream());
readHandler.start();
return new TCPClient(socket,readHandler);
} catch (Exception e) {
System.out.println("连接异常");
CloseUtils.close(socket);
}
return null;
}
static class ReadHandler extends Thread {
private boolean done = false;
private final InputStream inputStream;
ReadHandler(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public void run() {
super.run();
try {
// 得到输入流,用于接收数据
BufferedReader socketInput = new BufferedReader(new InputStreamReader(inputStream));
do {
String str;
try {
// 客户端拿到一条数据
str = socketInput.readLine();
} catch (SocketTimeoutException e) {
continue;
}
if (str == null) {
System.out.println("连接已关闭,无法读取数据!");
break;
}
// 打印到屏幕
System.out.println(str);
} while (!done);
} catch (Exception e) {
if (!done) {
System.out.println("连接异常断开:" + e.getMessage());
}
} finally {
// 连接关闭
CloseUtils.close(inputStream);
}
}
void exit() {
done = true;
CloseUtils.close(inputStream);
}
}
}