Java网络编程:TCP实现群聊&私聊代码
和上一篇博客差不多,只不过是在群里的基础之上增加了私聊的功能,我们约定,私聊格式为:@xxx:msg
如何实现私聊呢,加入客户端c给服务器发送消息,服务器不再是把消息转发给所以除c以外的客户端,而是解析数据格式,转发给与name(“:”之前,“@”之后的字符串)有相同名字的客户端即可。
群聊功能与之前类似,这里不再说说明。
下面是所有类的代码:
(1)Server类的代码:
package chat4;
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;
/**注意:实现私聊(约定数据格式为:@xxx:msg
* 在线聊天室:使用容器实现群聊
* 服务器
* @author Administrator
* 需要修改和遍历数据的时候使用list会操作不方便
* 问题:为什么加了static,在主函数里面就没错啦?
*
*/
public class Server {
//CopyOnWriteArrayList和list用法一样
private static CopyOnWriteArrayList<Channel> all=new CopyOnWriteArrayList<Channel>();
public static void main(String[] args) throws IOException {
System.out.println("----------server----------");
//创建服务器
ServerSocket server=new ServerSocket(8977);
while(true) {
//阻塞式等待客户端的连接
Socket client=server.accept();
System.out.println("一个客户端连接上。。。");
Channel c=new Channel(client);
all.add(c);//容器管理所有的成员
new Thread(c).start();
}
}
//一个客户代表一个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());
isRunning=true;
//获取名称
name=receiveMsg();
this.sendMsg(name+"同学,"+"欢迎您的到来");
sendOther(this.name+"加入群聊天室",true);
} catch (IOException e) {
System.out.println("-----1 出问题----");//自己方便观察哪里出问题
release();//注意:这里做了修改
}
}
//接收消息(服务器读取客户端发过来的信息)
private String receiveMsg() {
String msg="";
try {
msg=dis.readUTF();
} catch (IOException e) {
System.out.println("-----receive 出问题----");//自己方便观察哪里出问题
release();//注意:这里做了修改
}
return msg;
}
//发送消息(服务器反馈信息给客户端)
private void sendMsg(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
System.out.println("-----send 出问题----");//自己方便观察哪里出问题
release();//注意:这里做了修改
}
}
//发送消息(服务器反馈信息(其实就是服务器读取的当前客户端的消息)给其他所有的客户端(除了当前客户端)
private void sendOther(String msg,boolean isSysMsg) {
boolean isPrivate=msg.startsWith("@");
if(isPrivate) {//是私聊消息
int idx=msg.indexOf(":");
//获取目标和数据
String targetName=msg.substring(1,idx);
msg=msg.substring(idx+1);
System.out.println("服务器收到的私聊信息为:"+targetName+"&"+msg);
for(Channel other:all) {
if(other.name.equals(targetName)) {//找到目标
other.sendMsg(this.name+":"+msg);
}
}
}else {
for(Channel other:all) {//服务器把收到的客户端的消息发给其余所有的客户端(除了当前客户端)
if(other!=this) {
if(isSysMsg) {
other.sendMsg(msg);//系统消息
}else {
other.sendMsg(this.name+":"+msg);//群聊消息
}
}
}
}
}
//释放资源
private void release() {
this.isRunning=false;//释放资源意味着该该客户端要停下来
Utils.close(dis,dos,client);//自己写的一个工具类Utils
all.remove(this);//当前客户端退出
sendOther(this.name+"离开了聊天室。。。", true);
}
@Override
public void run() {
while(isRunning) {
//流程:服务器读取客户端消息-服务器发送反馈消息给客户端-释放资源
String msg=receiveMsg();
//System.out.println("服务器读取到的信息为:"+msg);
if(!msg.equals("")) {
//sendMsg(msg);
sendOther(msg,false);//群聊消息
}
}
//release();//老师这里没有释放
}
}
}
(2)Client类的代码(下面的Receive类和Send类为Client类所用):
package chat4;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
/**注意:
* 在线聊天室:使用容器实现群聊
* 客户端
* @author Administrator
*
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
System.out.println("---------------client--------------");
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入用户名:");
String name=br.readLine();
//建立套接字
Socket client=new Socket("localhost", 8977);
//客户端发送消息(先读取控制台的内容,再写入到客户端的输出流里)
new Thread(new Send(client,name)).start();
new Thread(new Receive(client)).start();
}
}
(3)Receive类的代码:
package chat4;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
*作用: 被MutiClient类用到,是客户端用来接收服务器反馈的消息的
*客户端读取服务器反馈的消息,并显示输出
* @author Administrator
*
*/
public class Receive implements Runnable {
private DataInputStream dis;
private Socket client;
private boolean isRunning;
public Receive(Socket client) {
this.client=client;
isRunning=true;
try {
dis=new DataInputStream(client.getInputStream());
} catch (IOException e) {
System.out.println("客户端的Receive构造器有问题");
release();
}
}
@Override
public void run() {
while(isRunning) {
String msg=receiveMsg();
if(!msg.equals("")) {
System.out.println(msg);
}
}
}
private String receiveMsg() {
String msg="";
try {
msg=dis.readUTF();
} catch (IOException e) {
System.out.println("客户端的receiveMsg有问题");
release();
}
return msg;
}
//释放资源
private void release() {//内部类的使用
this.isRunning=false;//释放资源意味着该该客户端要停下来
Utils.close(dis,client);//自己写的一个工具类Utils
}
}
(4)Send类的代码:
package chat4;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 作用:被MutiClient类用到,是客户端用来发送消息的
* 具体操作是:客户端读取控制台消息--写入到client的输出流
* @author Administrator
*
*/
public class Send implements Runnable {
private BufferedReader console;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
private String name;
public Send(Socket client,String name) {
this.client=client;
this.name=name;
console=new BufferedReader(new InputStreamReader(System.in));
try {
dos=new DataOutputStream(client.getOutputStream());
sendMsg(name);//发送名字
isRunning=true;
} catch (IOException e) {
System.out.println("客户端的send构造器有问题");
this.release();
}
}
@Override
public void run() {
while(isRunning) {
//客户端读取控制台消息--写入到client的输出流
String msg=ConsoleMsg();
if(!msg.equals("")) {
sendMsg(msg);
}
}
}
private String ConsoleMsg() {//先读取控制台的内容
String msg="";
try {
msg=console.readLine();
} catch (IOException e) {
System.out.println("客户端的ConsoleMsg有问题");
this.release();
}
return msg;
}
private void sendMsg(String msg) {//再写入到客户端的输出流里
try {
dos.writeUTF(msg);
dos.flush();//这个一定不能忘记啦
} catch (IOException e) {
System.out.println("客户端的sendMsg有问题");
this.release();
}
}
//释放资源
private void release() {//内部类的使用
this.isRunning=false;//释放资源意味着该该客户端要停下来
Utils.close(dos,client);//自己写的一个工具类Utils
}
}
(5)Utils类的代码(编写工具类,方便资源的释放):
package chat4;
import java.io.Closeable;
/**
* 自己写一个释放资源的工具类
* @author Administrator
*
*/
public class Utils {
public static void close(Closeable... targets) {
for(Closeable target:targets) {
try {
if(null!=target) {
target.close();
}
}catch(Exception e){
}
}
}
}
运行结果如下: