1 完成聊天室的私聊功能

完成聊天室私聊功能。私聊功能是指,客户端之间可以实现一对一的聊天。

服务器端程序启动后,将等待客户端连接,界面效果如图-1所示:

图-1

客户端程序运行时,需要用户先输入昵称。用户输入昵称之后,提示用户可以开始聊天。界面效果如图-2所示:

图-2

另一个客户端运行起来后,也需要输入昵称,界面效果如图-3所示:

图-3

此时,其他运行中的客户端会收到昵称为“jerry”的客户端上线的消息。比如,之前运行起来的客户端“mary”的界面效果如图-4所示:

图-4

其他客户端可以通过输入类似“\jerry:你好”这样的字样和昵称为“jerry”的客户端私聊。比如,昵称为“mary”的客户端可以输入如图-5所示的信息:

图-5

注意:如果需要进行私聊,必需使用“\昵称:信息”的格式发送消息。其中,“\昵称:”为固定格式,“昵称”表示要私聊的客户端的昵称;“信息”表示需要发送的消息。例如:"\jerry:你好",表示发送消息“你好”给昵称为“jerry”的客户端。

昵称为“jerry”的客户端将接收到客户端“mary”发来的信息,界面效果如图-6所示:

图-6

如果某客户端程序停止运行,其他客户端程序可以接收到消息并显示。例如,昵称为“jerry”的客户端停止运行,昵称为“mary”的客户端的界面效果如图-7所示:

图-7

对于服务器端而言,只要有客户端连接,就会在界面输出提示信息。界面效果如图-8所示:

图-8

参考答案

实现此案例需要按照如下步骤进行。

步骤一:创建客户端类

新建名为com.tarena.homework的包,并在包下新建名为Client的类,用于表示客户端。

在Client 类中声明全局变量 socket 表示一个客户端Socket对象,并在实例化 Client 类时使用构造方法“Socket(String ip,int port)”来创建Socket类的对象。此时,需要进行异常处理。代码如下所示:

package com.tarena.homework;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端应用程序
 */
