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();
    				}
    			}
    		}
    	}
    }

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
里面包含聊天室的客户端和服务器端的源文件和一份完整的设计报告。 一、 系统概要 本系统实现基于VC++的网络聊天系统。有单独的客户端、服务器端。 服务器应用程序能够接受来自客户端的广播,然后向客户端发送本机的IP与服务端口,让客户端接入到服务器进行聊天,检测用户名是否合法(重复),服务器责接收来自客户端的聊天信息,并根据用户的需求发送给指定的人或所有人,能够给出上线下线提示。客户端能够发出连接请求,能编辑发送信息,可以指定发给单人或所有人,能显示聊天人数,上线下线用户等。 二、 通信规范的制定 服务请求规范: 服务器端: (1) 创建一个UDP的套接字,接受来自客户端的广播请求,当请求报文内容为“REQUEST FOR IP ADDRESS AND SERVERPORT”时,接受请求,给客户端发送本服务器TCP聊天室的端口号。 (2) 创建一个主要的TCP协议的套接字负责客户端TCP连接 ,处理它的连接请求事件。 (3)在主要的TCP连接协议的套接字里面再创建TCP套接字保存到动态数组里,在主要的套接字接受请求后 ,就用这些套接字和客户端发送和接受数据。 客户端: (1) 当用户按“连接”按钮时,创建UDP协议套接字,给本地计算机发广播,广播内容为“REQUEST FOR IP ADDRESS AND SERVERPORT”。 (2)当收到服务器端的回应,收到服务器发来的端口号后,关闭UDP连接。根据服务器的IP地址和端口号重新创建TCP连接。 故我思考:客户端一定要知道服务器的一个端口,我假设它知道服务器UDP服务的端口,通过发广播给服务器的UDP服务套接字,然后等待该套接字发回服务器TCP聊天室服务的端口号,IP地址用ReceiveForom也苛刻得到。 通信规范 通信规范的制定主要跟老师给出的差不多,并做了一小点增加: (增加验证用户名是否与聊天室已有用户重复,在服务器给客户端的消息中,增加标志0) ① TCP/IP数据通信 --- “聊天”消息传输格式 客户机 - 服务器 (1)传输“用户名” STX+1+用户名+ETX (2) 悄悄话 STX+2+用户名+”,”+内容+ETX (3) 对所有人说 STX+3+内容+ETX 服务器- 客户机 (0)请求用户名与在线用户名重复 //改进 STX+0+用户名+EXT (1)首次传输在线用户名 STX+1+用户名+ETX (2)传输新到用户名 STX+2+用户名+ETX (3)传输离线用户名 STX+3+用户名+ETX (4)传输聊天数据 STX+4+内容+ETX (注:STX为CHR(2),ETX 为CHR(3)) 三、 主要模块的设计分析 四、 系统运行效果 (要求有屏幕截图) 五、 心得与体会
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder_Boy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值