您的位置:首页 > 其它

简单的基于socket通讯的聊天室,详细讲解

2012-12-06 00:16 447 查看
//下面是服务器端的代码
import java.net.*;
import java.io.*;
import java.io.PrintStream;
import java.util.*;
interface CrazyProtocol
{
int PROTOCOL_LEN=2;      //定义协议字符串长度
String MSG_ROUND="MM";   //发群聊信息
String USER_ROUND="UU";  //用户名前缀
String LOGIN_SUCCESS="1";//用户名成功
String NAME_PER="-1";    //用户名重复
String PRIVATE_ROUND="PP";;//私聊前缀
String SPLIT_SIGN="SS";  //字符串分隔
}
class CrazyMap<K,V> extends HashMap<K,V>
{
public void removeByValue(Object value)//根据value删除指定项
{
for(Object key:keySet())
if(get(key)==value)
{
remove(key);
break;
}
}
public Set<V> valueSet()//获取所有value组成的Set集合
{
Set<V> valueSet=new HashSet<V>();
for(K key:keySet())
valueSet.add(get(key));
return valueSet;
}
public K getKeyByValue(V value)//根据value查找指定的key
{
for(K key:keySet())
if(get(key).equals(value) && get(key)==value)
return key;
return null;
}
//重写HashMap的put方法,该方法不允许value重复
public V put(K key,V value)
{
for(V val : valueSet())
if (val.equals(value) && val.hashCode()==value.hashCode())
throw new RuntimeException("MyMap中不允许重复的value!");
return super.put(key,value);
}
}
class Server
{
private static final int PORT=30000;
public static CrazyMap<String,PrintStream> clients=new CrazyMap<>();
public static void main(String[] args)
{
try(ServerSocket ss=new ServerSocket(PORT))
{
System.out.println("服务器启动成功");
while(true)
{
Socket socket=ss.accept();
new Thread(new ServerThread(socket)).start();
}
}
catch(IOException e)
{
System.out.println("服务器启动超时,知否端口"+PORT+"已被占用");
}
}
}
class ServerThread implements Runnable
{
private Socket s;
BufferedReader br=null;
PrintStream ps=null;
public ServerThread(Socket s)
{
this.s=s;
}
//将读取的内容去掉前后的协议,恢复成真正数据
public String getRealMsg(String line)
{
return line.substring(CrazyProtocol.PROTOCOL_LEN,(line.length()-CrazyProtocol.PROTOCOL_LEN));
}
public void run()
{
try
{
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
ps=new PrintStream(s.getOutputStream());
FileOutputStream fos=new FileOutputStream("fuwuqi.txt");
fos.write(new String("").getBytes()); //清空文本内容
PrintStream ser=new PrintStream(fos);
ser.println("服务器启动成功!");
String line=null;
while((line=br.readLine())!=null)
{
//如果读到的行是以CrazyProtocol.USER_ROUND开头,并以其结束,则可以确定读到的是用户登陆的用户名
if(line.startsWith(CrazyProtocol.USER_ROUND) && line.endsWith(CrazyProtocol.USER_ROUND))
{
String userName=getRealMsg(line);//得到真实姓名
if(Server.clients.containsKey(userName))
{
System.out.println("用户名重复");
ps.println(CrazyProtocol.NAME_PER);

}
else
{
Server.clients.put(userName,ps);
ps.println(CrazyProtocol.LOGIN_SUCCESS);
ser.println("恭喜用户:"+userName+" 登陆成功");
ser.println("当前的用户数为:"+Server.clients.size());
System.out.println("恭喜用户:"+userName+" 登陆成功");
System.out.println("当前的用户数为:"+Server.clients.size());
ps.println("恭喜登陆成功!");
}
}
//如果读到的是以CrazyProtocol.PRIVATE_ROUND开始并以其结束,则可以确定为私聊信息
else if(line.startsWith(CrazyProtocol.PRIVATE_ROUND) && line.endsWith(CrazyProtocol.PRIVATE_ROUND))
{
String userMsg=getRealMsg(line);
//以SPLIT_SIGN分割字符串,前半部分为私聊用户,后半部分为聊天信息
String user=userMsg.split(CrazyProtocol.SPLIT_SIGN)[0];
String msg=userMsg.split(CrazyProtocol.SPLIT_SIGN)[1];
boolean pp=false;//标记是否找到该用户
for(String name:Server.clients.keySet())
{
if(name.equals(user))
{
pp=true;
break;
}
}
if(pp==false)//未找到该私聊对象
ps.println("未找到该私聊对象");
else
{
Server.clients.get(user).println(Server.clients.getKeyByValue(ps)+"悄悄对你说:"+msg);
ser.println(Server.clients.getKeyByValue(ps)+" 对 "+user+" 说:"+msg);
System.out.println(Server.clients.getKeyByValue(ps)+" 对 "+user+" 说:"+msg);
}
}
else  //公聊要对每个Client发送信息
{
String msg=getRealMsg(line);
String name=Server.clients.getKeyByValue(ps);//保存当前用户名
Server.clients.removeByValue(ps); //先把自己当前的线程删掉,后面再添加进去
for(PrintStream clientPs:Server.clients.valueSet())
clientPs.println(name+"说: "+msg);
Server.clients.put(name,ps);
ser.println(name+" 对大家说: "+msg);
System.out.println(name+" 对大家说: "+msg);
}
}
}
//捕获到异常,表明Socket对应的用户客户端出现了问题,所以出现将其对应的输出流从Map中删除
catch(IOException e)
{
Server.clients.removeByValue(ps);
System.out.println("当前的用户数为:"+Server.clients.size());
//关闭网络,IO资源
try
{
if(br != null)
br.close();
if(ps != null)
ps.close();
if(s != null)
s.close();
}
catch(IOException m)
{
m.printStackTrace();
}
}
}
}
//下面是客户端的代码:

import java.net.*;
import java.io.*;
import java.io.PrintStream;
import java.util.*;
import javax.swing.JOptionPane;
class Client  //主要处理想服务器发送信息
{
private static final int SERVER_PORT=30000;
private Socket s;
private PrintStream ps;
private BufferedReader keyIn;
private BufferedReader brServer;
public void init()
{
try
{
keyIn=new BufferedReader(new InputStreamReader(System.in));
Socket s=new Socket("127.0.0.1",SERVER_PORT);
ps=new PrintStream(s.getOutputStream());
brServer=new BufferedReader(new InputStreamReader(s.getInputStream()));
String tip="";
while(true)
{
String userName=JOptionPane.showInputDialog(tip+"请输入用户名:");
ps.println(CrazyProtocol.USER_ROUND+userName+CrazyProtocol.USER_ROUND);
String result=brServer.readLine();
if(result.equals(CrazyProtocol.NAME_PER))
{
tip="用户名重复,请重新输入!";
continue;
}
if(result.equals(CrazyProtocol.LOGIN_SUCCESS))
break;
}
}
catch(UnknownHostException e)
{
System.out.println("找不到远程服务器,请确认服务器是否已经启动");
closeRs();
System.exit(1);
}
catch(IOException e)
{
System.out.println("网络异常,请重新登陆");
closeRs();
System.exit(1);
}
//以该Socket对应的输入流启动ClientThread线程
new ClientThread(brServer).start();
}
//定义一个读取键盘输入,并发向网络的方法
public void readAddSend()
{
try
{
String line=null;
while((line=keyIn.readLine())!=null)
{
//如果发送信息中有冒号,且以/开头,则认为想发送私聊信息
if(line.startsWith("/") && line.indexOf(":")>0)
{
line=line.substring(1);
ps.println(CrazyProtocol.PRIVATE_ROUND+line.split(":")[0]+CrazyProtocol.SPLIT_SIGN+line.split(":")[1]+CrazyProtocol.PRIVATE_ROUND);
}
else
{
ps.println(CrazyProtocol.MSG_ROUND+line+CrazyProtocol.MSG_ROUND);
}
}
}
catch(IOException em)
{
System.out.println("网络通讯异常!请重新登陆!");
closeRs();
System.exit(1);
}
}
//关闭输入流,输出流,Socket的方法
public void closeRs()
{
try
{
if(keyIn!=null)
keyIn.close();
if(ps!=null)
ps.close();
if(s!=null)
s.close();
}
catch(IOException ex)
{
ex.printStackTrace();
}
}
public static void main(String[] args)
{
Client mm=new Client();
mm.init();
mm.readAddSend();
}
}

//不断读取来自服务器的的信息
class ClientThread extends Thread
{
BufferedReader br;
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 e)
{
e.printStackTrace();
}
finally
{
try
{
if(br!=null)
br.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: