服务器端的实现

发表时间:2017-05-09 14:09:14 浏览量( 16 ) 留言数( 0 )

我们还是先实现服务器端的功能。你可以先参考前面的描述的界面,先把界面设计好,由于比较简单,这里我就不讲解了。

一、实现服务器对应每个客户端的线程类。

在com.dao包中新建一个Server2ClientThread类,该类是服务器端类的核心功能,其实现思路与上一天所讲的差不多,也是每一个客户端就对应一个线程来维护,不同的是,服务器需要记录所有的在线用户和信息,而这些信息是所有的用户共享的,所以这里我们把这些变量定义为静态变量(static),这点要注意。实现代码如下:

/**
 * 服务器对应每个客户端的线程类
 * 
 * @author Administrator
 * 
 */
public class Server2ClientThread extends Thread {
	private Socket socket;// 对应客户端
	private String name;// 客户姓名

	private PrintWriter out;// 输出流
	private BufferedReader in;// 输入流
	// 所有的客户端,这个变量所有的线程共享,所以使用静态变量的方式。
	private static List<Socket> sockets = new ArrayList<Socket>();
	// 记录所有的人的信息
	public static List<String> messages = new ArrayList<String>();
	// 记录所有额人的名字
	public static List<String> names = new ArrayList<String>();

	public Server2ClientThread(Socket socket) {
		this.socket = socket;

		sockets.add(socket);// 记录所有的socket

		try {
			out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
					socket.getOutputStream())), true);
			in = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		// 接收客户的用户名
		try {
			name = in.readLine();

			names.add(name);

		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		// 负责与客户通讯
		out.println(name + ",欢迎你登陆:");
		out.flush();
		try {
			while (true) {
				String message = in.readLine();

				if (message == null) {
					break;
				}

				messages.add(name + "说:" + message + "   ");

				// 转发给所有的socket
				for (Socket so : sockets) {
					PrintWriter soout = new PrintWriter(new BufferedWriter(
							new OutputStreamWriter(so.getOutputStream())), true);

					soout.println(name + "说:" + message);

					soout.flush();
				}

			}
			sockets.remove(socket);
			names.remove(name);
		} catch (IOException e) {
			// 如果客户端发生异常就从列表删除
			sockets.remove(socket);
			names.remove(name);
			// e.printStackTrace();
		}

	}

}

二、实现服务器端的功能类。

我们把启动服务和关闭服务功能封装成为两个方法,在com.dao中新建类ServerStart。实现代码如下:

/**
 * 启动服务器的监听 
 * @author Administrator
 *
 */
public class ServerStart {
	
	private ServerSocket server;
	/**
	 * 启动
	 * @param port
	 */
	public void start(int port){
		try {
			server = new ServerSocket(port);
            System.out.println("服务器端运行了:端口是:"+port);
			int i = 0;
			while (true) {
				// 不断的等待客户的连接,每一次连接都产生一个新的socket
				Socket socket = server.accept();
				i++;
				
				System.out.println("有客户端连接了。");
				
				// 启动一个独立的线程负责与之通讯
				Server2ClientThread clientThread = new Server2ClientThread(
						socket);
				
				//启动线程
				clientThread.start();

			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
		}finally{
			try {
				server.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public void close(){
		System.out.println("服务器关闭");
		try {
			server.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
		}
	}

}

三、绑定按钮事件。

接下我们绑定前台的启动按钮的单击事件,由于程序运行ServerSocket的accept()时会产生阻塞程序的效果,为了不影响界面程序的正常运行,所以我们需要把Socket服务的启动放入另外一个线程中,异步运行。

用户点击了启动后,同时会不断的刷新在线用户和最新聊天信息,所以这里我们有定义一个线程处理这个事情,每个500毫秒就读取保存在Server2ClientThread中的静态变量(保存了在线用户和最新聊天信息),以达到刷新界面的效果。

用户点击了启动后,按钮将会变成关闭。用户在此点击按钮就表示关闭服务。

实现代码如下:

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {

		if (jButton1.getText().equals("启动")) {
			// 获得用户输入的端口
			final int port = Integer.parseInt(jTextField1.getText());

			// 启动另外一个线程监听客户端
			new Thread() {

				public void run() {
					serverStart = new ServerStart();
					serverStart.start(port);
				}

			}.start();

			// 启动一个线程不断更新用户列表的信息和聊天信息
			new Thread() {

				public void run() {
					while(true){
						
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						
						StringBuffer usersb=new StringBuffer();
						for(String name:Server2ClientThread.names){
							usersb.append(name+"\r\n");
						}
						
						StringBuffer messagesb=new StringBuffer();
						for(String message:Server2ClientThread.messages){
							messagesb.append(message+"\r\n");
						}
						
						userjTextArea.setText(usersb.toString());
						messagejTextArea.setText(messagesb.toString());
					}
				}

			}.start();

			jButton1.setText("关闭");
		} else {
			// 关闭服务
			serverStart.close();
			jButton1.setText("启动");
		}

	}


好,我们的服务器端功能就完成了。