Java聊天室

一、问题描述:
多人聊天室,实现群聊和私聊的功能。
整的项目的大框架为:服务器端和客户端两个端口。
客户端可以向服务器端发送信息,并接受服务器返回的信息。
二、代码实现
服务器S端:

package cs;

import java.io.BufferedReader;
import java.io.BufferedWriter;
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.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 聊天室服务端
 * 运行在服务端SeverSocket有两个主要作用;
 * 1、向系统申请对外的服务端口,客户端就是通过这个端口与服务端建立连接
 * 2、监听服务端端口,等待客户端连接,一旦一个客户端通过Socket与服务端建立连接
 * 		那么SeverSocket会创建一个Socket与该客户端通讯。
 * 
 * 注意:该端口号不能和其他端口号冲突,否则会抛出端口号占用异常
 * 
 * @author 臻冉
 *
 */
public class Server {
	private ServerSocket server;
	private Socket socket;
	private ExecutorService pool;
	//所有客户端共享数据
	private PrintWriter[] outAll = new PrintWriter[0];
	
	//服务端初始化
	public Server() {	
		try {
			this.server = new ServerSocket(8088);
			
			//创建线程池
			 this.pool = Executors.newFixedThreadPool(2);
			 //listOut = Collections.synchronizedList(list);
		} catch (IOException e) {
			//e.printStackTrace();		//日志跟踪
			System.out.println("连接异常...");
		}
		
	}
	
	public void start(){
		/*
		   * ServerSocket实例化后等待客户端连接
		   * 提供了一个构造方法ServerSocket(int port)
		   */
		  try {
		  
		   /*
		    * ServerSocket提供了一个accept();
		    * 该方法是一个阻塞方法,调用后即等待客户端的连接,
		    * 一旦一个客户端通过端口连接,那么accept方法会返回一个Socket实例,
		    * 通过这个Socket实例就可以与连接的客户端交互了
		    */
			  
			  while(true) {
				  System.out.println("等待客户端连接...");
				  socket = server.accept();//阻塞作用
				  
				  InetAddress address = socket.getInetAddress();
				  
				  int port = socket.getPort();
				  System.out.println(port);
				  System.out.println(address+":连接上了");
				  //把每一个客户端连接成功后生成一个socket并纳入 
				  ClinetHandler clinet = new ClinetHandler(socket);
				  pool.equals(clinet);
				  
				 Thread t = new Thread(clinet);
				  t.start();
			  }
		  } catch (IOException e) {
		   e.printStackTrace();
		  }
	}
	
	public static void main(String[] args) {
		Server s = new Server();
		s.start();
	}
	
	//内部类(在这里我们要共享每个客户端pw数据):此任务主要负责接收每一个客户端输入和输出
	class ClinetHandler implements Runnable {
		private Socket socket;//每个用户的socket
		private String host;//获取地址
		private int port;//远程端口
		public ClinetHandler(Socket socket){
			this.socket = socket;
			//获取远程的地址
			InetAddress addresd = this.socket.getInetAddress();
			//将远程地址转化字符串形式
			this.host=addresd.getHostAddress();
			this.port=this.socket.getPort();
		}
		
		public void run(){
			PrintWriter pw = null;
			try {
					/*
				    * 获取远端的IP地址
				    * Socket提供一个获取远端的IP地址的方法getInetAddress(),
				    * 返回getInetAddress
				    */
				   //获取远端IP地址
				   InetAddress address=socket.getInetAddress();
				   
				   //获取远程的端口号
				   int port = socket.getPort();
				   System.out.println(port);
				   System.out.println(address+"连接上了");
				   
				   //获取输出流
				   OutputStream out = socket.getOutputStream();
				   pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out,"UTF-8")),true);
				   synchronized(outAll){
					   //对数组扩容
					   outAll = Arrays.copyOf(outAll, outAll.length+1);
					   outAll[outAll.length-1] = pw;
					   System.out.println("在线人数"+Arrays.toString(outAll));
				   }
				   
				   //对数组扩容
				   outAll = Arrays.copyOf(outAll, outAll.length+1);
				   //把每个客户端pw对象存入共享数组中的最后一个元素
				   outAll[outAll.length-1]=pw;
				   System.out.println("在线人数:"+outAll.length);
				   
