zoukankan      html  css  js  c++  java
  • 简单聊天室(java版)

    这是本人从其他地方学习到的关于聊天室的一个模本,我从中截取了一部分关于客户端和服务端通信的Socket的内容。希望对大家对socket有个了解,我写的这些代码可以实现两人或多人在多台电脑上实现简单的对话。在运行时要先运行server(服务端),再运行client(客户端)。Windows获取自己电脑的ip需要再DOS(命令窗口)界面输入ipconfig或者再网络和共享中心已连接的网络查看详细信息。具体的代码如下

    客户端代码


     package talkRoom;
    
    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 ylg
     *
     */
    public class Client {
    	//客户端用于与服务端通信的socket
    	private Socket socket;
    	/**
    	 *初始化客户端相关内容
    	 */
    	public Client(){
    		try {
    			/**
    			 * 实例化socket的过程就是连接的过程通常我们要传入两个参数
    			 * 1:字符串,服务器的IP地址
    			 * 2:整数,服务器端申请的端口号
    			 * (serversocket创建时申请的端口号:8088)
    			 */
    			System.out.println("尝试连接");
    			//此处的localhost可以改为运行服务端的那台电脑的的ip地址这样就可以连在一起聊天了
    			//localhost指的是本机的ip
    			socket =new Socket("localhost", 8088);
    			System.out.println("连接成功");
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	/**
    	 * 客户端用于交互的方法
    	 */
    	public void start(){
    		try {
    			/**
    			 * 创建一个线程,用于读取服务器发过来的信息
    			 */
    			Runnable hander=new GetMessageFromServerHandler();
    			Thread t=new Thread(hander);
    			t.start();
    			/**
    			 * 客户端想向服务发送消息,通过socket花去输出流之后写出数据即可
    			 */
    			OutputStream out=socket.getOutputStream();
    			/**
    			 * 向服务器发送字符串,我们可以将字节流转换为缓冲字符流输出PrintWrint
    			 * 
    			 */
    			OutputStreamWriter osw=new OutputStreamWriter(out,"UTF-8");
    			/**
    			 * 发送一个字符串就应当立即写出,所以要自动行刷新
    			 */
    			PrintWriter pw=new PrintWriter(osw,true);
    			/**
    			 * 创建scanner,将控制台输入的字符串通过pw发送给服务器
    			 */
    			String message=null;
    			Scanner scanner=new Scanner(System.in);
    			while(true){
    				message=scanner.nextLine();
    				pw.println(message);
    			}
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	public static void main(String[] args) {
    		  Client client=new Client();
    		  client.start();
    	}
    	/**
    	 * 该线程的作用是让客户端可以读取服务器发送过来的信息
    	 * @author ylg
    	 *
    	 */
    	class GetMessageFromServerHandler implements Runnable{
    		/**
    		 * 通过socket获取输入流,在转换为缓冲字符输入流
    		 * 最后通过循环都读取服务端发送的每一行信息
    		 */
    		
    		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) {
    				
    			}
    		}
    		
    	}
    }
    
    

    服务端代码(请先运行服务端)

    package talkRoom;
    
    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.ArrayList;
    import java.util.List;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 服务端
     * @author ylg
     *
     */
    public class Server {
    	/**
    	 * 用于与客户端连接的ServerSOocket
    	 */
    	private ServerSocket server;
    	/**
    	 * 存放所有客户端的输入流,用于广播信息
    	 */
    	private List<PrintWriter> allOut;
    	/**
    	 * 线程池,用于控制服务端线程数量,并重用线程
    	 */
    	private ExecutorService threadPool;
    	/**
    	 * 构造方法,用于初始化服务器相关内容
    	 * 
    	 */
    	public Server(){
    		try {
    			//初始化ServerSocket
    			/**
    			 * 初始化时要求我们传入一个整数,这个整数表示端口号,客户端就是
    			 * 通过这个端口号连接到服务端的
    			 */
    			server=new ServerSocket(8088);
    			/**
    			 * 初始化存放所有客户端输出流的家集合
    			 */
    			allOut =new ArrayList<PrintWriter>();
    			//初始化线程池
    			threadPool=Executors.newFixedThreadPool(50);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	/**
    	 * 将给定的输出流存入共享集合中
    	 * @param out
    	 */
    	private synchronized void addOut(PrintWriter out){
    		allOut.add(out);
    	}
    	/**
    	 * 从共享集合中删除给定的删除流
    	 * @param out
    	 */
    	private synchronized void removeOut(PrintWriter out){
    		allOut.remove(out);
    	}
    	
    	  //还要遍历方法,并且三个操作集合的方法互斥
    	 /**
    	  * 遍历所有的输出流将给定的字符串发送给所有客户端
    	  * @param message 服务器接收到的消息
    	  */
    	private synchronized void sendMsgToAllClient(String message){
    		for(PrintWriter pw:allOut){
    			pw.println(message);
    		}
    	}
    	/**
    	 * 服务端开始工作的方法
    	 */
    	public void start(){
    		try {
    			/**
    			 * socket accept()
    			 * 该方法是一个阻塞方法,用于等待客户端的连接
    			 * 一旦一个客户端连接上,该方法就会返回与该客户端通信socket
    			 */
    			System.out.println("等待客户端的连接...");
    			/**
    			 * 死循环的目的是一直监听不同客户端的连接
    			 */
    			while(true){
    				Socket socket=server.accept();
    				System.out.println("一个客户端连接上了...");
    				/**
    				 * 当一个客户端连接后,启动一个线程,将该客户端的socket传入,
    				 * 是该线程与客户端通信
    				 */
    				Runnable clientHandler=new ClientHandler(socket);
    //				Thread t=new Thread(clientHandler);
    //				t.start();
    				threadPool.execute(clientHandler);
    			} 
    			
    			
    		} catch (Exception e) {
    		}finally {
    			
    		}
    	}
    	public static void main(String[] args) {
    		Server server =new Server();
    		server.start();
    	}
    	/**
    	 * 该线程的作用是与给定的客户端Socket进行通信
    	 * @author ylg
    	 *
    	 */
    	class ClientHandler implements Runnable{
    		/**
    		 * 当前线程用于交流的指定客户端的Socket
    		 */
    		private Socket socket;
    		/**
    		 * 创建线程体时将交互的Socket传入
    		 * @param socket
    		 */
    		public ClientHandler(Socket socket){
    			this.socket=socket;
    		}
    		/**
    		 * 定义在try外面是因为finally中要引用
    		 */
    		PrintWriter pw=null;
    		public void run(){
    			try {
    				/**
    				 * 通过socket获取输出流,用于将信息发送给客户端
    				 */
    				OutputStream out=socket.getOutputStream();
    				OutputStreamWriter osw=new OutputStreamWriter(out, "utf-8");
    				pw=new PrintWriter(osw,true);
    				/**
    				 * 将该客户端的输出流存入共享集合
    				 */
    				addOut(pw);
    				/**
    				 * 通过连接上的客户端的socket获取输入流来读取客户端发送过来的信息
    				 */
    				InputStream in=socket.getInputStream();
    				InputStreamReader isr=new InputStreamReader(in,"UTF-8");
    				/**
    				 * 包装为缓冲流字符输入流,可以按行读取字符串
    				 */
    				BufferedReader br=new BufferedReader(isr);
    				String message=null;
    				while((message=br.readLine())!=null){
    					//将当前的发送的消息广播给所有客户端
    					sendMsgToAllClient(message);
    				/*	//System.out.println("客户端说: "+message);	
    					//将读取到的信息发送给客户端
    					pw.println(message);*/
    					//在服务端上显示
    					System.out.println(message);
    				}
    			} catch (Exception e) {
    			}finally{
    				/**
    				 * linux客户端若断开连接,服务端会读取到null
    				 * windows的客户端断开连接,服务端会抛出异常
    				 * 所以finally是我们最后处理的最佳地点
    				 */
    				System.out.println("客户端下线");
    				/**
    				 * 当客户端断开后,将其输出流从共享集合中删除
    				 */
    				removeOut(pw);
    				/**
    				 * 输出在线人数
    				 */
    				System.out.println("当前在线人数"+allOut.size());
    				/**
    				 * 不同分别关闭输入流与输出流
    				 * 关闭socket即可,因为这两个流都是从socket获取的,就好比打电话
    				 * 我们最终挂断电话就自然断开了麦克风和听筒一样
    				 */
    				try {
    					socket.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    }
    
    
    
    
    
    
    

    看不懂的可以在下方留言

  • 相关阅读:
    vue cli
    vue element-ui
    vue mint-ui
    vue富文本编辑器
    vue-单文件组件相关
    axios
    vue Router
    css 行内元素和块级元素的一些注意事项
    golang协程和变量
    Internet地址介绍
  • 原文地址:https://www.cnblogs.com/yangliguo/p/7398998.html
Copyright © 2011-2022 走看看