客户端:
/**
* 将聊天室客户端内容独立完成一次.
* 下面内容可以选做:
* 修改代码,使聊天室可以实现用户自定义昵称,
* 以及私聊功能。
*
* 参考思路:
* 客户端连接服务端后,要求用户输入一个昵称,
* 然后将改昵称发送给服务端,服务端那边读取到
* 客户端发送的第一个字符串认为是昵称。
*
* 私聊功能可以定义格式,例如:
* @张三:你好
* 服务端在读取客户端发送过来的昵称时,需要进行
* 解析,分解出昵称与聊天内容,然后将该聊天内容
* 单独发送给指定昵称的用户。
* 服务端的输出流需要使用Map进行维护,而不能再
* 使用List,Map的key可以是该用户昵称,value
* 为该用户输出流。
* @author Xiloer
*
*/
public class Client {
private Socket socket;
public Client() throws IOException{
socket=new Socket("176.195.107.88",8088);
System.out.println("已连接服务器!");
}
private void inputNickName(Scanner scan) throws Exception
{
String nickName = null;
//创建输出流
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream(),
"UTF-8"),true);
//创建输入流
BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream(),"UTF-8"));
while(true)
{
System.out.println("请创建您的昵称:");
nickName = scan.nextLine();
if (nickName.trim().equals(""))
{
System.out.println("昵称不得为空");
}
else
{
pw.println(nickName);
String pass = br.readLine();
if(pass!=null&&!pass.equals("OK"))
{
System.out.println("昵称已经被占用,请更换!");
break;
}
else
{
System.out.println("你好!"+nickName+"可以开始聊天了");
break;
}
}
}
}
public void start(){
try {
System.out.println("请输入:");
Scanner scanner=new Scanner(System.in);
inputNickName(scanner);
ServerHandler server=new ServerHandler();
Thread t=new Thread(server);
t.start();
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");
PrintWriter pw = new PrintWriter(osw,true);
while(true)
{
pw.println(scanner.nextLine());
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(socket !=null)
{
try
{
socket.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws IOException {
Client client=new Client();
client.start();
}
private class ServerHandler implements Runnable{
public void run() {
try {
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is,"UTF-8");
BufferedReader br = new BufferedReader(isr);
String msgString = null;
while((msgString = br.readLine())!= null)
{
System.out.println(msgString);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
服务端:
/**
* 将聊天室服务端内容独立完成一次
* 下面内容可以选做:
* 配合客户端实现支持昵称与私聊功能
* @author Xiloer
*
*/
public class Server {
private ServerSocket server;
// private List<PrintWriter> allOut;
private Map<String,PrintWriter> allOut;
public Server(){
try {
server=new ServerSocket(8088);
allOut = new HashMap<String, PrintWriter>();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void addOut(String key,PrintWriter value)
{
synchronized(this)
{
allOut.put(key, value);
}
}
private synchronized void removeOut(String key)
{
allOut.remove(key);
System.out.println("当前在线人数为:"+ allOut.size());
}
private synchronized void sendMsgToAll(String message)
{
for(PrintWriter out: allOut.values())
{
out.println(message);
System.out.println("当前在线人数为:"+ allOut.size());
}
}
private synchronized void sendMsgToPrivate(String nickname,String message)
{
PrintWriter pw = allOut.get(nickname); //将对应客户端的聊天信息取出作为私聊内容发送出去
if(pw!=null)
{
pw.println(message);
System.out.println("1111111111111111111111111111111111111");
System.out.println("当前在线私聊人数为:"+ allOut.size());
}
}
public void start(){
try{
while(true){
System.out.println("等待客户端连接.......");
Socket socket=server.accept();
System.out.println("客户端已连接!");
ClientHandler client=new ClientHandler(socket);
Thread t=new Thread(client);
t.start();
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server=new Server();
server.start();
}
private class ClientHandler implements Runnable{
private Socket socket;
private String host;
private String nickName;
public ClientHandler(Socket socket){
this.socket=socket;
InetAddress adress=socket.getInetAddress();
host=adress.getHostAddress();
}
private String getNickName() throws Exception
{
try
{
//服务端的输入流读取客户端发送来的昵称输出流
InputStream in = socket.getInputStream();
InputStreamReader isr =
new InputStreamReader(in,"UTF-8");
BufferedReader bReader = new BufferedReader(isr);
//服务端将昵称验证结果通过自身的输出流发送给客户端
OutputStream out = socket.getOutputStream();
OutputStreamWriter iosw =
new OutputStreamWriter(out,"UTF-8");
PrintWriter ipw = new PrintWriter(iosw,true);
//读取客户端发来的昵称
String nameString = bReader.readLine();
while(true)
{
if(nameString.trim().length()==0)
{
ipw.println("FAIL");
}
if(allOut.containsKey(nameString))
{
ipw.println("FAIL");
}
else
{
ipw.println("OK");
return nameString;
}
}
}
catch(Exception e)
{
throw e;
}
}
public void run()
{
PrintWriter pw = null;
try
{
/*
* 通过客户端的Socket获取客户端的输出流
* 用来将消息发送给客户端
*/
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");
pw = new PrintWriter(osw,true);
/*
* 将客户昵称和其所说的话作为元素存入共享集合HashMap中
*/
nickName = getNickName();
addOut(nickName, pw);
Thread.sleep(100);
/*
* 服务端通知所有客户端,某用户登录
*/
sendMsgToAll("[系统通知]:欢迎**"+nickName+"**登陆聊天室!");
/*
* 通过客户端的Socket获取输入流
* 读取客户端发送来的信息
*/
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is,"UTF-8");
BufferedReader br = new BufferedReader(isr);
String msgString = null;
while((msgString = br.readLine())!=null)
{
//验证是否是私聊
if(msgString.startsWith("@"))
{
/*
* 私聊格式:@昵称:内容
*/
int index = msgString.indexOf(":");
if(index >=0)
{
//获取昵称
String name = msgString.substring(1,index);
String info = msgString.substring(index+1,msgString.length());
info = nickName + "对你说:"+ info;
//将私聊信息发送出去
sendMsgToPrivate(name, info);
//服务端不在广播私聊的信息
continue;
}
System.out.println("已输出");
}
/*
* 遍历所有输出流,将该客户端发送的信息转发给所有客户端
*/
System.out.println(nickName+"说:"+ msgString);
sendMsgToAll(nickName+"说:"+ msgString);
}
}
catch (Exception e)
{
/*
* 因为Win系统用户的客户端断开连接后,br.readLine()方法读取
* 不到信息就会抛出异常,而Linux系统会持续发送null;
* 因此这里就不在将捕获的异常抛出了。
*/
}
finally
{
/*
* 当执行到此处时,说明客户端已经与服务端断开连接
* 则将该客户端存在共享集合中的输出流删除
*/
removeOut(nickName);
/*
* 通知所有客户端,某某客户已经下线
*/
sendMsgToAll("[系统通知]:"+nickName + "已经下线了。");
/*
* 关闭socket,则通过Socket获取的输入输出流也一同关闭了
*/
if(socket!=null)
{
try
{
socket.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
}
}