Java实现局域网聊天室功能(私聊、群聊)
本文实例为大家分享了Java实现局域网聊天室功能的具体代码,供大家参考,具体内容如下
Server 服务端
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static final int SERVER_PORT=8080;
//使用CrazyitMap对象来保存每个客户名字和对应输出流之间的对应关系
public static CrazyitMap<String,PrintStream> clients=new CrazyitMap<>();
public void init(){
try( //建立监听的ServerSocket
ServerSocket ss=new ServerSocket(SERVER_PORT))
{
//采用死循环来不断地接收来自客户端的请求
while(true){
Socket socket=ss.accept();
new ServerThread(socket).start();
}
}
//如果抛出异常
catch (IOException ex){
System.out.println("服务器启动失败,是否端口"+SERVER_PORT+"已被占用");
}
}
public static void main(String[] args){
Server server=new Server();
server.init();
}
}
ServerThread 服务端线程
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class ServerThread extends Thread {
private Socket socket;
BufferedReader br = null;
PrintStream ps = null;
//定义一个构造器,用于接收一个Socket来创建ServerThread线程
public ServerThread(Socket socket) {
this.socket = socket;
}
public void run() {
try {
//获取该Socket对应的输入流
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取该Socket对应的输出流
ps = new PrintStream(socket.getOutputStream());
String line = null;
while ((line = br.readLine()) != null) {
//如果读到的行以CrazyitProtocol.USER_ROUND开始,并以其结束
//则可以确定读到的是用户登录的用户名
if (line.startsWith(CrazyitProtocol.USER_ROUND) && line.endsWith(CrazyitProtocol.USER_ROUND)) {
//得到真实消息
String userName = getRealMsg(line);
//如果用户名重复
if (Server.clients.map.containsKey(userName)) {
System.out.println("重复");
ps.println(CrazyitProtocol.NAME_REP);
} else {
System.out.println(userName+"上线");
ps.println(CrazyitProtocol.LOGIN_SUCCESS);
Server.clients.put(userName, ps);
}
}
//如果读到的行以CrazyitProtocol.PRIVATE_ROUND开始,
//则可以确定是私聊信息,私聊信息只向特定的输入流发送
else if (line.startsWith(CrazyitProtocol.PRIVATE_ROUND) && line.endsWith(CrazyitProtocol.PRIVATE_ROUND)) {
//得到真实的消息
String userAndMsg = getRealMsg(line);
//以SPLIT_SIGN分割字符串,前半是私聊用户,后半是聊天信息
String user = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[0];
String msg = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[1];
//获取私聊用户对应的输出流,并发送私聊信息
Server.clients.map.get(user).println(Server.clients.geTKEyByValue(ps) + "悄悄对你说:" + msg);
}
//公聊要向每一个Socket发送
else {
//得到真实消息
String msg = getRealMsg(line);
//遍历clients中的每个输出流
for (PrintStream clientPs : Server.clients.valueSet()) {
clientPs.println(Server.clients.getKeyByValue(ps) + "说:" + msg);
}
}
}
}
//捕获到异常后,表明Socket对应的客户端已经出现了问题
//所以程序将其对应的输出流从Map中删除
catch (IOException e) {
Server.clients.removeByValue(ps);
System.out.println(Server.clients.map.size());
//关闭网络,IO资源
try {
if (br != null) {
br.close();
}
if (ps != null) {
ps.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
//将读到的内容去掉前后协议字符,恢复成真实数据
private String getRealMsg(String line) {
return line.substring(CrazyitProtocol.PROTOCOL_LEN,line.length()-CrazyitProtocol.PROTOCOL_LEN);
}
}
Client 客户端
import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
private static final int SERVER_PORT=8080;
private Socket socket;
private PrintStream ps;
private BufferedReader brServer;
private BufferedReader keyIn;
public void init(){
try
{
//初始化代表键盘的输入流
keyIn=new BufferedReader(new InputStreamReader(System.in));
//连接到服务器端
socket=new Socket("127.0.0.1",SERVER_PORT);
//获取该Socket对应的输入流和输出流
ps=new PrintStream(socket.getOutputStream());
brServer=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String tip=" ";
//采用不断循环地弹出对话框要求输入用户名
while(true){
String userName= JOptionPane.showInputDialog(tip+"输入用户名");
//用户输入的用户名前后增加协议字符串后发送
ps.println(CrazyitProtocol.USER_ROUND+userName+CrazyitProtocol.USER_ROUND);
//读取服务器端的响应
String result=brServer.readLine();
//如果用户名重复,则开始下一次循环
if (result.equals(CrazyitProtocol.NAME_REP)){
tip="用户名重复,请重试";
continue;
}
//服务器端登录成功
if (result.equals(CrazyitProtocol.LOGIN_SUCCESS)){
break;
}
}
}
//捕获到异常,关闭网络资源,并退出该程序
catch (UnknownHostException ex){
System.out.println("找不到远程服务器,请确定服务器已经启动");
closeRs();
System.exit(1);
}
catch(IOException ex){
System.out.println("网络异常,请重新登录");
closeRs();
System.exit(1);
}
//以该Socket对应的输入流启动ClientThread线程
new ClientThread(brServer).start();
}
//定义一个读取键盘输出,并以网络发送的方法
private void readAndSend(){
try
{
//不断读取键盘输入
String line=null;
while ((line=keyIn.readLine())!=null){
//如果发送的信号中有冒号,并以//开头,则认为想发送私聊信息
if (line.indexOf(":")>0&&line.startsWith("//")){
line=line.substring(2);
ps.println(CrazyitProtocol.PRIVATE_ROUND+line.split(":")[0]+CrazyitProtocol.SPLIT_SIGN+line.split(":")[1]+CrazyitProtocol.PRIVATE_ROUND);
}
else{
ps.println(CrazyitProtocol.MSG_ROUND+line+CrazyitProtocol.MSG_ROUND);
}
}
}
catch (IOException ex){
System.out.println("网络通信异常!请重新登录");
closeRs();
System.exit(1);
}
}
//关闭Socket,输入流,输出流的方法
private void closeRs(){
try
{
if (keyIn!=null){
ps.close();
}
if (brServer!=null){
ps.close();
}
if (ps!=null){
ps.close();
}
if (socket!=null){
keyIn.close();
}
}
catch (IOException ex){
ex.printStackTrace();
}
}
public static void main(String[] args){
Client client=new Client();
client.init();
client.readAndSend();
}
}
ClientThread 客户端线程
import java.io.BufferedReader;
import java.io.IOException;
public class ClientThread extends Thread {
//该客户端线程负责处理输入流
BufferedReader br=null;
//使用一个网络输入流来创建客户端线程
public ClientThread(BufferedReader br){
this.br=br;
}
public void run(){
try
{
String line=null;
//不断地从输入流中读取数据,并将这些数据打印输出
while((line=br.readLine())!=null){
System.out.println(line);
}
}
catch (IOException ex){
ex.printStackTrace();
}
finally {
try {
if (br!=null){
br.close();
}
}
catch (IOException ex){
ex.printStackTrace();
}
}
}
}
CrazyitMap 容器
import java.util.*;
public class CrazyitMap<k,v> {
//创建一个线程安全的HashMap
public Map<k,v> map= Collections.synchronizedMap(new HashMap<k,v>());
//根据value来删除指定项
public synchronized void removeByValue(Object value){
for (Object key:map.keySet()){
if (map.get(key)==value){
map.remove(key);
break;
}
}
}
//获取所有value组成的Set集合
public synchronized Set<v> valueSet(){
Set<v> result=new HashSet<v>();
//将map中的所有value添加到result集合中
map.forEach((key,value)->result.add(value));
return result;
}
//根据value查找key
public synchronized k getKeyByValue(v value){
//遍历所有key组成的集合
for (k key:map.keySet()){
//如果指定key对应的value与被搜索的value相同,则返回对应的key
if(map.get(key)==value||map.get(key).equals(value)){
return key;
}
}
return null;
}
//实现put()方法,该方法不允许value重复
public synchronized v put(k key,v value){
//遍历所有value组成的集合
for (v val:valueSet()){
//如果某个value与试图放入集合的value相同
//则抛出一个RuntimeException异常
if (val.equals(value)&&val.hashCode()==value.hashCode()){
throw new RuntimeException("MyMap实例不允许有重复的value");
}
}
return map.put(key,value);
}
}
CrazyitProtocol 接口
public interface CrazyitProtocol {
//定义协议字符串的长度
int PROTOCOL_LEN=2;
//下面是一些协议字符串,服务器端和客户端交换的信息都应该在前后添加这种特殊字符串
String MSG_ROUND="ηθ";
String USER_ROUND="∏∑";
String LOGIN_SUCCESS="1";
String NAME_REP="-1";
String PRIVATE_ROUND="★【";
String SPLIT_SIGN="卐";
}
测试
1、启动服务端
2、启动客户端 (上线)
3 、群发
4、私聊 (//名字:)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
相关文章