博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【JavaSE】day15_TCP之聊天室
阅读量:4305 次
发布时间:2019-05-27

本文共 6111 字,大约阅读时间需要 20 分钟。

【JavaSE】day15_TCP之聊天室

1.服务端:

package day07chat;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.InetAddress;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.List;/** * 聊天室服务端 * java.net.ServerSocket * ServerSocket是运行在服务端的,其作用是向系统 * 申请服务端口,以便监听该端口,等待客户端的连接. * 一旦一个客户端连接,就会创建一个Socket与 * 该客户端进行通信. */public class Server {	//运行在服务端的ServerSocket	private ServerSocket server;		//存放所有输出流的集合,用于广播消息	private List
allOut; /** * 构造方法,用来初始化服务端 */ public Server(){ try{ allOut = new ArrayList
(); /* * 初始化ServerSocket的同时需要指定服务端口,该端口不能与当前系统 * 使用TCP协议的其他程序申请的端口冲突,否则会抛出端口被占用的异常. */ server = new ServerSocket(8088); }catch(Exception e){ e.printStackTrace(); } } private synchronized void addOut(PrintWriter pw){ allOut.add(pw); } private synchronized void removeOut(PrintWriter pw){ allOut.remove(pw); } private synchronized void sendMessageToAllClient(String m){ for(PrintWriter pw : allOut){ pw.println(m); } } /** * 服务端开始工作的方法 */ public void start(){ try{ /* * Socket accept() * ServerSocket提供的该方法用来监听打开的服务端口(8088), * 该方法是一个阻塞方法,直到一个客户端尝试连接才会解除阻塞, * 并创建一个Socket与刚才连接的客户端进行通讯. * * accept方法每次调用都会等待一个客户端连接,所以若希望服务端 * 能够接受若干客户端的连接,就需要多次调用该方法,来分别获取对应 * 这些客户端的Socket与他们通讯. * * 查看本机ip: * windows:ipconfig * linux:/sbin/ifconfig */ while(true){ System.out.println("等待客户端连接..."); Socket socket = server.accept(); System.out.println("一个客户端连接了!"); /* * 当一个客户端连接后,启动一个线程,来负责与该客户端交互. */ ClientHandler handler = new ClientHandler(socket); Thread t = new Thread(handler); t.start(); } }catch(Exception e){ e.printStackTrace(); } } public static void main(String[] args){ Server server = new Server(); server.start(); } /** * 该线程用来与一个指定的客户端进行交互. * 每当一个客户端连接服务端后,都会启动当前线程来负责与之交互工作. * */ private class ClientHandler implements Runnable{ //当前线程交互的客户端的Socket private Socket socket; //客户端的地址信息 private String host; public ClientHandler(Socket socket){ this.socket = socket; //通过socket可以得到远端计算机信息 InetAddress address = socket.getInetAddress(); //获取远端计算机IP host = address.getHostAddress(); } public void run() { PrintWriter pw = null; try{ /* * InputStream getInputStream() * Socket提供的该方法用来获取输入流,读取远端计算机 * 发送过来的数据. */ InputStream in = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(in,"UTF-8"); BufferedReader br = new BufferedReader(isr); /* * 通过客户端的Socket获取输出流,以便将消息 * 发送给客户端. */ OutputStream out = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8"); pw = new PrintWriter(osw,true); //共享该客户端的输出流 addOut(pw); //广播该用户上线. sendMessageToAllClient(host+"上线了"); /* * 当我们使用BufferedReader读取来自远端计算机发送过来的内容时, * 由于远端计算机的操作系统不同,当它们断开连接时,这里readLine * 方法的结果也不同: * windows->抛异常 * linux->返回null */ //Scanner scan = new Scanner(System.in); String str = null; while((str=br.readLine())!=null){ sendMessageToAllClient(host+"说:"+str); } }catch(Exception e){ e.printStackTrace(); }finally{ /* * 当该客户端与服务器断开时,应当将该客户端从输出流 * 共享集合中删除. */ removeOut(pw); //广播该用户下线. sendMessageToAllClient(host+"下线了"); /* * 无论时linux的客户端,还是windows的客户端,当与 * 服务端断开连接后,都应当将与该客户端交互的Socket * 关闭,来释放底层资源. */ if(socket!=null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }

2.客户端:

package day07chat;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;/** * 聊天室客户端 * * java.net.Socket * 封装了TCP协议的Socket.通过它来连接服务端的ServerSocket, * 并创建输入输出流来与服务器通信. */public class Client {	//用来与服务端通信的Socket	private Socket socket;		/**	 * 构造方法,用来初始化客户端	 * 构造方法通常用来初始化对象属性等操作	 */	public Client(){		try{						/*			 * 初始化Socket时需要传入两个参数			 * 1:服务端的IP地址			 * 2:服务端的端口号			 * 			 * 首先要清楚:			 * 通讯是客户端计算机的一个客户端应用程序与服务端			 * 计算机(俗称服务器)上的一个服务端应用程序之间的通讯.			 * 			 * IP地址的作用是让我们通过网络可以找到服务器			 * 而端口可以让我们找到运行在服务器上的服务端应用程序.			 * 			 * 创建Socket实例的过程就是与服务端连接的过程,若可以			 * 成功与服务器连接上,则会创建Socket实例,否则构造			 * 方法会抛异常.			 */			System.out.println("正在尝试连接服务端...");			//本机ip:192.168.202.13			socket = new Socket("localhost",8088);			System.out.println("与服务端连接成功!");					}catch(Exception e){			e.printStackTrace();		}	}		/**	 * 客户端开始工作的方法	 */	public void start(){				try{			/*			 * 当客户端启动后,就启动接收服务段发送过来			 * 消息的线程.			 */			GetServerMessageHandler handler = new GetServerMessageHandler();			Thread t = new Thread(handler);			t.start();						/*			 * OutputStream getOutputStream()			 * Socket提供了该方法,用来获取输出流来向服务端发送数据.			 */			OutputStream out = socket.getOutputStream();						OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");						PrintWriter pw = new PrintWriter(osw,true);						Scanner scan = new Scanner(System.in);			String str = "";			System.out.println("请输入...");			while(!str.equalsIgnoreCase("exit")){				str = scan.nextLine().trim();				pw.println(str);				//System.out.println(br.readLine());			}									pw.close();					}catch(Exception e){					}	}			public static void main(String[] args){		Client client = new Client();		client.start();	}		/**	 * 由于接收服务端发送过来的消息,与我们给定服务端发送消息	 * 没有必然关系,所以两者应当在两个不同的线程上完成,各做	 * 各的,互不干涉.	 */	private class GetServerMessageHandler implements Runnable{		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){				e.printStackTrace();			}		}			}}

转载地址:http://ygews.baihongyu.com/

你可能感兴趣的文章
vue中的状态管理 vuex store
查看>>
Maven之阿里云镜像仓库配置
查看>>
Maven:mirror和repository 区别
查看>>
微服务网关 Spring Cloud Gateway
查看>>
SpringCloud Feign的使用方式(一)
查看>>
SpringCloud Feign的使用方式(二)
查看>>
关于Vue-cli+ElementUI项目 打包时排除Vue和ElementUI
查看>>
Vue 路由懒加载根据根路由合并chunk块
查看>>
vue中 不更新视图 四种解决方法
查看>>
MySQL 查看执行计划
查看>>
OpenGL ES 3.0(四)图元、VBO、VAO
查看>>
OpenGL ES 3.0(五)纹理
查看>>
OpenGL ES 3.0(八)实现带水印的相机预览功能
查看>>
OpenGL ES 3.0(九)实现美颜相机功能
查看>>
FFmpeg 的介绍与使用
查看>>
Android 虚拟机简单介绍——ART、Dalvik、启动流程分析
查看>>
原理性地理解 Java 泛型中的 extends、super 及 Kotlin 的协变、逆变
查看>>
FFmpeg 是如何实现多态的?
查看>>
FFmpeg 源码分析 - avcodec_send_packet 和 avcodec_receive_frame
查看>>
FFmpeg 新旧版本编码 API 的区别
查看>>