java 多线程套接字_Java面试题:用Java的套接字编程实现一个多线程的回显(echo)服务器。...

本文展示了如何使用Java的多线程和套接字编程实现一个回显服务器,以及如何通过NIO的多路复用套接字进行优化。服务器端通过静态内部类实现线程池,提高性能,客户端则提供输入并接收服务器的回显。NIO版本的代码涉及Selector和ByteBuffer,虽然性能更好但相对较复杂。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.ServerSocket;

import java.net.Socket;

public class EchoServer {

private static final int ECHO_SERVER_PORT = 6789;

public static void main(String[] args) {

try(ServerSocket server = new ServerSocket(ECHO_SERVER_PORT)) {

System.out.println("服务器已经启动...");

while(true) {

Socket client = server.accept();

new Thread(new ClientHandler(client)).start();

}

} catch (IOException e) {

e.printStackTrace();

}

}

private static class ClientHandler implements Runnable {

private Socket client;

public ClientHandler(Socket client) {

this.client = client;

}

@Override

public void run() {

try(BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));

PrintWriter pw = new PrintWriter(client.getOutputStream())) {

String msg = br.readLine();

System.out.println("收到" + client.getInetAddress() + "发送的: " + msg);

pw.println(msg);

pw.flush();

} catch(Exception ex) {

ex.printStackTrace();

} finally {

try {

client.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

}

注意:上面的代码使用了Java 7的TWR语法,由于很多外部资源类都间接的实现了AutoCloseable接口(单方法回调接口),因此可以利用TWR语法在try结束的时候通过回调的方式自动调用外部资源类的close()方法,避免书写冗长的finally代码块。此外,上面的代码用一个静态内部类实现线程的功能,使用多线程可以避免一个用户I/O操作所产生的中断影响其他用户对服务器的访问,简单的说就是一个用户的输入操作不会造成其他用户的阻塞。当然,上面的代码使用线程池可以获得更好的性能,因为频繁的创建和销毁线程所造成的开销也是不可忽视的。

下面是一段回显客户端测试代码:

import java.io.BufferedReader;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.Socket;

import java.util.Scanner;

public class EchoClient {

public static void main(String[] args) throws Exception {

Socket client = new Socket("localhost", 6789);

Scanner sc = new Scanner(System.in);

System.out.print("请输入内容: ");

String msg = sc.nextLine();

sc.close();

PrintWriter pw = new PrintWriter(client.getOutputStream());

pw.println(msg);

pw.flush();

BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));

System.out.println(br.readLine());

client.close();

}

}

如果希望用NIO的多路复用套接字实现服务器,代码如下所示。NIO的操作虽然带来了更好的性能,但是有些操作是比较底层的,对于初学者来说还是有些难于理解。

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.CharBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.util.Iterator;

public class EchoServerNIO {

private static final int ECHO_SERVER_PORT = 6789;

private static final int ECHO_SERVER_TIMEOUT = 5000;

private static final int BUFFER_SIZE = 1024;

private static ServerSocketChannel serverChannel = null;

private static Selector selector = null; // 多路复用选择器

private static ByteBuffer buffer = null; // 缓冲区

public static void main(String[] args) {

init();

listen();

}

private static void init() {

try {

serverChannel = ServerSocketChannel.open();

buffer = ByteBuffer.allocate(BUFFER_SIZE);

serverChannel.socket().bind(new InetSocketAddress(ECHO_SERVER_PORT));

serverChannel.configureBlocking(false);

selector = Selector.open();

serverChannel.register(selector, SelectionKey.OP_ACCEPT);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

private static void listen() {

while (true) {

try {

if (selector.select(ECHO_SERVER_TIMEOUT) != 0) {

Iterator it = selector.selectedKeys().iterator();

while (it.hasNext()) {

SelectionKey key = it.next();

it.remove();

handleKey(key);

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

private static void handleKey(SelectionKey key) throws IOException {

SocketChannel channel = null;

try {

if (key.isAcceptable()) {

ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();

channel = serverChannel.accept();

channel.configureBlocking(false);

channel.register(selector, SelectionKey.OP_READ);

} else if (key.isReadable()) {

channel = (SocketChannel) key.channel();

buffer.clear();

if (channel.read(buffer) > 0) {

buffer.flip();

CharBuffer charBuffer = CharsetHelper.decode(buffer);

String msg = charBuffer.toString();

System.out.println("收到" + channel.getRemoteAddress() + "的消息:" + msg);

channel.write(CharsetHelper.encode(CharBuffer.wrap(msg)));

} else {

channel.close();

}

}

} catch (Exception e) {

e.printStackTrace();

if (channel != null) {

channel.close();

}

}

}

}

import java.nio.ByteBuffer;

import java.nio.CharBuffer;

import java.nio.charset.CharacterCodingException;

import java.nio.charset.Charset;

import java.nio.charset.CharsetDecoder;

import java.nio.charset.CharsetEncoder;

public final class CharsetHelper {

private static final String UTF_8 = "UTF-8";

private static CharsetEncoder encoder = Charset.forName(UTF_8).newEncoder();

private static CharsetDecoder decoder = Charset.forName(UTF_8).newDecoder();

private CharsetHelper() {

}

public static ByteBuffer encode(CharBuffer in) throws CharacterCodingException{

return encoder.encode(in);

}

public static CharBuffer decode(ByteBuffer in) throws CharacterCodingException{

return decoder.decode(in);

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值