public class Client {
	//客户端Socket
	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
	public Client(){
		try {
			socket = new Socket("localhost",8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

步骤二:定义客户端线程要执行的任务

在Client类中定义成员内部类ServerHander。该内部类需要实现Runnable接口并实现该接口的run() 方法。在该方法中实现线程要执行的任务,在此,线程要执行的任务为循环接收服务端的消息并打印到控制台。代码如下所示:

public class Client {
	//其他代码,略
	
	/**
	 * 该线程用于接收服务端发送过来的信息
	 */
#cold_bold	private class ServerHander implements Runnable{
#cold_bold		@Override
#cold_bold		public void run() {
#cold_bold			try {
#cold_bold				InputStream in = socket.getInputStream();
#cold_bold				InputStreamReader isr = new InputStreamReader(in, "UTF-8");
#cold_bold				BufferedReader br = new BufferedReader(isr);
#cold_bold				while(true){
#cold_bold					System.out.println(br.readLine());
#cold_bold				}
#cold_bold			} catch (Exception e) {
#cold_bold				e.printStackTrace();
#cold_bold			}
#cold_bold		}
#cold_bold	}
}

步骤三:定义方法inputNickName(),用于输入昵称

为Client类定义方法inputNickName(),用于输入昵称。代码如下所示:

public class Client {
	//其他代码,略
	
	/**
	 * 输入昵称
	 */
#cold_bold	private void inputNickName(Scanner scanner)throws Exception{
#cold_bold		//定义昵称
#cold_bold		String nickName = null;
#cold_bold		//创建输出流
#cold_bold		PrintWriter pw = new PrintWriter(
#cold_bold							new OutputStreamWriter(
#cold_bold								socket.getOutputStream(),"UTF-8")
#cold_bold						 ,true);
#cold_bold		//创建输入流
#cold_bold		BufferedReader br = new BufferedReader(
#cold_bold								new InputStreamReader(
#cold_bold										socket.getInputStream(),"UTF-8")
#cold_bold						    );
#cold_bold		/*
#cold_bold		 * 循环以下操作
#cold_bold		 * 输入用户名,并上传至服务器,等待服务器回应,若昵称可用就结束循环,否则通知用户后
#cold_bold		 * 重新输入昵称
#cold_bold		 */
#cold_bold		while(true){
#cold_bold			System.out.println("请输入昵称:");
#cold_bold			nickName = scanner.nextLine();
#cold_bold			if(nickName.trim().equals("")){
#cold_bold				System.out.println("昵称不能为空");
#cold_bold			}else{
#cold_bold				pw.println(nickName);
#cold_bold				String pass = br.readLine();
#cold_bold				if(pass!=null&&!pass.equals("OK")){
#cold_bold					System.out.println("昵称已被占用,请更换。");
#cold_bold				}else{
#cold_bold					System.out.println("你好!"+nickName+",开始聊天吧!");
#cold_bold					break;
#cold_bold				}
#cold_bold			}
#cold_bold		}
#cold_bold	}
}

步骤四:创建客户端工作方法 start()

为 Client 类创建客户端工作方法 start()。在该方法中,首先调用方法inputNickName()得到用户昵称,然后启动接收服务端信息的线程,接收数据后打印显示。

代码如下所示:

public class Client {
	//其他代码,略

	/**
	 * 客户端工作方法
	 */
#cold_bold	public void start(){
#cold_bold		try {
#cold_bold			//创建Scanner读取用户输入内容
#cold_bold			Scanner scanner = new Scanner(System.in);
#cold_bold			//首先输入昵称
#cold_bold			inputNickName(scanner);
#cold_bold			
#cold_bold			//将接收服务端信息的线程启动
#cold_bold			ServerHander handler = new ServerHander();
#cold_bold			Thread t = new Thread(handler);
#cold_bold			t.setDaemon(true);
#cold_bold			t.start();
#cold_bold			
#cold_bold			OutputStream out = socket.getOutputStream();			
#cold_bold			OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");			
#cold_bold			PrintWriter pw = new PrintWriter(osw,true);
#cold_bold			while(true){
#cold_bold				pw.println(scanner.nextLine());
#cold_bold			}
#cold_bold		} catch (Exception e) {
#cold_bold			e.printStackTrace();
#cold_bold		} finally{
#cold_bold			if(socket != null){
#cold_bold				try {
#cold_bold					socket.close();
#cold_bold				} catch (IOException e) {
#cold_bold					e.printStackTrace();
#cold_bold				}
#cold_bold			}
#cold_bold		}
#cold_bold	}
}

步骤五:为客户端类定义 main() 方法

为类 Client 定义 main() 方法,并在该方法中,创建 Client 对象,调用上一步中所创建的 start() 方法。代码如下所示:

/**
 * 客户端应用程序
 */
public class Client {
	//其他代码,略
	
#cold_bold	public static void main(String[] args) {
#cold_bold		Client client = new Client();
#cold_bold		client.start();
#cold_bold	}
}

步骤六:定义 Server类

定义Server类,并在Server类中添加ExecutorService类型的属性threadPool,并在构造方法中将其初始化。初始化时,使用固定大小的线程池,线程数量为40。这里使用Executors类的newFixedThreadPool(int threads)方法来创建固定大小的线程池。定义属性serverSocket,其类型为ServerSocket,并在构造方法中将其初始化,申请的服务端口为8088。再定义属性allOut,其类型为HashMap,其中key用于保存用户昵称,value用于保存该客户端的输出流,并在构造方法中初始化以便服务端可以转发信息。

代码如下所示:

package com.tarena.homework;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 服务端应用程序
 */
public class Server {
	// 服务端Socket
	private ServerSocket serverSocket;
	// 所有客户端输出流,key为用户的昵称,value为该用户的输出流
	private Map<String,PrintWriter> allOut;
	// 线程池
	private ExecutorService threadPool;
	/**
	 * 构造方法,用于初始化
	 */
	public Server() {
		try {
			serverSocket = new ServerSocket(8088);			
			allOut = new HashMap<String,PrintWriter>();			
			threadPool = Executors.newFixedThreadPool(40);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

步骤七:为 Server 类定义 addOut()和removeOut()方法

定义 addOut()方法,该方法向Server的属性allOut集合中添加输出流,并使用synchronized关键字修饰,使该方法变为同步方法。

再定义removeOut()方法,该方法从Server的属性allOut集合中删除输出流,并使用synchronized关键字修饰,使该方法变为同步方法。

代码如下所示:

public class Server {
	//其他代码,略

	/**
	 * 将输出流存入共享集合,与下面两个方法互斥,保证同步安全
	 * @param out
	 */
#cold_bold	private synchronized void addOut(String nickName,PrintWriter out){
#cold_bold		allOut.put(nickName,out);
#cold_bold	}
	/**
	 * 将给定输出流从共享集合删除
	 * @param out
	 */
#cold_bold	private synchronized void removeOut(String nickName){
#cold_bold		allOut.remove(nickName);
#cold_bold	}
}

步骤八:为 Server 类定义sendMessage()方法

定义sendMessage()方法,该方法用于遍历Server的属性allOut集合元素,将信息写入每一个输出流来完成广播消息的功能,并使用synchronized关键字修饰,使该方法变为同步方法。代码如下所示:

public class Server {
	//其他代码,略

	/**
	 * 将消息转发给所有客户端
	 * @param message
	 */
#cold_bold	private synchronized void sendMessage(String message){
#cold_bold		for(PrintWriter o : allOut.values()){
#cold_bold			o.println(message);
#cold_bold		}
#cold_bold	}
}

步骤九:为 Server 类定义sendMessageToOne() 方法

定义sendMessageToOne()方法,该方法用于将消息发送给指定昵称的客户端来实现私聊功能。代码如下所示:

public class Server {
	//其他代码,略

	/**
	 * 将消息发送给指定昵称的客户端
	 * @param nickName
	 * @param message
	 */
#cold_bold	private synchronized void sendMessageToOne(String nickName,String message){
#cold_bold		PrintWriter out = allOut.get(nickName);
#cold_bold		if(out!=null){
#cold_bold			out.println(message);
#cold_bold		}
#cold_bold	}
}

步骤十:创建内部类

创建 Server的内部类ClientHandler,在内部类中定义run()方法。在run()方法中,读取用户昵称以发送用户上线信息,并进行消息转发,其中先判断是否为私聊信息,若是则调用发送私聊信息的方法,否则向所有客户端广播消息 。代码如下所示:

	/**
	 * 线程体,用于并发处理不同客户端的交互
	 */
	private class ClientHandler implements Runnable {
		// 该线程用于处理的客户端
		private Socket socket;
		// 开客户端的昵称
		private String nickName;

		public ClientHandler(Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			PrintWriter pw = null;
			try {
				//将客户端的输出流存入共享集合,以便广播消息
				OutputStream out = socket.getOutputStream();
				OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
				pw = new PrintWriter(osw,true);
				/*
				 * 将用户信息存入共享集合
				 * 需要同步
				 */
				//先获取该用户昵称
				nickName = getNickName();
				addOut(nickName,pw);
				Thread.sleep(100);
				/*
				 * 通知所有用户该用户已上线
				 */
				sendMessage(nickName+"上线了");
				
				InputStream in = socket.getInputStream();
				InputStreamReader isr = new InputStreamReader(in, "UTF-8");
				BufferedReader br = new BufferedReader(isr);
				
				String message = null;
				// 循环读取客户端发送的信息
				while ((message = br.readLine())!=null) {
					//首先查看是不是私聊
					if(message.startsWith("\\")){
						/*
						 * 私聊格式:\昵称:内容
						 */
						//找到:的位置
						int index = message.indexOf(":");
						if(index>=0){
							//截取昵称
							String name = message.substring(1,index);
							//截取内容
							String info = message.substring(
												     index+1,message.length()
												 );
							//拼接内容
							info = nickName+"对你说:"+info;
							//发送私聊信息给指定用户
							sendMessageToOne(name, info);
							//发送完私聊后就不在广播了。
							continue;
						}
					}
					/*
					 * 遍历所有输出流,将该客户端发送的信息转发给所有客户端
					 * 需要同步
					 */
					sendMessage(nickName+"说:"+message);
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				/*
				 * 当客户端断线,要将输出流从共享集合中删除
				 * 需要同步
				 */
				removeOut(nickName);
				/*
				 * 通知所有用户该用户已下线
				 */
				sendMessage(nickName+"下线了");
				System.out.println("当前在线人数:"+allOut.size());
				if (socket != null) {
					try {
						socket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
}

步骤十一:为内部类定义方法getNickName()

为 Server的内部类ClientHandler定义方法getNickName(),用于获取用户的昵称。代码如下所示:

	private class ClientHandler implements Runnable {
		//其他代码,略

		/**
		 * 获取该用户的昵称
		 * @return
		 */
#cold_bold		private String getNickName()throws Exception{
#cold_bold			try {
#cold_bold				//获取该用户的输出流
#cold_bold				OutputStream out = socket.getOutputStream();
#cold_bold				OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
#cold_bold				PrintWriter pw = new PrintWriter(osw,true);
#cold_bold				//获取该用户的输入流
#cold_bold				InputStream in = socket.getInputStream();
#cold_bold				InputStreamReader isr = new InputStreamReader(in, "UTF-8");
#cold_bold				BufferedReader br = new BufferedReader(isr);
#cold_bold				//读取客户端发送过来的昵称
#cold_bold				String nickName = br.readLine();
#cold_bold				while(true){
#cold_bold					//若昵称为空发送失败代码
#cold_bold					if(nickName.trim().equals("")){
#cold_bold						pw.println("FAIL");
#cold_bold					}
#cold_bold					//若昵称已经存在发送失败代码
#cold_bold					if(allOut.containsKey(nickName)){
#cold_bold						pw.println("FAIL");
#cold_bold					//若成功,发送成功代码,并返回昵称
#cold_bold					}else{
#cold_bold						pw.println("OK");
#cold_bold						return nickName;
#cold_bold					}
#cold_bold					//若改昵称被占用,等待用户再次输入昵称
#cold_bold					nickName = br.readLine();
#cold_bold				}
#cold_bold			} catch (Exception e) {
#cold_bold				throw e;
#cold_bold			}			
#cold_bold		}
	}

步骤十二:为 Server 类创建 start()方法

为 Server 类创建 start()方法。在该方法中,循环监听8088端口,等待客户端的连接,一旦一个客户端连接后,向线程池申请一个线程来完成针对该客户端的交互。代码如下所示:

public class Server {
	//其他代码,略

	/**
	 * 服务端开启方法
	 */
#cold_bold	public void start() {
#cold_bold		try {
#cold_bold			//循环监听客户端的连接
#cold_bold			while(true){
#cold_bold				System.out.println("等待客户端连接...");
#cold_bold				// 监听客户端的连接
#cold_bold				Socket socket = serverSocket.accept();
#cold_bold				System.out.println("客户端已连接!");
#cold_bold				
#cold_bold				//启动一个线程来完成针对该客户端的交互
#cold_bold				ClientHandler handler = new ClientHandler(socket);
#cold_bold				threadPool.execute(handler);
#cold_bold			}			
#cold_bold		} catch (Exception e) {
#cold_bold			e.printStackTrace();
#cold_bold		}
#cold_bold	}
}

步骤十三:为 Server类定义 main() 方法

为 Server 类定义 main() 方法,并在 main() 方法中,创建 Server 对象,调用上一步中所创建的 start() 方法。代码如下所示:

public class Server {
	//其他代码,略

#cold_bold	public static void main(String[] args) {
#cold_bold		Server server = new Server();
#cold_bold		server.start();
#cold_bold	}
}

本案例中,类Server的完整代码如下所示:

package com.tarena.homework;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 服务端应用程序
 */
public class Server {
	// 服务端Socket
	private ServerSocket serverSocket;
	// 所有客户端输出流,key为用户的昵称,value为该用户的输出流
	private Map<String,PrintWriter> allOut;
	// 线程池
	private ExecutorService threadPool;
	/**
	 * 构造方法,用于初始化
	 */
	public Server() {
		try {
			serverSocket = new ServerSocket(8088);			
			allOut = new HashMap<String,PrintWriter>();			
			threadPool = Executors.newFixedThreadPool(40);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 服务端开启方法
	 */
	public void start() {
		try {
			//循环监听客户端的连接
			while(true){
				System.out.println("等待客户端连接...");
				// 监听客户端的连接
				Socket socket = serverSocket.accept();
				System.out.println("客户端已连接!");
				
				//启动一个线程来完成针对该客户端的交互
				ClientHandler handler = new ClientHandler(socket);
				threadPool.execute(handler);
			}			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 将输出流存入共享集合,与下面两个方法互斥,保证同步安全
	 * @param out
	 */
	private synchronized void addOut(String nickName,PrintWriter out){
		allOut.put(nickName,out);
	}
	/**
	 * 将给定输出流从共享集合删除
	 * @param out
	 */
	private synchronized void removeOut(String nickName){
		allOut.remove(nickName);
	}
	/**
	 * 将消息转发给所有客户端
	 * @param message
	 */
	private synchronized void sendMessage(String message){
		for(PrintWriter o : allOut.values()){
			o.println(message);
		}
	}
	/**
	 * 将消息发送给指定昵称的客户端
	 * @param nickName
	 * @param message
	 */
	private synchronized void sendMessageToOne(String nickName,String message){
		PrintWriter out = allOut.get(nickName);
		if(out!=null){
			out.println(message);
		}
	}
	
	public static void main(String[] args) {
		Server server = new Server();
		server.start();
	}

	/**
	 * 线程体,用于并发处理不同客户端的交互
	 */
	private class ClientHandler implements Runnable {
		// 该线程用于处理的客户端
		private Socket socket;
		// 开客户端的昵称
		private String nickName;

		public ClientHandler(Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			PrintWriter pw = null;
			try {
				//将客户端的输出流存入共享集合,以便广播消息
				OutputStream out = socket.getOutputStream();
				OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
				pw = new PrintWriter(osw,true);
				/*
				 * 将用户信息存入共享集合
				 * 需要同步
				 */
				//先获取该用户昵称
				nickName = getNickName();
				addOut(nickName,pw);
				Thread.sleep(100);
				/*
				 * 通知所有用户该用户已上线
				 */
				sendMessage(nickName+"上线了");
				
				InputStream in = socket.getInputStream();
				InputStreamReader isr = new InputStreamReader(in, "UTF-8");
				BufferedReader br = new BufferedReader(isr);
				
				String message = null;
				// 循环读取客户端发送的信息
				while ((message = br.readLine())!=null) {
					//首先查看是不是私聊
					if(message.startsWith("\\")){
						/*
						 * 私聊格式:\昵称:内容
						 */
						//找到:的位置
						int index = message.indexOf(":");
						if(index>=0){
							//截取昵称
							String name = message.substring(1,index);
							//截取内容
							String info = message.substring(index+1,message.length());
							//拼接内容
							info = nickName+"对你说:"+info;
							//发送私聊信息给指定用户
							sendMessageToOne(name, info);
							//发送完私聊后就不在广播了。
							continue;
						}
					}
					/*
					 * 遍历所有输出流,将该客户端发送的信息转发给所有客户端
					 * 需要同步
					 */
					sendMessage(nickName+"说:"+message);
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				/*
				 * 当客户端断线,要将输出流从共享集合中删除
				 * 需要同步
				 */
				removeOut(nickName);
				/*
				 * 通知所有用户该用户已下线
				 */
				sendMessage(nickName+"下线了");
				System.out.println("当前在线人数:"+allOut.size());
				if (socket != null) {
					try {
						socket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		/**
		 * 获取该用户的昵称
		 * @return
		 */
		private String getNickName()throws Exception{
			try {
				//获取该用户的输出流
				OutputStream out = socket.getOutputStream();
				OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
				PrintWriter pw = new PrintWriter(osw,true);
				//获取该用户的输入流
				InputStream in = socket.getInputStream();
				InputStreamReader isr = new InputStreamReader(in, "UTF-8");
				BufferedReader br = new BufferedReader(isr);
				//读取客户端发送过来的昵称
				String nickName = br.readLine();
				while(true){
					//若昵称为空发送失败代码
					if(nickName.trim().equals("")){
						pw.println("FAIL");
					}
					//若昵称已经存在发送失败代码
					if(allOut.containsKey(nickName)){
						pw.println("FAIL");
					//若成功,发送成功代码,并返回昵称
					}else{
						pw.println("OK");
						return nickName;
					}
					//若改昵称被占用,等待用户再次输入昵称
					nickName = br.readLine();
				}
			} catch (Exception e) {
				throw e;
			}			
		}
	}
}

本案例中,类Client的完整代码如下所示:

package com.tarena.homework;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端应用程序
 */
public class Client {
	//客户端Socket
	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
	public Client(){
		try {
			socket = new Socket("localhost",8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 客户端工作方法
	 */
	public void start(){
		try {
			//创建Scanner读取用户输入内容
			Scanner scanner = new Scanner(System.in);
			//首先输入昵称
			inputNickName(scanner);
			
			//将接收服务端信息的线程启动
			ServerHander handler = new ServerHander();
			Thread t = new Thread(handler);
			t.setDaemon(true);
			t.start();
			
			OutputStream out = socket.getOutputStream();			
			OutputStreamWriter osw = new OutputStreamWriter(out,"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) {
		Client client = new Client();
		client.start();
	}
	
	/**
	 * 输入昵称
	 */
	private void inputNickName(Scanner scanner)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 = scanner.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("昵称已被占用,请更换。");
				}else{
					System.out.println("你好!"+nickName+",开始聊天吧!");
					break;
				}
			}
		}
	}
	
	/**
	 * 该线程用于接收服务端发送过来的信息
	 */
	private class ServerHander implements Runnable{
		@Override
		public void run() {
			try {
				InputStream in = socket.getInputStream();
				InputStreamReader isr = new InputStreamReader(in, "UTF-8");
				BufferedReader br = new BufferedReader(isr);
				while(true){
					System.out.println(br.readLine());
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}