Java开发基础-网络编程-Socket编程【实现简单的聊天系统】—10

案例说明:

1.掌握Socket编程的基本步骤

2.熟练Socket有关常用API的使用

下面通过简单实例演示:

  • 客户端代码:
/**
 * 聊天室客户端
 * @author Cher_du
 *
 */
public class Client {
	/*
	 * java.net.Socket 套接字
	 * 封装了TCP通讯.使用该类完成与服务端的连接
	 * 并进行相应的通讯.
	 */
	private Socket socket;
	/**
	 * 构造方法用来初始化客户端
	 * @throws Exception 
	 */
	public Client() throws Exception{
		System.out.println("正在连接服务端...");
		try {
			/*
			 * 实例化Socket时需要传入两个参数
			 * 1:服务端的地址
			 * 2:服务端的端口
			 * 
			 * 通过地址找到服务端的计算机,端口
			 * 则找到该计算机上的服务端应用程序.
			 * 
			 * 实例化Socket的过程就是连接服务端
			 * 的过程.连接不成功该构造方法会抛出
			 * 异常.
			 */
			socket = new Socket("localhost",
								8088);
			System.out.println("与服务端建立连接!");
		} catch (Exception e) {
			throw e;
		}	
	}
	
	/**
	 * 客户端的启动方法
	 */
	public void start() {
		try {
			
			/*
			 * 先启动用于接收服务端发送过来的消息的
			 * 线程
			 */
			ServerHandler handler = new ServerHandler();
			Thread t = new Thread(handler);
			t.start();
			
			Scanner scanner = new Scanner(System.in);
			/*
			 * Socket提供了方法:
			 * OutputStream getOutputStream()
			 * 该方法可以获取一个字节输出流,通过
			 * 该输出流写出的数据会发送至远端计算机
			 * 对于客户端这边而言远端就是服务端.
			 */

			OutputStream out = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8");
			PrintWriter pw = new PrintWriter(osw, true);

			String message = null;
			while (true) {
				message = scanner.nextLine();
				pw.println(message);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		try {
			Client client = new Client();
			client.start();
		} catch (Exception e) {
			
			e.printStackTrace();
		}
	}
	
	/**
	 * 该线程负责循环接收服务端发送过来的消息
	 * 并输出到客户端的控制台
	 */
	class ServerHandler implements Runnable{

		@Override
		public void run() {
		   try {
			BufferedReader br = new BufferedReader(
					   new InputStreamReader(
							   socket.getInputStream(),
							   "utf-8"
							   )
					   );
			
			String message = null;
			while((message=br.readLine())!=null){
				System.out.println("服务端:"+message);
			}
		   } catch (Exception e) {
			e.printStackTrace();
		   }
				   
			
		}
		
	}
}
  • 服务端代码:
    /**
     * 聊天室服务端
     * @author Cher_du
     *
     */
    public class Server {
    	
    	/*
    	 * java.net.ServerSocket
    	 * 运行在服务端的ServerSocket有两个作用
    	 * 1.申请服务端口(客户端通过该端口与服务端建立连接)
    	 * 2.监听服务端口,等待客户端连接,一旦客户端连接则
    	 * 创建一个Socket实例用于与该客户端交互.
    	 */
    	private ServerSocket server;
    	
    	/*
    	 *该集合用于保存所以客户端的Socket(数据流) 
    	 */
    	private List<PrintWriter> allOut;
    	
    	public Server() throws Exception{
    		try {
    			allOut  = new ArrayList<PrintWriter>();
    			/*
    			 * 实例化ServerSocket需要指定
    			 * 服务端口.该端口不能与当前操作系统
    			 * 其他程序申请的端口冲突,否则会抛出
    			 * 端口被占用异常
    			 */
    			server = new ServerSocket(8088);
    		} catch (Exception e) {
    			throw e;
    		}
    	}
    	
    	/**
    	 * 将给定的输出流存入共享集合
    	 */
    	private synchronized void addOut(PrintWriter out){
    		allOut.add(out);
    	}
    	/**
    	 * 将给定的输出流从共享集合中删除
    	 */
    	private synchronized void removeOut(PrintWriter out){
    		allOut.remove(out);
    	}
    	/**
    	 * 将给定的消发送给所以的客户端
    	 */
    	private synchronized void sendMessage(String message){
    		for(PrintWriter out :allOut){
    			out.println(message);
    		}
    	}
    	
    	public void start() {
    		try {
    
    			/*
    			 * ServerSocket提供一个方法:
    			 * Socket  accept()
    			 * 该方法是一个阻塞方法,作用是监听
    			 * ServerSocket开启的服务端口,
    			 * 直到一个客户端通过该端口连接,该方法
    			 * 才会解除阻塞,并返回一个Socket实例
    			 * 通过该Socket实例与刚刚建立连接的
    			 * 客户端进行通信.
    			 */
    			while (true) {
    				System.out.println("等待客户端连接...");
    				Socket socket = server.accept();
    				System.out.println("一个客户端连接了!");
    				
    				/*
    				 *当一个客户端连接后,启动一个线程来处理
    				 *与该客户端的交互工作. 
    				 */
    				ClientHandlerWriter clientHandler = new ClientHandlerWriter(socket);
    				Thread t = new Thread(clientHandler);
    				t.start();
    				
    				ClientHandlerRead clientHandler2 = new ClientHandlerRead(socket);
    				Thread t2 = new Thread(clientHandler2);
    				t2.start();
    				
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	public static void main(String[] args) {
    		
    		try {
    			Server server = new Server();
    			server.start();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}	
    	}
    	
    	/**
    	 * 该线程负责与指定的客户端进行交互
    	 * @author Cher_du
    	 *
    	 */
    	class ClientHandlerWriter implements Runnable{
    		
    		/**
    		 * 当前线程负责与指定客户端交互的Socket
    		 */
    		private Socket socket;
    		//客户端的地址信息
    		private String host;
    
    		
    		public ClientHandlerWriter(Socket socket){
    			this.socket = socket;
    			/*
    			 * 通过socket获取远程计算机地址信息
    			 * 对于服务端而言,远程计算机就是客户端
    			 */
    			InetAddress address = socket.getInetAddress();
    			//获取远端计算机的IP
    			host = address.getHostAddress();
    		}
    
    		@Override
    		public void run() {
    			PrintWriter pw = null;
    			try {
    				/*
    				 *通过Socket创建输出流,用于将消息
    				 *发送给客户端 
    				 */
    				OutputStream out = socket.getOutputStream();
    				OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8");
    				pw = new PrintWriter(osw,true);
    				
    				/*
    				 * br.readLine读取客户端发送过来的一行字符串
    				 * 时,客户端断开连接时,由于客户端所在系统不同,
    				 * 这里readLine方法的执行结果也不相同:
    				 * 当windows的客户端断开连接时,readLine方法
    				 * 会直接抛出异常
    				 * 当Linux的客户端断开连接时,readLine方法会
    				 * 返回null.
    				 * 
    				 */
    //				while((message = br.readLine())!=null){
    //					System.out.println(host+"说:"+message);
    //					//pw.println(host+"说:"+message);
    //					//转发给所有客户端
    //					sendMessage(host+"说:"+message);
    //				}
    				
    				Scanner scanner = new Scanner(System.in);
    				String message = null;
    				while (true) {
    					message = scanner.nextLine();
    					pw.println(message);
    				}
    
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    class ClientHandlerRead implements Runnable{
    		
    		/**
    		 * 当前线程负责与指定客户端交互的Socket
    		 */
    		private Socket socket;
    		//客户端的地址信息
    		private String host;
    
    		
    		public ClientHandlerRead(Socket socket){
    			this.socket = socket;
    			/*
    			 * 通过socket获取远程计算机地址信息
    			 * 对于服务端而言,远程计算机就是客户端
    			 */
    			InetAddress address = socket.getInetAddress();
    			//获取远端计算机的IP
    			host = address.getHostAddress();
    		}
    
    		@Override
    		public void run() {
    			PrintWriter pw = null;
    			try {
    				sendMessage(host+"上线了!");
    				/*
    				 *通过Socket创建输出流,用于将消息
    				 *发送给客户端 
    				 */
    				OutputStream out = socket.getOutputStream();
    				OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8");
    				pw = new PrintWriter(osw,true);
    				
    				//将该客户端的输出流存入共享集合
    				addOut(pw);
    				
    			
    				
    				InputStream in  = socket.getInputStream();
    				InputStreamReader isr = new InputStreamReader(in,"UTF-8");
    				BufferedReader br = new BufferedReader(isr);
    				
    				String message = null;
    				/*
    				 * br.readLine读取客户端发送过来的一行字符串
    				 * 时,客户端断开连接时,由于客户端所在系统不同,
    				 * 这里readLine方法的执行结果也不相同:
    				 * 当windows的客户端断开连接时,readLine方法
    				 * 会直接抛出异常
    				 * 当Linux的客户端断开连接时,readLine方法会
    				 * 返回null.
    				 * 
    				 */
    				while((message = br.readLine())!=null){
    					System.out.println(host+"说:"+message);
    					//pw.println(host+"说:"+message);
    					//转发给所有客户端
    					//sendMessage(host+"说:"+message);
    				}
    				
    			} catch (Exception e) {
    				e.printStackTrace();
    			}finally{
    				//客户端与服务端断开连接.
    				
    				//客户端下线后从共享集合中删除输出流
    				removeOut(pw);
    				sendMessage(host+"下线了!");
    				
    				try {
    					socket.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    }

  • 启动服务端再启动客户端后测试一下:
  • 客户端录入发送给服务端的信息,即可在服务端看到,同样在服务端录入信息也可发送到客户端,从而实现简单的聊天系统。

猜你喜欢

转载自blog.csdn.net/coder_boy_/article/details/80467827