需求
使用TCP的Socket实现一个聊天室(未实现图形界面)
- 服务器端:一个线程专门发送消息,一个线程专门接受消息。
- 客户端:一个线程专门发送消息,一个线程专门接受消息。
- 群聊,私聊
代码
工具类:
package chat;
import java.io.Closeable;
/**
* 工具类
* @author 王星宇
* @date 2020年2月21日
*/
public class Utils {
//释放资源
public static void close(Closeable ... targets) {
for(Closeable target : targets) {
try {
if(target != null) target.close();
}catch(Exception e) {
e.printStackTrace();
}
}
}
}
服务器端
package chat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 在线聊天室:服务端
* @author 王星宇
* @date 2020年2月21日
*/
public class Server {
//CopyWriteArrayList可以避免在一边遍历容器,一边修改容器的时候出错
private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<Channel>();
public static void main(String[] args) throws IOException {
System.out.println("---------Server-----------");
ServerSocket server = new ServerSocket(9999);
boolean running = true;
while(running) {
Socket client = server.accept();
System.out.println("一个客户端建立了连接");
Channel c = new Channel(client);
all.add(c);
new Thread(c).start();
}
}
//一个客户代表一个channel
//服务器用channel是因为服务器一定是先接受信息,在发送信息
static class Channel implements Runnable{
private DataInputStream dis;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
private String name;
public Channel(Socket client) {
this.client = client;
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
this.name = receive();
send("欢迎加入聊天室");
sendOthers(name + "加入了群聊",true);
} catch (IOException e) {
e.printStackTrace();
release();
}
isRunning = true;
}
//接受消息
private String receive() {
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
e.printStackTrace();
release();
}
return msg;
}
//发送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
release();
}
}
//群聊
//私聊:约定格式:@xxx:msg
private void sendOthers(String msg,boolean isSys) {
boolean isPrivate = msg.startsWith("@");
if(isPrivate) {//如果时私聊
//获取目标
int idEnd = msg.indexOf(":");//名字不能有冒号
String name = msg.substring(1,idEnd);
msg = msg.substring(idEnd + 1);
for(Channel other : all) {
if(other.getName().equals(name)) {
other.send(this.name + " 对您说:" + msg);
break;
}
}
}else {
for (Channel other : all) {
if (other.equals(this)) {
continue;
}
if(!isSys)
other.send(this.name + ":" + msg);
else
other.send("系統:" + msg);
}
}
}
//释放资源
private void release() {
this.isRunning = false;
Utils.close(dis,dos,client);
//退出容器
all.remove(this);
sendOthers(this.name + "退出了群聊",true);
}
@Override
public void run() {
while(isRunning) {
String msg = receive();
if(!msg.equals("")) {
sendOthers(msg,false);
}
}
}
public String getName() {return this.name;}
}
}
客户用send类
package chat;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class send implements Runnable{
private BufferedReader br;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
private String name ;
public send(Socket client,String name) {
this.name = name;
this.client = client;
this.isRunning = true;
br = new BufferedReader(new InputStreamReader(System.in));
try {
dos = new DataOutputStream(client.getOutputStream());
Send(name);
} catch (IOException e) {
e.printStackTrace();
release();
}
}
@Override
public void run() {
String msg;
while(isRunning) {
msg = getInfoFromConsole();
if(!msg.equals("")) {
Send(msg);
}
}
}
private void Send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
release();
}
}
//从控制台获取信息
private String getInfoFromConsole() {
try {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
//释放资源
private void release() {
this.isRunning = false;
Utils.close(dos, client);
}
}
客户用receive类
package chat;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
public class receive implements Runnable{
private DataInputStream dis ;
private Socket client;
private boolean isRunning;
public receive(Socket client) {
this.client = client;
this.isRunning = true;
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
e.printStackTrace();
release();
}
}
@Override
public void run() {
while(isRunning) {
String msg = Receive();
if(!msg.equals("")) {
System.out.println(msg);
}
}
}
private String Receive() {
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
e.printStackTrace();
release();
}
return msg;
}
//释放资源
private void release() {
this.isRunning = false;
Utils.close(dis , client);
}
}
客户类
package chat;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 在线聊天室:客户端
*
* @author 王星宇
* @date 2020年2月21日
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入用户名:");
String name = br.readLine();
System.out.println("---------" + name + "-----------");
Socket client = new Socket("localhost",9999);
new Thread(new send(client,name)).start();//一个线程实时发送信息
new Thread(new receive(client)).start();//一个线程实时接受消息
}
}