在上章(java socket套接字编程入门)中,服务端只能处理一次,accept()是一种阻塞状态,因此它只能同时处理一个请求,其它的请求只能排队等待前面的处理完成。
为了支持多任务同时处理的能力,首先不要让主服务运行完成即结束,而是一种死循环的方式,让一直等待接收,其次,处理数据的需要另开线程进行,即socket的生命周期置于新开线程中。
先定义SocketHandler作为线程单独处理socket。
public class SocketHandler implements Runnable {
private Socket socket;
SocketHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader bufferedReader = null;
PrintWriter writer = null;
try {
System.out.println("thread:\t" + Thread.currentThread().getName() );
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//true,表示自动刷新,不需要人为触发
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
String userInput;
while ((userInput = bufferedReader.readLine()) != null) {
if ("exit".equals(userInput)) {
System.out.println("退出连接通信\t");
break;
}
System.out.println("接收内容:\t" + userInput);
String result = "服务器时间:" + LocalDateTime.now() +"\t" + new StringBuilder(userInput).reverse();
writer.println(result);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) writer.close();
if (bufferedReader != null) bufferedReader.close();
socket.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
}
server服务端:
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9090);
//固定线程池来接收处理
Executor executor = Executors.newFixedThreadPool(3);
//死循环,保证主线程不退出
while (true){
executor.execute(new SocketHandler(serverSocket.accept()));
}
}
client客户端:
public class Client {
public void send(String message){
Socket socket = null;
BufferedReader bufferedReader = null;
PrintWriter writer = null;
try {
socket = new Socket("127.0.0.1",9090);
//发送数据到服务端
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
writer.println(message);
System.out.println("发送内容:\t"+message);
//接收服务端返回数据流
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String input = null;
while ((input = bufferedReader.readLine()) != null){
System.out.println("接收服务端数据:\t"+input);
//退出指令,关闭连接
writer.println("exit");
break;
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) writer.close();
if (bufferedReader != null) bufferedReader.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client client = new Client();
for (int i = 0; i < 10; i++) {
String text = RandomStringUtils.randomAlphabetic(5);
client.send(text);
}
}
PrintWriter 与 BufferedWriter 的区别:
PrintWriter:是一种格式化对象的输出流,是一种高级流。此类实现了所有的PrintStream的print方法,它不包含用于写原始字节的方法,而对于原始字节,程序应该使用未编码的字节流。
与PrintStream相比较,它具有自动行刷新的缓冲字符输出流。只需要在构造函数中指定参数autoFlush=true,使用方法println()即可实现自动行刷新。(println(xx) = print(xx) + println())。
这个类中的方法永远不会抛出I/O异常,尽管它的一些构造函数可能抛出I/O异常。客户端可以通过调用checkError()来查询是否发生了错误。
PrintWriter还可以直接与文件通信:
new PrintWriter("c://text.txt");
BufferedWriter:字符缓冲输出流(高级流),将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
提供了一个newLine()方法,通知流结束。因此如果把上面的PrintWriter 换成BufferedWriter
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write(result);
bufferedWriter.newLine();
bufferedWriter.flush();