Java之TCP与Socket应用实例:实现简单的聊天室(群聊)功能

首先是服务端:Server.java

package chat;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class Server {
	/*
	 * 运行在服务端的ServerSocket主要负责:
	 * 1.向系统申请服务端口。客户端就是通过这个端口与之连接的。
	 * 
	 * 2.监听申请的服务端口。当一个客户端通过该端口尝试建立连接时
	 * ServerSocket会在服务端创建一个Socket与客户端建立连接。
	 */
	private ServerSocket server;
	/*
	 * 保存所有客户端输出流的集合。
	 */
	private List<PrintWriter> allOut;
	/*
	 * 用于初始化服务端
	 */
	public Server() throws Exception{
		/*
		 * 初始化的同时申请服务端口。
		 */
		server = new ServerSocket(8088);
		allOut = new ArrayList<PrintWriter>();
	}
	/**
	 * 将给定的输出流存入共享集合。
	 * @param out
	 */
	private void addOut(PrintWriter out){
		allOut.add(out);
	}
	/**
	 * 将给定的输出流从共享集合中删除。
	 * @param out
	 */
	private void removeOut(PrintWriter out){
		allOut.remove(out);
	}
	/**
	 * 将给定的消息发送给所有客户端。
	 * @param message
	 */
	private void sendMessage(String message){
		for(PrintWriter out:allOut){
			out.println(message);
		}
	}
	
	/*
	 * 服务端开始工作的方法
	 */
	public void start(){
		try{
			/*
			 * ServerSocket的accept方法是一个阻塞方法
			 * 作用是监听服务端口,直到一个客户端连接并创建一
			 * 个Socket,使用该Socket即可与刚连接的客户
			 * 端进行交互与数据传输。
			 */
			while(true){
				System.out.println("等待客户端连接...");
				Socket socket = server.accept();
				System.out.println("一个来自火星的客户端成功连接。");
				/*
				 * 启动一个线程,来完成与该客户端的交互。
				 */
				ClientHandler handler = new ClientHandler(socket);
				Thread t = new Thread(handler);
				t.start();
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		try{
			Server server = new Server();
			server.start();
		}catch (Exception e) {
			e.printStackTrace();
			System.out.println("服务端启动失败。");
		}
	}
	/**
	 * 该线程负责处理一个客户端交互。
	 * @author kaixu
	 *
	 */
	class ClientHandler implements Runnable{
		/*
		 * 该线程处理客户端的Socket
		 */
		private Socket socket;
		
		//客户端的地址信息
		private String host;
		//该用户的昵称
		private String nickName;
		
		public ClientHandler(Socket socket){
			this.socket = socket;
			/*
			 * 通过Socket可以获取远端计算机的地址信息。
			 */
			InetAddress address = socket.getInetAddress();
			//解析获取的IP地址信息
			host = address.getHostAddress();
		}
		
		public void run(){
			PrintWriter pw = null;
			try {
				/*
				 * Socket提供的方法:
				 * InputStream  getInputStream
				 * 该方法可以获取一个输入流,从该流读取的数据就是从远端计算机发送过来的。
				 */
				InputStream in = socket.getInputStream();
				InputStreamReader isr = new InputStreamReader(in, "UTF-8");
				BufferedReader br = new BufferedReader(isr);
				//首先读取一行字符串作为昵称。
				nickName = br.readLine();
				System.out.println(nickName+"上线了。");
				/*
				 * 通过Socket创建输出流用于将消息发送给客户端。
				 */
				OutputStream out = socket.getOutputStream();
				OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8");
				pw = new PrintWriter(osw, true);
				/*
				 * 将该客户端的输出流存入到共享集合中。
				 */
				addOut(pw);
				String message = null;
				/*
				 * br.readLine在读取客户端发送过来的消息时
				 * 由于客户端短线,而其操作系统的不同,此处读取
				 * 后的结果不同:
				 * -当Windows的客户端断开时:br.readLine会抛出异常。
				 * -当Linux的客户端断开时:br.readLine会返回null.
				 */
				while((message = br.readLine())!=null){
					//System.out.println(host+"客户端发来消息:"+message);
					//pw.println(host+"说:"+message);
					//广播消息
					sendMessage(nickName+"说:"+message);
				}
			} catch (Exception e) {
			} finally {
				/*
				 * 处理当前客户端断开后的逻辑。
				 */
				//将该客户端的输出流从共享集合中删除。
				removeOut(pw);
				System.out.println(nickName+"下线了。");
				try {
					socket.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}

客户端基于服务端的功能设定而搭建:Client.java

package chat;

import java.io.BufferedReader;
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;

/**
 * 聊天室客户端
 * @author kaixu
 *
 */
public class Client {
	/*
	 * (套接字)
	 * java.net.Socket
	 * 封装了TCP协议,使用它就可以基于TCP协议进行网络通讯。
	 * Socket是运行在客户端的。
	 */
		private Socket socket;
		/*
		 * 构造方法,用于初始化客户端。
		 * 实例化Socket的时候需要传入两个参数:
		 * 1.服务端地址:通过IP地址可以找到服务的那台计算机。
		 * 2.服务端端口:通过端口可以找到服务端计算机上的服务端应用程序。
		 * 实例化Socket的过程就是连接的过程,若远端计算机没有响应则会抛出异常。
		 */
		public Client() throws Exception{
			System.out.println("正在连接服务器...");
			socket = new Socket("localhost", 8088);
			System.out.println("连接服务器成功。");
		}
		/*
		 * 启动客户端的方法。
		 */
		public void start(){
			try{
				Scanner scanner = new Scanner(System.in);
				/*
				 * 先要求用户输入一个昵称。
				 */
				String nickName = null;
				while(true){
					System.out.println("请输入一个名字作为你聊天的昵称:");
					nickName = scanner.nextLine();
					if(nickName.length()>0){
						break;
					}
					System.out.println("昵称无效。请重新输入:");
				}
				System.out.println("欢迎,"+nickName+"。Enjoying Chatting!");
				/*
				 * Socket提供的方法:
				 * OutputStream  getOutputStream
				 * 获取一个字节输出流,通过该流写出的数据会被发送至远端计算机。
				 */
				OutputStream out = socket.getOutputStream();
				OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8");
				PrintWriter pw = new PrintWriter(osw,true);
				//先将昵称发送给服务端。
				pw.println(nickName);
				
				/*
				 * 启动读取服务端发送过来的消息的线程。
				 */
				ServerHandler handler = new ServerHandler();
				Thread t = new Thread(handler);
				t.start();
				while(true){
				/*
				 * 将字符串发送至服务端,设置自动行刷新。
				 */
				pw.println(scanner.nextLine());
				}
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		public static void main(String[] args) {
			try{
				Client client = new Client();
				client.start();
			}catch (Exception e) {
				e.printStackTrace();
				System.out.println("客户端启动失败。");
			}
		}
		/**
		 * 该线程用来读取服务端发送过来的消息。
		 * 同时输出到客户端控制台显示。
		 * @author kaixu
		 *
		 */
		class ServerHandler implements Runnable{
			public void run(){
				try {
					InputStream in = socket.getInputStream();
					InputStreamReader isr = new InputStreamReader(in, "UTF-8");
					BufferedReader br = new BufferedReader(isr);
					String message = null;
					while((message = br.readLine())!=null){
						System.out.println(message);
					}
				} catch (Exception e) {
					
				}
			}
		}
	}

以上。

你可能感兴趣的:(JavaSE)