• Java利用TCP编程实现简单聊天室


    前言:

    本文是我在学习尚学堂JAVA300集第二季网络编程部分仿照视频内容实现而成
    具体可以去尚学堂官网观看视频学习

    一、实现思路

       实现聊天室的最核心部分就是JAVA的TCP网络编程。
      TCP 传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议 ,在Java中我们利用ServerSocket类来建立服务端,利用Socket类来建立客户端。这里要注意,在TCP中,Socket实际上是指
    Server端与Client端建立的一个双向的流通道,我们利用这个流通道实现数据的传输。
      我们将聊天室分为两部分,客户端和服务端.
      对于客户端,主要有两个功能,信息的收与信息的发。因为这两个功能需要并行进行,并且要不停的进行收和发,所以将这两个功能抽象成两个实现Runnable接口的(Send,Recevice)类,每次客户端Client启动,建立一个Socket,并利用这个Socket建立一个收线程(Recevice类)和发线程(Send)类

      对于服务器端,因为我们要不停的监听是否有新的连接进来,所有要通过一个循环不停的接收,这里的接收函数是阻塞式的。因为我们要对所有的连接进行同时处理,所有我们将新得到连接抽象成一个实现Runnable接口的User类,利用多线程进程对每一个连接并行处理。为了方便多个连接之间的交互,我们将User类作为Server类的一个内部类使用。

    二、实现过程

    1.客户端

    Client类:

    package top.dlkkill.tcp.chat;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    
    public class Client {
    	
    	public static void main(String[] args) throws IOException {
    		//从控制台获得输入
    		BufferedReader console=new BufferedReader(new InputStreamReader(System.in));
    		System.out.println("请输入您的名字:");
    		String name=console.readLine();
    		if(name.equals(""))
    			return;
    		Socket client=null;
    		try {
    			//建立新连接,注意这里创建好就已经连接上了,要保证服务端已经开启
    			client=new Socket("localhost", 8888);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			//e.printStackTrace();
    			System.err.println(name+"连接失败");
    		}
    		//两条线路,一条负责发,一条负责收
    		new Thread(new Send(client,name)).start();
    		new Thread(new Recevice(client)).start();
    	}
    	
    }
    
    

    Send类

    package top.dlkkill.tcp.chat;
    
    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    
    public class Send implements Runnable{
    	
    	//负责写出,将信息传输到服务端
    	private DataOutputStream os;
    	//负责读取控制台输入
    	private BufferedReader console;
    	//线程标识
    	private boolean isRun=true;
    	
    	//通过死循环保证线程一直进行
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		while(isRun) {
    			send(getMsgFromConsole());
    		}
    	}
    	
    	//构造方法,利用Socket类获得流
    	public Send(Socket client,String name) {
    		// TODO Auto-generated constructor stub
    		try {
    			os=new DataOutputStream(client.getOutputStream());
    			console=new BufferedReader(new InputStreamReader(System.in));
    			send(name);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			isRun=false;
    			CloseUtil.closeAll(os,console);
    		}
    	}
    	
    	//发送函数
    	public void send(String msg) {
    		
    		try {
    			if(msg!=null&&!msg.equals("")) {
    				os.writeUTF(msg);
    				os.flush();
    			}
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			isRun=false;
    			CloseUtil.closeAll(os,console);
    		}
    	}
    	
    	//从控制台不断读取信息
    	public String getMsgFromConsole() {
    		String msg=null;
    		try {
    			msg=console.readLine();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			isRun=false;
    			CloseUtil.closeAll(os,console);
    		}
    		return msg;
    	}
    }
    
    

    Recevice类

    package top.dlkkill.tcp.chat;
    
    import java.io.DataInputStream;
    import java.io.IOException;
    import java.net.Socket;
    
    public class Recevice implements Runnable{
    	
    	    //负责读取服务端发送过来的信息
    		private DataInputStream is;
    		//线程标识
    		private boolean isRun=true;
    		
    		
    		@Override
    		public void run() {
    			// TODO Auto-generated method stub
    			while(isRun){
    				recevice();
    			}
    		}
    		
    		public Recevice(Socket client) {
    			// TODO Auto-generated constructor stub
    			try {
    				is=new DataInputStream(client.getInputStream());
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    				CloseUtil.closeAll(is);
    				isRun=false;
    			}
    		}
    		
    		public void recevice() {
    			String msg=null;
    			try {
    				msg=is.readUTF();
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    				CloseUtil.closeAll(is);
    				isRun=false;
    			}
    			System.out.println(msg);
    		}
    		
    }
    
    

    2.服务端

    Server类(内部有一个User类)

    package top.dlkkill.tcp.chat;
    
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.HashSet;
    
    public class Server {
    	
    	//保存所有的连接
    	private HashSet<User> users;
    	//线程标识
    	private boolean run=true;
    	
    	public static void main(String[] args) {
    
    		//创建一个Server类
    		Server server=new Server();
    		try {
    			//启动服务器
    			server.start();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	public Server() {
    		run=true;
    		users=new HashSet<User>();
    	}
    	
    	public void start() throws IOException {
    		//创建一个服务器端
    		ServerSocket server=new ServerSocket(8888);
    		while(run) {
    			//不断接收一个新的连接,利用新连接创建一个User线程进行处理
    			Socket client= server.accept();
    			User user=new User(client);
    			users.add(user);
    			new Thread(user).start();
    		}
    	}
    	
    	public void stop() {
    		run=false;
    	}
    	
    	
    	//代表一个连接,负责信息的接收与转发
    	private class User implements Runnable{
    		
    		//记录连接用户的名字
    		private String name;
    		
    		public String getName() {
    			return name;
    		}
    		//负责接收
    		private DataInputStream is;
    		//负责发送
    		private DataOutputStream os;
    		//线程标识
    		private boolean isRun=true;
    		
    		public User(Socket client) {
    
    			try {
    				is=new DataInputStream(client.getInputStream());
    				os=new DataOutputStream(client.getOutputStream());
    				isRun=true;
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    				isRun=false;
    				CloseUtil.closeAll(is,os);
    			}
    			try {
    				 name=is.readUTF();
    				 this.sendOther(new String("欢迎"+name+"进入聊天室"),true);
    				 this.send(new String("系统:您已经进入了聊天室"));
    			}catch (Exception e) {
    				// TODO: handle exception
    			}
    		}
    		
    		@Override
    		public void run() {
    			// TODO Auto-generated method stub
    			while(isRun) {
    				this.sendOther(this.revice(),false);
    			}
    		}
    		
    		//接收信息
    		public String revice() {
    			String msg = null;
    			try {
    				msg=is.readUTF();
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			return msg;
    		}
    		
    		//发送信息
    		public void send(String msg) {
    			try {
    				os.writeUTF(msg);
    				os.flush();
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		
    		//将信息转发给其他用户,同时实现了私聊功能和系统信息功能
    		//因为是内部类,所以可以访问Server类中的private HashSet<User> users
    		//@XX:代表向XX发送私聊信息
    		public void sendOther(String msg,boolean admin) {
    			if(msg.startsWith("@")&&msg.contains(":")) {
    				String toname=msg.substring(1, msg.indexOf(":"));
    				String newmsg=msg.substring(msg.indexOf(":")+1);
    				for (User user : users) {
    					if(user.getName().equals(toname)) {
    						user.send(this.name+"悄悄的对你说:"+newmsg);
    					}
    				}
    			}else {
    				for (User client : users) {
    					if(client!=this) {
    						if(admin)
    							client.send("系统:"+":"+msg);
    						else
    							client.send(this.name+":"+msg);
    					}
    				}
    			}
    		}
    	}
    }
    
    

    3.工具类

    CloseUtil类(负责关闭流)

    package top.dlkkill.tcp.chat;
    
    import java.io.Closeable;
    
    public class CloseUtil {
    	public static void closeAll(Closeable ...io) {
    		for (Closeable closeable : io) {
    			try {
    				if(closeable!=null)
    					closeable.close();
    			}catch (Exception e) {
    				// TODO: handle exception
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    
  • 相关阅读:
    数值的整数次方
    二进制中1的个数
    SpingBoot 启动自动给MongoDB建库
    Java 依赖冲突的解决办法
    Http协议
    你被限流了吗?
    LeetCode 783. 二叉搜索树节点最小距离
    Leetcode 687. 最长同值路径
    LeetCode 784. 字母大小写全排列
    LeetCode 面试题 08.06. 汉诺塔问题
  • 原文地址:https://www.cnblogs.com/DLKKILL/p/10368968.html
Copyright © 2020-2023  润新知