【Java】使用socket实现控制台多线程聊天室详解编程语言

刚学习了socket编程和多线程相关知识,为了巩固下知识,动手实现了一个基于BIO的socket+多线程的控制台聊天室。

github代码地址:传送门

功能介绍

首先启动server端,然后启动client端。任意一个client发送给server的消息都将会被转发给所有在线的client,实现了一个聊天室功能。

client发送exit指令可以下线,所有的client端,以及server端都会收到它的下线通知。

原理

socket 通信是基于TCP/IP 网络层上的一种传送方式,有关TCP的介绍前面有写过(TCP概述),这里不再赘述。

首先,服务端初始化ServerSocket,然后对指定的端口(这里是65000)进行绑定,接着对端口进行监听,通过调用accept方法阻塞,此时,如果客户端有一个socket连接到服务端,那么服务端通过监听和accept方法可以与客户端进行连接,就创建了一个客户端线程。

然后就是客户端的启动和连接了,在服务端socket启动之后,启动客户端连接ServerSocket,这里实现了多线程,可以同时连接多个客户端。这里多线程方面使用了线程池,线程池的作用是:

  • 线程复用,减少创建线程耗时;
  • 防止短时间内高并发,指定线程池大小,超过数量将等待,防止短时间创建大量线程导致资源耗尽,服务挂掉。

代码

客户端:

 
import java.io.*; 
import java.net.InetAddress; 
import java.net.InetSocketAddress; 
import java.net.Socket; 
import java.util.Scanner; 
public class TCPClient {
 
Socket clientSocket = null; 
BufferedReader reader = null; 
private static final int PORT = 65000; 
public static void main(String[] args) throws IOException {
 
new TCPClient(); 
System.exit(0);  //client退出后自动结束此进程 
} 
//客户端逻辑写在构造方法中 
public TCPClient() {
 
clientSocket = new Socket(); 
try {
 
clientSocket.connect(new InetSocketAddress("localhost", PORT)); 
new getInputStream().start(); 
System.out.println("Client started!"); 
reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
String receiveStr = null; 
while ((receiveStr = reader.readLine()) != null) {
 
//接收服务端发来的数据 
if (receiveStr.equals("exit")) {
 
//如果发送exit,服务端会回传exit,这里判断回传的是不是exit。是的话就结束此线程 
break; 
} 
System.out.println(receiveStr); 
} 
} catch (IOException e) {
 
e.printStackTrace(); 
} finally {
 
//exit -> 关闭客户端套接字、线程、BufferReader 
try {
 
if (reader != null) {
 
reader.close(); 
} 
if (clientSocket != null) {
 
clientSocket.close(); 
} 
} catch (IOException e) {
 
e.printStackTrace(); 
} 
} 
} 
//客户端线程,用于向服务端写入从客户端控制台输入的数据 
class getInputStream extends Thread {
 
@Override 
public void run() {
 
BufferedReader is = null; 
PrintWriter os = null; 
try {
 
is = new BufferedReader(new InputStreamReader(System.in)); 
os = new PrintWriter(new BufferedOutputStream(clientSocket.getOutputStream())); 
String str = null; 
while ((str = is.readLine()) != null) {
 
os.println(str); 
os.flush(); 
} 
} catch (IOException e) {
 
e.printStackTrace(); 
} finally {
 
try {
 
if (is != null) {
 
is.close(); 
} 
if (os != null) {
 
os.close(); 
} 
} catch (IOException e) {
 
e.printStackTrace(); 
} 
} 
} 
} 
} 

服务端:

import java.io.*; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.util.ArrayList; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
public class TCPServer {
 
static ArrayList<Socket> clients = new ArrayList<>(); 
//接受客户端信息线程,每个客户端对应一个,用来获取客户端发送来的数据并广播到所有客户端 
static class HandleMsg implements Runnable {
 
Socket clientSocket; 
PrintWriter os = null; 
String msg = null; 
public HandleMsg(Socket clientSocket) {
 
this.clientSocket = clientSocket; 
} 
@Override 
public void run() {
 
clients.add(clientSocket);  //添加进客户端套接字池 
//客户端刚连接时显示其端口、当前人数,并广播 
msg = "欢迎【" + clientSocket.getRemoteSocketAddress() + "】加入聊天室!聊天室当前人数为:" + clients.size(); 
sendMsg(msg); 
BufferedReader is = null; 
try {
 
//从InputStream中读取客户端发来的数据 
is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
while ((msg = is.readLine()) != null) {
 
if (msg.equals("exit")) {
 
//客户端发送exit退出 
msg = "【" + clientSocket.getRemoteSocketAddress() + "】下线了."; 
sendMsg(msg); 
System.out.println(msg); 
os = new PrintWriter(new BufferedOutputStream(clientSocket.getOutputStream())); 
os.println("exit"); 
os.flush(); 
break; 
} else {
 
msg = "【" + clientSocket.getRemoteSocketAddress() + "】说: " + msg; 
sendMsg(msg); 
} 
} 
} catch (IOException e) {
 
e.printStackTrace(); 
} finally {
 
try {
 
if (is != null) {
 
is.close(); 
} 
if (os != null) {
 
os.close(); 
} 
clientSocket.close(); 
clients.remove(clientSocket); 
} catch (IOException e) {
 
e.printStackTrace(); 
} 
} 
} 
//广播发送方法 
void sendMsg(String msg) {
 
for (int i = clients.size() - 1; i >= 0; --i) {
 
try {
 
os = new PrintWriter(new BufferedOutputStream(clients.get(i).getOutputStream())); 
os.println(msg); 
os.flush(); 
} catch (IOException e) {
 
e.printStackTrace(); 
} 
} 
} 
} 
public static void main(String[] args) throws IOException {
 
ServerSocket ss = null; 
Socket clientSocket = null; 
ExecutorService tp = Executors.newCachedThreadPool(); 
ss = new ServerSocket(65000); 
System.out.println("Server " + ss.getLocalPort() + " started!"); 
while (true) {
 
try {
 
//阻塞,直到有客户端连接 
clientSocket = ss.accept(); 
System.out.println(clientSocket.getRemoteSocketAddress() + " connect!"); 
//为客户端创建一个接收信息的线程 
tp.execute(new HandleMsg(clientSocket)); 
} catch (IOException e) {
 
System.out.println(e); 
} 
} 
} 
} 

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/19354.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论