				   //获取输入流
				   InputStream in = socket.getInputStream();
				   BufferedReader br= new BufferedReader(new InputStreamReader(in,"UTF-8"));
				   /*
				    * 客户端再断开连接时,不同的系统有不同的反应,
				    * linux的客户端断开后,br.readLine()会返回null,
				    * windows的客户端断开后,br.readLine()会直接抛出异常Connection reset
				    */
				   
				   String line = null;
				   while((line = br.readLine())!=null){
					   System.out.println(address+","+port+"说:"+line);
					   //pw.println(line);
					   //将当前客户端对应的输出流共享数据进行遍历
					   synchronized (outAll) {
						   for(int i=0;i<outAll.length;i++){
							   PrintWriter p = outAll[i];
							   p.println(line);
						   }
					}
					   
					   
					   
				   }
			} catch (Exception e) {
				
			} finally{
				//1、处理客户端断开后的操作,将该客户端的输出流从共享数组中删除
				
				synchronized (outAll) {
					for(int i=0;i<outAll.length;i++){
						if(outAll[i]==pw){
							outAll[i] = outAll[outAll.length-1];
							outAll=Arrays.copyOf(outAll, outAll.length-1);
							System.out.println("远程地址:"+host+"端口号:"+port+"下线了!");
							System.out.println("在线人数是:"+outAll.length);
							break;
						}
					}
				}
				
				//2.客户端断开连接后,服务端关闭该客户Socket,释放资源
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}


package cs;

import java.io.BufferedReader;
import java.io.BufferedWriter;
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.Socket;
import java.util.Scanner;

/**
 * 聊天室客户端
 * @author 臻冉
 *
 */
public class Clinet {
	/*
	 *实例化Soket就是连接额服务端的过程
	 *	Socket提供了构造方法
	 *Socket(InterAddress address,int prot)
	 *参数1:服务端地址(IP)
	 *参数2:服务端开启的两端
	 *
	 * 通过服务器IP地址可以找到服务器所出在通过端口找到
	 * 运行在服务器计算机上的服务端应用程序。
	 * 
	 * localhost:本机(IP:127.0.0.1)
	 * 查询本机IP地址:cmd--->ipconfig 查看电脑IP地址
	 */
	private Socket socket;
	public Clinet() {
		try {
			this.socket = new Socket("localhost",8088);
			System.out.println("客户端连接成功!");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	//启动客户端方法
	private void start(){
		Scanner scanner = new Scanner(System.in);
		try {
			
			/*
			 * Socket提供了方法:
			 * getOutputStream返回OutputStream
			 * 
			 * 通过返回的字节流写出的数据发送到给远端计算机,对于客户端这边而言,
			 * 远程计算机就是服务端了
			 * getInputStream 返回InputStream
			 */
			
			ServerHandler server = new ServerHandler(socket);
			//该线程接收服务端发送过来的信息
			Thread t = new Thread(server);
			t.start();//默认调用线程中的start()
			
			//获取输出流
			OutputStream out = socket.getOutputStream();		//所有字节流的父类
			PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out,"UTF-8")),true);
			scanner = new Scanner(System.in);
			System.out.println("和服务端说话吧...");
			while(true){
				String message = scanner.next();
				pw.println(message);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			scanner.close();
		}
	}
	
	public static void main(String[] args) {
		Clinet clinet = new Clinet();
		clinet.start();
	}
}

//该任务接收服务端发送过来的消息
class ServerHandler implements Runnable{
	private Socket socket;
	public ServerHandler(Socket socket){
		this.socket = socket;
	}
	@Override
	public void run() {
		try {
			//获取输入流
			InputStream in = socket.getInputStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(in,"UTF-8"));
			String line = null;
			while((line=br.readLine())!=null){
				System.out.println("服务端说:"+line);
			}
		} catch (IOException e) {
			//e.printStackTrace();		//日志跟踪
			System.out.println("服务端异常,请稍等...");
			//throw new RuntimeException("服务端异常,请稍等...");
		}
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值