基于网络编程Socket写的一个简单聊天室
客户端
import com.qf.xjw.thread.ReaderThread;
import com.qf.xjw.thread.WriterThread;
import java.io.IOException;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 8888);
new ReaderThread(socket).start();
new WriterThread(socket).start();
} catch (IOException e) {
System.out.println("连接不上服务器");
}
}
}
服务端
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server{
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
ServerThread thread;
try {
serverSocket = new ServerSocket(8888);
while(true){
socket = serverSocket.accept();
thread = new ServerThread(socket);
DB.list.add(thread);
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器读写线程
import java.io.*;
import java.net.Socket;
public class ServerThread extends Thread {
private Socket socket;
private BufferedReader br;
private BufferedWriter bw;
private User user;
public ServerThread(Socket socket) throws IOException {
this.socket = socket;
this.br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
this.user = new User();
}
@Override
public void run() {
try{
//用户注册
register();
//消息转发
forward();
} catch (IOException e) {
//判断是否已经注册,如果没有注册则退出不需要通知其他人
if (this.user.getUserName() != null){
offLine();
}
} finally {
IOUtils.closeAll(br,bw,socket);
}
}
/**
* 消息转发
* @throws IOException
*/
private void forward() throws IOException {
String str = null;
while (true){
str = br.readLine();
//判断是否是退出
if ("esc".equalsIgnoreCase(str)){
offLine();
}
//判断是否为私聊
if (str.startsWith("@")){
oneToOne(str);
continue;
}
//群聊
sendToOther(this.user.getUserName()+":"+str);
}
}
/**
* 私聊方法
* @param str
*/
private void oneToOne(String str) throws IOException {
//判断是否符合规则
if (!str.matches("[@].{2,8}[::].+")){
send("系统:不符合私聊规则(规则:@xxx:...)");
return;
}
//看接收方的用户名是否存在
String receiveName = str.split("[::]")[0].substring(1);
if (!checkName(receiveName)){
send("系统:该用户不存在或已经下线,请重新输入!");
return;
}
String content = str.substring(receiveName.length()+2);
//给指定用户发送消息
for (ServerThread thread : DB.list) {
if (thread.getUser().getUserName().equals(receiveName)){
thread.send(this.getUser().getUserName()+"私聊说:"+content);
break;
}
}
}
/**
* 下线方法
*/
private void offLine() {
try {
//通知自己
send("系统:您已退出交流群!");
//通知其他人
sendToOther("系统:"+this.getUser().getUserName()+"已退出交流群!");
} catch (IOException e) {
//通知其他人
try {
sendToOther("系统:"+this.getUser().getUserName()+"已退出交流群!");
} catch (IOException ex) {
ex.printStackTrace();
}
}finally {
//把线程从集合中移除
DB.list.remove(this);
}
}
/**
* 用户注册功能
* @throws IOException
*/
private void register() throws IOException {
String name = null;
do {
//接收用户输入的用户名
name = br.readLine();
//判断用户名是否为空
if (name == null){
send("系统:用户名不能为空,请重新输入不为空的用户名!");
continue;
}
//判断名字长度是否合理
if (!(name.length()>=2 && name.length()<8)){
//提示用户长度不合理
send("系统:输入的用户名长度不合理,请重新输入2-8个字符的用户名!");
continue;
}
//判断用户名是否重复
if (checkName(name)){
send("系统:输入的用户名已经存在,请重庆输入!");
continue;
}
//判断用户名是否是敏感词
if ("系统".equals(name) || name.startsWith("@") || name.endsWith(":") || name.endsWith(":")){
send("系统:用户名中不能包含敏感字符,请重新输入!");
continue;
}
//经过上面的过滤,最终的用户名是长度合理并且不重复和不是敏感词
this.user.setUserName(name);
//告诉用户进入了聊天室
send("系统:恭喜您,"+name+",注册成功,欢迎加入高质量人类交流群");
//通知其他用户
sendToOther("系统:"+name+"加入了高质量人类交流群,大家一起出来欢迎他吧!");
break;
}while (true);
}
/**
* 给其他用户发送消息
* @param content 内容
*/
private void sendToOther(String content) throws IOException {
for (ServerThread thread : DB.list) {
if (thread != this && thread.getUser().getUserName()!=null){
thread.send(content);
}
}
}
/**
* 判断用户名是否重复
* @param name 用户输入的用户名
* @return 已经存在 true 不存在false
*/
private boolean checkName(String name) {
for (ServerThread thread : DB.list) {
if (name.equals(thread.getUser().getUserName())){
return true;
}
}
return false;
}
/**
* 给用户发送系统消息
* @param content 需要发送的内容
* @throws IOException
*/
private void send(String content) throws IOException {
bw.write(content);
bw.newLine();
bw.flush();
}
public User getUser() {
return user;
}
}
客户端读线程
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ReaderThread extends Thread {
private Socket socket;
private BufferedReader br;
public ReaderThread(Socket socket) throws IOException {
this.socket = socket;
this.br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
@Override
public void run() {
try{
String content = null;
while(true){
content = br.readLine();
System.out.println(content);
}
} catch (IOException e) {
System.out.println("系统:服务器已关闭");
System.exit(0);
} finally {
IOUtils.closeAll(br,socket);
}
}
}
客户端写线程
import java.io.*;
import java.net.Socket;
public class WriterThread extends Thread {
private Socket socket;
private BufferedWriter bw;
private BufferedReader br;
public WriterThread(Socket socket) throws IOException {
this.socket = socket;
this.bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
this.br = new BufferedReader(new InputStreamReader(System.in));
}
@Override
public void run() {
try{
String content = null;
System.out.println("系统:请输入用户名注册");
while(true){
content = br.readLine();
bw.write(content);
bw.newLine();
bw.flush();
if ("esc".equalsIgnoreCase(content)){
System.exit(0);
}
}
} catch (IOException e) {
System.out.println("客户端写出线程异常");
e.printStackTrace();
} finally {
IOUtils.closeAll(br,bw,socket);
}
}
}
存放数据的类
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class DB {
public static final List<ServerThread> list = new CopyOnWriteArrayList<>();
}
关流工具类
import java.io.Closeable;
import java.io.IOException;
public class IOUtils {
public static void closeAll(Closeable ...closeables){
for (Closeable closeable : closeables) {
if (closeable != null){
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}