当前位置: 首页 > news >正文

网站搭建赚钱吗wordpress插件微信

网站搭建赚钱吗,wordpress插件微信,空间设计图片,怎么成立一家公司一、BIO/NIO/AIO介绍 1. 背景说明 在Java的软件设计开发中#xff0c;通信架构是不可避免的。我们在进行不同系统或者不同进程之间的数据交互#xff0c;或者在高并发的通信场景下都需要用到网络通信相关的技术。 对于一些经验丰富的程序员来说#xff0c;Java早期的网络…一、BIO/NIO/AIO介绍 1. 背景说明 在Java的软件设计开发中通信架构是不可避免的。我们在进行不同系统或者不同进程之间的数据交互或者在高并发的通信场景下都需要用到网络通信相关的技术。 对于一些经验丰富的程序员来说Java早期的网络通信架构存在一些缺陷其中最令人恼火的是基于性能低下的同步阻塞式的I/O通信BIO)。随着互联网开发下通信性能的高要求Java在2002年开始支持了非阻塞式的I/O通信技术NIO)。大多数读者在学习网络通信相关技术的时候都只是接触到零碎的通信技术点没有完整的技术体系架构以至于对Java的通信场景总是没有清晰的解决方案。 本文将通过大量清晰直接的案例从最基础的BlO式通信开始介绍到NIO、AIO读者可以清晰的了解到阻塞 、同步 、异步 的现象、概念和特征以及优缺点。本文结合了大量的案例让读者可以快速了解每种通信架构的使用 。 2. 技术要求 不适合0基础需要掌握Java SE基础编程如Java多线程、Java IO编程、Java网络基础知识还要了解一些Java设计模式能熟练掌握OOP编程有一定的编程思维。 3. 通信技术整体上解决的问题 局域网内的通信要求多系统间的底层消息传递机制高并发下大数据量的通信场景需要如netty游戏行业无论是手游还是大型的网络游戏Java语言越来越被广泛应用。 二、Java的IO演进之路 1.I/O模型基本说明 I/O模型就是用什么样的通道或者说是通信模式和架构进行数据的传输和接收很大程度上决定了程序通信的性能Java共支持3种网络编程的I/O模型BlO. NIO. AlO 实际通信需求下要根据不同的业务场景和性能需求决定选择不同的I/O模型 2. I/O模型介绍3种 ①Java BIO 同步阻塞传统阻塞型服务器实现模式为一个连接一个线程即客户端有连接请求时服务器端就需要启动一个线程进行处理如果这个连接不做任何事情就会造成不必要的的线程开销以下是简单示意图 ②Java NIO 同步非阻塞服务器实现模式为一个线程处理多个请求连接即客户端发送的连接请求都会注册到多路复用器上多路复用器轮询到连接有I/O请求就进行处理【简单示意图】 ③Java AIO 也叫NIO.2异步非阻塞服务器实现模式为一个有效请求一个线程客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理一般适用于连接数较多且连接时间较长的应用 3. BIO、NIO、AIO适用场景分析 ①BIO 方式适用于连接数目比较小且固定的架构这种方式对服务器资源要求比较高并局限于应用中 jDK1.4以前的唯一选择但程序简单易理解。 ②NIO 方式适用于连接数目多且连接比较短轻操作的架构比如聊天服务器弹幕系统服务器间通讯等。 编程比较复杂jDK1.4开始支持。 ③AIO 方式使用于连接数目多且连接比较长重操作的架构比如相册服务器充分调用OS参与并发操作 编程比较复杂JDK7开始支持。 三、JAVA BIO深入剖析 1. Java BIO基本介绍 Java BlO就是传统的Java IO编程其相关的类和接口在Java.io包中。 BIO(blocking I/O) 同步阻塞服务器实现模式为一个连接一个线程即客户端有连接请求时服务器端就需要启动一个线程进行处理如果这个连接不做任何事情会造成不必要的线程开销可以通过线程池机制改善 实现多个客户连接服务器。 2. Java BIO工作机制 服务端和客户端都是socket 服务端 通过ServerSocket注册端口服务端通过调用accept方法用于监听客户端的Socket请求从Socket种获取字节输入或者输出流进行数据的读写操作 客户端 通过Socket对象请求与服务端的连接从Socket中得到字节输入或者字节输出流进行数据的读写操作 3. 传统的BIO编程实例回顾 网络编程的基本模型是Client/Server模型也就是两个进程之间进行相互通信其中服务端提供位置信息绑定IP地址和端口客户端通过连接操作向服务端监听的端口地址发起连接请求基于TCP协议下进行三次握手连接连接成功后双方通过网络套接字Socket进行通信。 传统的同步阻塞模型开发中服务端ServerSocket负责绑定IP地址启动监听端口客户端Socket负责发起连接操作。连接成功后双方通过输入和输出流进行同步阻塞式通信。 基于BIO模式下的通信客户端-服务端是完全同步完全藕合的。 服务端代码 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; //服务端目标客户端发送消息服务端接收消息 public class Server {public static void main(String[] args) {System.out.println(服务端启动);try {//1.定义一个ServerSocket对象进行服务端的端口注册ServerSocket ss new ServerSocket(9999);//2.监听客户端的Socket连接请求Socket socket ss.accept();//3.从Socket管道中得到一个字节输入流对象InputStream is socket.getInputStream();//4.把字节输入流包装成一个缓冲字符输入流BufferedReader reader new BufferedReader(new InputStreamReader(is));String msg;if ((msg reader.readLine()) ! null) {System.out.println(服务端接收到msg);}} catch (IOException e) {throw new RuntimeException(e);}} }客户端代码 import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; //客户端连接上服务端然后发送消息到服务端 public class Client {public static void main(String[] args) {//1.创建和服务端的连接try {Socket socket new Socket(127.0.0.1,9999);//2.从socket对象中获取一个字节输出流OutputStream os socket.getOutputStream();//3.把字节输出流包装成一个打印流PrintStream ps new PrintStream(os);//4.输出消息ps.println(hello world!服务端你好);ps.flush();} catch (IOException e) {throw new RuntimeException(e);}} } 启动服务端后启动客户端效果如下 服务端启动 服务端接收到hello world!服务端你好小结 在以上通信中服务端会一直等待客户端的消息如果客户端没有进行消息的发送服务端将一直进入阻塞状态同时服务端是按照行获取消息的这意味着客户端也必须按照行进行消息的发送否则服务端将进入等待消息的阻塞状态 4. BIO模式下多发和多收消息 上面的案例只能实现单发和单收消息并不能实现反复的收消息和发消息。我们只需要在客户端案例中加上反复按照行发送消息的逻辑即可案例代码如下 服务端代码 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; //服务端目标服务端可以反复的接收消息客户端可以反复的发送消息 public class Server {public static void main(String[] args) {System.out.println(服务端启动);try {//1.定义一个ServerSocket对象进行服务端的端口注册ServerSocket ss new ServerSocket(9999);//2.监听客户端的Socket连接请求Socket socket ss.accept();//3.从Socket管道中得到一个字节输入流对象InputStream is socket.getInputStream();//4.把字节输入流包装成一个缓冲字符输入流BufferedReader reader new BufferedReader(new InputStreamReader(is));String msg;while ((msg reader.readLine()) ! null) {System.out.println(服务端接收到msg);}} catch (IOException e) {throw new RuntimeException(e);}} }客户端代码 public class Client {public static void main(String[] args) {//1.创建和服务端的连接try {Socket socket new Socket(127.0.0.1,9999);//2.从socket对象中获取一个字节输出流OutputStream os socket.getOutputStream();//3.把字节输出流包装成一个打印流PrintStream ps new PrintStream(os);Scanner scanner new Scanner(System.in);String msg;while (true){//4.输出消息System.out.println(请说);msg scanner.nextLine();ps.println(msg);ps.flush();}} catch (IOException e) {throw new RuntimeException(e);}} }效果 启动服务端-启动客户端-在客户端打字服务端收到消息如下 服务端启动 服务端接收到你好 服务端接收到今天在干嘛 服务端接收到好的5. BIO模式下接收多个客户端 在上述的案例中一个服务端只能接收一个客户端的通信请求那么如果服务端需要处理很多个客户端的消息通信请求应该如何处理呢 此时我们就需要在服务端引入线程了也就是说客户端每发起一个请求服务端就创建一个新的线程 来处理这个客户端的请求这样就实现了一个客户端一个线程的模型图解模式如下 服务端代码 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; //目标实现服务端可以同时接收到多个客户端的Socket通信需求 //思路服务端每接收一个客户端的socket请求对象之后都交给一个独立的线程来处理客户端的数据交互需求 public class Server {public static void main(String[] args) {try {//1.注册端口ServerSocket ss new ServerSocket(9999);//2.定义一个死循环不断接收客户端的socket连接请求while(true){Socket socket ss.accept();//3.创建一个独立的线程来处理与这个客户端的socket通信需求new ServerThreadReader(socket).start();}} catch (IOException e) {throw new RuntimeException(e);}} }线程处理代码 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket; public class ServerThreadReader extends Thread {private Socket socket;public ServerThreadReader(Socket socket){this.socket socket;}Overridepublic void run() {try {//从socket对象中得到一个字节输入流InputStream is socket.getInputStream();//使用缓存字符输入流包装字节输入流BufferedReader br new BufferedReader(new InputStreamReader(is));String msg;while ((msg br.readLine()) ! null){System.out.println(msg);}} catch (IOException e) {e.printStackTrace();}} }客户端代码 import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; public class Client {public static void main(String[] args) {try {//1.请求与服务端的Socket对象连接Socket socket new Socket(127.0.0.1,9999);//2. 得到一个打印流PrintStream ps new PrintStream(socket.getOutputStream());//3. 使用循环不断的发送消息给服务端接收Scanner sc new Scanner(System.in);while (true){System.out.println(请说);String msg sc.nextLine();ps.println(msg);ps.flush();}} catch (Exception e) {e.printStackTrace();}} }启动服务端启动客户端1、客户端2然后通过客户端1和客户端2分别发送消息服务端效果如下 客户端1 客户端2小结 每个Socket接收到都会创建一个线程线程的竞争、切换上下文影响性能每个线程都会占用栈空间和CPU资源并不是每个socket都进行lO操作无意义的线程处理客户端的并发访问增加时。服务端将呈现1:1的线程开销访问量越大系统将发生线程栈溢出线程创建失败最终导致进程宕机或者僵死从而不能对外提供服务 6. 伪异步I/O编程 在上述案例中客户端的并发访问增加时。服务端将呈现1:1的线程开销访问量越大系统将发生线程栈溢出线程创建失败最终导致进程宕机或者僵死从而不能对外提供服务。 接下来我们采用一个伪异步I/O的通信框架采用线程池和任务队列实现当客户端接入时将客户端的Socket封装成一个Task(该任务实现Java. lang. Runnable(线程任务接口交给后端的线程池中进行处理。JDK的线程池维护一个消息队列和N个活跃的线程对消息队列中Socket任务进行处理由于线程池可以设置消息队列的大小和最大线程数因此它的资源占用是可控的无论多少个客户端并发访问都不会导致资源的耗尽和宕机。 如下图所示 服务端代码 import java.net.ServerSocket; import java.net.Socket; //目标开发实现伪异步通讯架构 //思路服务端每接收到一个客户端socket请求对象之后都交给一个独立的线程来处理客户端的数据交互需求 public class Server {public static void main(String[] args) {try {//1.注册端口ServerSocket ss new ServerSocket(9999);//2.定义一个死循环负责不断的接收客户端的Socket的连接请求//初始化一个线程池对象HandlerSocketServerPool pool new HandlerSocketServerPool(3, 10);while (true) {Socket socket ss.accept();//3.把socket对象交给一个线程池进行处理//把socket封装成一个任务对象交给线程池处理Runnable target new ServerRunnableTarget(socket);pool.execute(target);}} catch (Exception e) {e.printStackTrace();}} }定义线程池代码 import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class HandlerSocketServerPool {//1. 创建一个线程池的成员变量用于存储一个线程池对象private ExecutorService executorService;/*** 2.创建这个类的的对象的时候就需要初始化线程池对象* public ThreadPoolExecutor(int corePoolSize,* int maximumPoolSize,* long keepAliveTime,* TimeUnit unit,* BlockingQueueRunnable workQueue)*/public HandlerSocketServerPool(int maxThreadNum, int queueSize) {executorService new ThreadPoolExecutor(3, maxThreadNum, 120,TimeUnit.SECONDS, new ArrayBlockingQueueRunnable(queueSize));}/*** 3.提供一个方法来提交任务给线程池的任务队列来暂存等待线程池来处理*/public void execute(Runnable target) {executorService.execute(target);} }Socket任务类 import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket; public class ServerRunnableTarget implements Runnable {private Socket socket;public ServerRunnableTarget(Socket socket) {this.socket socket;}Overridepublic void run() {//处理接收到的客户端socket通信需求try {//1.从socket管道中得到一个字节输入流对象InputStream is socket.getInputStream();//2.把字节输入流包装成一个缓存字符输入流BufferedReader br new BufferedReader(new InputStreamReader(is));String msg;while ((msg br.readLine()) ! null) {System.out.println(服务端收到 msg);}} catch (Exception e) {e.printStackTrace();}} }客户端代码 import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; public class Client {public static void main(String[] args) {try {//1.请求与服务端的Socket对象连接Socket socket new Socket(127.0.0.1,9999);//2. 得到一个打印流PrintStream ps new PrintStream(socket.getOutputStream());//3. 使用循环不断的发送消息给服务端接收Scanner sc new Scanner(System.in);while (true){System.out.println(请说);String msg sc.nextLine();ps.println(msg);ps.flush();}} catch (Exception e) {e.printStackTrace();}} }效果 可以接收到3个客户端的消息当开启第四个客户端的时候不报错但是服务端不会接收到第四个客户端发送的消息。 小结 伪异步采用了线程池 实现因此避免了为每个请求创建一个独立线程造成线程资源耗尽的问题但由于底层依然是采用的同步阻塞模型因此无法从根本上解决问题。 如果单个消息处理的缓慢或者服务器线程池中的全部线程都被阻塞那么后续socket的I/O消息都将在队列中排队。新的Socket请求将被拒绝客户端会发生大量连接超时。 7. 基于BIO形式下的文件上传 目标 支持任意类型文件形式的上传 客户端代码 import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; //目标实现客户端上传任意类型的文件数据给服务端保存起来 public class Client {public static void main(String[] args) {try(InputStream is new FileInputStream(D:\\Desktop\\抖音的推荐机制.docx)) {//1.请求与服务端的socket连接Socket socket new Socket(127.0.0.1, 8888);//2.把字节输出流包装成一个数据输出流DataOutputStream dos new DataOutputStream(socket.getOutputStream());//3.先发送文件的后缀名给服务端dos.writeUTF(.docx);//4.把文件数据发送给服务端进行接收byte[] buffer new byte[1024];int len;while ((len is.read(buffer)) 0) {dos.write(buffer, 0, len);}dos.flush();//通知服务端我客户端这边的数据已经发送完毕了socket.shutdownOutput();} catch (IOException e) {throw new RuntimeException(e);}} }服务端代码 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Server {public static void main(String[] args) {System.out.println(服务端启动);try {ServerSocket ss new ServerSocket(8888);while (true) {Socket socket ss.accept();//交给一个独立的线程来处理与这个客户端的文件通信需求new ServerReadThread(socket).start();}} catch (IOException e) {throw new RuntimeException(e);}} }Socket线程处理类 import java.io.DataInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.UUID; public class ServerReadThread extends Thread{private Socket socket;public ServerReadThread(Socket socket) {this.socket socket;}Overridepublic void run() {try {//1.得到一个数据输入流来读取客户端发送过来的数据DataInputStream dis new DataInputStream(socket.getInputStream());//2.读取客户端发送过来的文件类型String suffix dis.readUTF();System.out.println(服务端已经成功接收到文件类型suffix);//3.定义一个字节输出管道负责将客户端发送来的数据写出去OutputStream os new FileOutputStream(D:\\Desktop\\server\\ UUID.randomUUID().toString() suffix);//4.从数据输入流中读取文件数据写出到字节输出流中去byte[] buffer new byte[1024];int len;while ((len dis.read(buffer)) 0) {os.write(buffer, 0, len);}os.close();System.out.println(服务端接收文件保存成功);} catch (IOException e) {throw new RuntimeException(e);}} }启动Server后启动Client效果如下 小结 同步阻塞模式下BIO)客户端怎么发服务端就必须对应的怎么收。如客户端用的是DataOutputStream那么服务端就该用DataInputStream客户端dos.writeUTF(“.jpg”);服务端就该String suffix dis.readUTF();客户端发完数据后必须通知服务端自己已经发完socket.shutdownOutput()否则服务端会一直等待。 8. Java BIO模式下的端口转发思想 需求需要实现一个客户端的消息可以发送给所有的客户端去接收。群聊实现 服务端代码 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; /*** 目标BIO模式下的端口转发思想-服务端实现* 服务端实现需求* 1.注册端口* 2.接收客户端的socket连接交给一个独立的线程来处理* 3.把当前连接的客户端socket存入到一个所谓的在线socket集合中保存* 4.接收客户端的消息然后推动给当前所有的在线socket接收*/ public class Server {public static ListSocket allSocketOnline new ArrayList();public static void main(String[] args) {try {ServerSocket ss new ServerSocket(9999);while (true) {Socket socket ss.accept();//把登陆的客户端socket存入到一个在线集合上allSocketOnline.add(socket);//为当前登录成功的socket分配一个独立的线程来处理与之通信new ServerReadThread(socket).start();}} catch (IOException e) {throw new RuntimeException(e);}} }服务端线程处理类 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; public class ServerReadThread extends Thread{private Socket socket;public ServerReadThread(Socket socket){this.socket socket;}Overridepublic void run() {try {//1.从socket中获取当前客户端的输入流BufferedReader br new BufferedReader(new InputStreamReader(socket.getInputStream()));String msg;while ((msg br.readLine())!null) {//2.服务端接收到了客户端的消息之后是需要推送给当前所有的在线socketsendMsgToAllClient(msg, socket);}} catch (Exception e) {e.printStackTrace();System.out.println(当前有人下线了);//从在线socket集合中移除本socketServer.allSocketOnline.remove(socket);}}/*** 把当前客户端发来的消息推送给全部在线的socket* param msg*/private void sendMsgToAllClient(String msg, Socket socket) throws IOException {for (Socket socket1 : Server.allSocketOnline) {//只发送给除自己以外的客户端if (socket ! socket1) {PrintStream ps new PrintStream(socket1.getOutputStream());ps.println(msg);ps.flush();}}} }客户端代码 import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; public class Client {public static void main(String[] args) {try {//1.请求与服务端的Socket对象连接Socket socket new Socket(127.0.0.1, 9999);//收消息Thread clientThread new ClientReaderThread(socket);clientThread.start();while (true) {//发消息OutputStream os socket.getOutputStream();PrintStream ps new PrintStream(os);//3. 使用循环不断的发送消息给服务端接收Scanner sc new Scanner(System.in);//System.out.print(client send message);String msg sc.nextLine();ps.println(msg);ps.flush();}} catch (Exception e) {e.printStackTrace();}} }客户端线程处理类 import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket; public class ClientReaderThread extends Thread {private Socket socket;public ClientReaderThread(Socket socket) {this.socket socket;}Overridepublic void run() {try {while (true) {InputStream is socket.getInputStream();//4.把字节输入流包装成一个缓存字符输入流BufferedReader br new BufferedReader(new InputStreamReader(is));String msg;if ((msg br.readLine()) ! null) {System.out.println(msg);}}} catch (Exception e) {}} }效果 先启动服务端Server然后启动3个客户端Client 9. 基于BIO模式下即时通讯 基于BIO模式下的即时通信我们需要解决客户端到客户端的通信也就是需要实现客户端与客户端的端口消息转发逻辑。 项目案例说明 本项目案例为即时通信的软件项目适合基础加强的大案例具备综合性。学习本项目案例至少需要具备如下java SE技术点 java面向对象设计语法设计多线程技术IO流技术网络通信相关技术集合框架项目开发思维java常用api使用 …… 功能清单简单说明 客户端登录功能 可以启动客户端进行登录客户端登录只需要输入用户名和服务端IP地址即可。 在线人数实时更新 客户端用户登录后需要同步更新所有客户端的联系人信息栏。 离线人数更新 检测到有客户端下线后需要同步更新所有客户端的联系人信息栏。 群聊 任意一个客户端的消息可以推动给当前所有的客户端接收。 私聊 任意一个客户端消息可以推送给当前所有客户端接收。 消息 可以选择某个员工然后发出的消息可以该用户但是其他所有人都能收到消息。 消息用户和消息时间点 服务端可以实时记录该用户的消息时间点然后进行消息的多路转发或则选择。 技术选型分析 Java GUIBIO 服务端设计 服务端接收多个客户端逻辑 服务端需要接收多个客户端目前我们采取的策略是 一个客户端对应一个服务端线程服务端除了要注册端口以外还需要为每个客户端分配 一个独立线程处理与之通信 服务端主体代码主要进行端口注册和接收客户端分配线程处理该客户端请求 服务端接收登陆消息以及监测离线 在上面我们实现了服务端可以接收多个客户端连接接下来我们要接收客户端的登陆消息。 我们需要在服务端处理客户端线程的登陆消息。需要注意的是服务端需要接收客户端的消息可能有很多种分别是登陆消息群聊消息私聊消息和消息。这里需要约定如果客户端发送消息之前需要先发送消息的类型类型我们使用信号值标志123。 1代表接收的是登陆消息2代表群发| 消息3代表了私聊消息 服务端的线程中有异常校验机制一旦发现客户端下线会在异常机制中处理然后移除当前客户端用户把最新的用户列表 发回给全部客户端进行在线人数更新。 服务端接收群聊消息 在上面实现了接收客户端的登陆消息然后提取当前在线的全部的用户名称和当前登陆的用户名称发送给全部在线用户更新自己的在线人数列表。接下来要接收客户端发来的群聊消息然后推送给当前在线的所有客户端。 服务端接收私聊消息 解决私聊消息的推送逻辑私聊消息需要知道推送给某个具体的客户端。我们可以接收到客户端发来的私聊用户名称根据用户名称定位该用户的Socket管道然后单独推送消息给该Socket管道。 代码实现 常量类 public class Constants {//端口public static final int PORT 7778;//逗号分割public static final String SPLIT ,; }ServerChat import com.linxc.bio.instant_message.util.Constants; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; public class ServerChat {//定义一个集合存放所有在线的socket//在线集合只需要一个存储客户端socket的同时还需要知道这个socket的客户端名称public static MapSocket, String onLineSockets new HashMap();public static void main(String[] args) {//注册端口ServerSocket serverSocket null;try {serverSocket new ServerSocket(Constants.PORT);//循环一直等待所有可能的客户端连接while (true) {Socket socket serverSocket.accept();//把客户端的socket管道单独配置一个线程来处理new ServerReader(socket).start();}} catch (Exception e) {e.printStackTrace();}} }ServerReader import com.linxc.bio.instant_message.util.Constants; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.Socket; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Set; public class ServerReader extends Thread {private Socket socket;public ServerReader(Socket socket) {this.socket socket;}Overridepublic void run() {DataInputStream dis null;try {dis new DataInputStream(socket.getInputStream());//1.循环一直等待客户端的消息while (true) {//2.读取当前的消息类型登录群发私聊消息int flag dis.readInt();System.out.println(服务flagflag);if (flag 1) {//先将当前登录的客户端socket存到在线人数的socket集合中String name dis.readUTF();System.out.println(name ----- socket.getRemoteSocketAddress());ServerChat.onLineSockets.put(socket, name);}writeMsg(flag, dis);}} catch (Exception e) {System.out.println(---有人下线了---);ServerChat.onLineSockets.remove(socket);try {//重新更新在线人数并发给所有客户端writeMsg(1, dis);} catch (Exception e1) {e1.printStackTrace();}}}private void writeMsg(int flag, DataInputStream dis) throws Exception {//定义一个变量存放最终的消息形式String msg null;if (flag 1) {//读取所有在线人数发给所有客户端去更新自己的在线人数列表StringBuilder rs new StringBuilder();CollectionString onlineNames ServerChat.onLineSockets.values();//判断是否存在在线人数if (onlineNames ! null onlineNames.size() 0) {for (String name : onlineNames) {rs.append(name Constants.SPLIT);}//去掉最后一个分隔符msg rs.substring(0, rs.lastIndexOf(Constants.SPLIT));//将消息发送给所有的客户端sendMsgToAll(flag, msg);}}else if (flag 2||flag 3) {//读到消息群发的或者是消息String newMsg dis.readUTF();//消息String sendName ServerChat.onLineSockets.get(socket);//内容StringBuilder msgFinal new StringBuilder();//时间SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss EEE);if (flag 2) {//群发消息和消息msgFinal.append(sendName).append().append(sdf.format(System.currentTimeMillis()*2)).append(\r\n);msgFinal.append( ).append(newMsg).append(\r\n);sendMsgToAll(flag, msgFinal.toString());} else if (flag 3) {//私聊消息msgFinal.append(sendName).append().append(sdf.format(System.currentTimeMillis()*2)).append( 对您私发\r\n);msgFinal.append().append(newMsg).append(\r\n);//私发//得到发给谁String destName dis.readUTF();sendMsgToOne(destName, msgFinal.toString());}System.out.println(发送消息msgFinal);}}/*** param destName 对谁私发* param msg 发的消息内容* throws Exception*/private void sendMsgToOne(String destName, String msg) throws Exception {//拿到所有在线的socket管道 给这些管道写出消息SetSocket allOnLineSockets ServerChat.onLineSockets.keySet();for (Socket sk : allOnLineSockets) {//得到当前需要私发的Socket//只对这个名字对应的socket私发消息if (ServerChat.onLineSockets.get(sk).trim().equals(destName)) {DataOutputStream dos newDataOutputStream(sk.getOutputStream());dos.writeInt(2);//消息类型dos.writeUTF(msg);dos.flush();}}}private void sendMsgToAll(int flag, String msg) throws Exception {//拿到所有的在线socket管道 给这些管道写出消息SetSocket allOnLineSockets ServerChat.onLineSockets.keySet();for (Socket sk : allOnLineSockets) {DataOutputStream dos new DataOutputStream(sk.getOutputStream());dos.writeInt(flag);//消息类型System.out.println(msg);dos.writeUTF(msg);dos.flush();}} }客户端设计 启动客户端界面登录刷新在线 客户端界面主要是GUI设计主体页面分为登陆界面和聊天窗口以及在线用户列表。 登陆输入服务端ip和用户名后要请求与服务端的登陆然后立即为当前客户端分配一个读线程处理客户端的读数据消息。因为客户端可能随时会接收到服务端那边转发过来的各种即时消息信息。客户端登陆完成服务端收到登陆的用户名后会立即发来最新的用户列表给客户端更新。 客户端发送消息逻辑 客户端发送群聊消息消息以及私聊消息。 实现步骤 客户端启动后在聊天界面需要通过发送按钮推送群聊消息消息以及私聊消息。 代码实现 Client import com.linxc.bio.instant_message.util.Constants; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.DataOutputStream; import java.net.Socket; public class ClientChat implements ActionListener {//设计界面private JFrame win new JFrame();//消息内容框架public JTextArea smsContent new JTextArea(23,50);//发送消息的框private JTextArea smsSend new JTextArea(4,40);//在线人数的区域public JListString onlineUsers new JList();//是否私聊按钮private JCheckBox isPrivateBn new JCheckBox(私聊);//消息按钮private JButton sendBn new JButton(发送);//登录界面private JFrame loginView;private JTextField ipEt, nameEt, idEt;private Socket socket;public static void main(String[] args) {new ClientChat().initView();}private void initView() {/** 初始化聊天窗口的界面 */win.setSize(650, 600);/** 展示登录界面 */displayLoginView();}private void displayChatView() {JPanel bottomPanel new JPanel(new BorderLayout());//将消息框和按钮添加到窗口的底端win.add(bottomPanel, BorderLayout.SOUTH);bottomPanel.add(smsSend);JPanel btns new JPanel(new FlowLayout(FlowLayout.LEFT));btns.add(sendBn);btns.add(isPrivateBn);bottomPanel.add(btns, BorderLayout.EAST);//-----------------------------------------------//给发送消息按钮绑定点击事件监听器//将展示消息区centerPanel添加到窗口的中间smsContent.setBackground(new Color(0xdd, 0xdd, 0xdd));//让展示消息区可以滚动win.add(new JScrollPane(smsContent), BorderLayout.CENTER);smsContent.setEditable(false);//-------------------------------------------------//用户列表和是否私聊放到窗口的最右边Box rightBox new Box(BoxLayout.Y_AXIS);onlineUsers.setFixedCellWidth(120);onlineUsers.setVisibleRowCount(13);rightBox.add(new JScrollPane(onlineUsers));win.add(rightBox, BorderLayout.EAST);//-------------------------------------------------//关闭窗口退出当前程序win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);win.pack();//swing 加上这句 就可以拥有关闭窗口的功能/** 设置窗口居中显示出来 */setWindowCenter(win, 650, 600, true);//发送按钮绑定点击事件sendBn.addActionListener(this);}private void displayLoginView() {/*** 先让用户进行登录* 服务端ip* 用户名* id*//** 显示一个qq的登录框 */loginView new JFrame(登录);loginView.setLayout(new GridLayout(3, 1));loginView.setSize(400, 230);JPanel ip new JPanel();JLabel label new JLabel(IP:);ip.add(label);ipEt new JTextField(20);ip.add(ipEt);loginView.add(ip);JPanel name new JPanel();JLabel label1 new JLabel(姓名:);name.add(label1);nameEt new JTextField(20);name.add(nameEt);loginView.add(name);JPanel btnView new JPanel();JButton login new JButton(登录);btnView.add(login);JButton cancle new JButton(取消);btnView.add(cancle);loginView.add(btnView);//关闭窗口退出当前程序loginView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setWindowCenter(loginView, 400, 260, true);/** 给登录和取消绑定点击事件 */login.addActionListener(this);cancle.addActionListener(this);}private void setWindowCenter(JFrame frame, int width, int height, booleanflag) {/** 得到所在系统所在屏幕的宽高 */Dimension ds frame.getToolkit().getScreenSize();/** 拿到电脑的宽 */int width1 ds.width;/** 高 */int height1 ds.height;System.out.println(width1 * height1);/** 设置窗口的左上角坐标 */frame.setLocation(width1 / 2 - width / 2, height1 / 2 - height / 2);frame.setVisible(flag);}Overridepublic void actionPerformed(ActionEvent e) {//得到点击的事件源JButton btn (JButton) e.getSource();switch (btn.getText()) {case 登录:String ip ipEt.getText().toString();String name nameEt.getText().toString();//校验参数是否为空//错误提示String msg ;//12.1.2.0// \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\if (ip null ||!ip.matches(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})) {msg 请输入合法的服务器ip地址;} else if (name null || !name.matches(\\S{1,})) {msg 姓名必须1个字符以上;}if (!msg.equals()) {//msg有内容说明参数有为空//参数一弹出放到那个窗口里面JOptionPane.showMessageDialog(loginView, msg);} else {try {//参数都合法了//当前登录的用户去服务器登录/** 先把当前用户的名称展示到界面*/win.setTitle(name);//去服务端登录连接一个socket管道socket new Socket(ip, Constants.PORT);//为客户端的socket分配一个线程 专门负责收消息new ClientReader(this, socket).start();//带上用户信息过去DataOutputStream dos newDataOutputStream(socket.getOutputStream());dos.writeInt(1);//登录消息dos.writeUTF(name.trim());dos.flush();//关闭当前窗口 弹出聊天界面loginView.dispose();//登录窗口销毁displayChatView();//展示了聊天窗口了} catch (Exception e1) {e1.printStackTrace();}}break;case 取消:/** 退出系统*/System.exit(0);break;case 发送://得到发送消息的内容String msgSend smsSend.getText().toString();if (!msgSend.trim().equals()) {//发消息给服务端try {//判断是否对谁发消息String selectName onlineUsers.getSelectedValue();int flag 2;// 群发 消息if (selectName ! null !selectName.equals()) {msgSend ( selectName , msgSend);//判断是否选中私发if (isPrivateBn.isSelected()) {//私发flag 3;//私发消息}}DataOutputStream dos new DataOutputStream(socket.getOutputStream());dos.writeInt(flag);//群发消息 发送给所有人dos.writeUTF(msgSend);if (flag 3) {//告诉服务器端我对谁私发dos.writeUTF(selectName.trim());}dos.flush();} catch (Exception e1) {e1.printStackTrace();}}smsSend.setText(null);break;}} }ClientReader import com.linxc.bio.instant_message.util.Constants; import java.io.DataInputStream; import java.net.Socket; import java.util.Arrays; public class ClientReader extends Thread {private Socket socket;private ClientChat clientChat;public ClientReader(ClientChat clientChat, Socket socket) {this.clientChat clientChat;this.socket socket;}Overridepublic void run() {try {DataInputStream dis new DataInputStream(socket.getInputStream());/** 循环一直等待客户端的消息 */while (true) {/** 读取当前的消息类型 登录群发私聊消息 */int flag dis.readInt();if (flag 1) {//在线人数消息回来了String nameDatas dis.readUTF();System.out.println(nameDatas);//展示到在线人数的界面String[] names nameDatas.split(Constants.SPLIT);System.out.println(Arrays.toString(names));clientChat.onlineUsers.setListData(names);} else if (flag 2) {//群发私聊 消息 都是直接显示的String msg dis.readUTF();clientChat.smsContent.append(msg);//让消息界面滚动到低端clientChat.smsContent.setCaretPosition(clientChat.smsContent.getText().length());}}} catch (Exception e) {e.printStackTrace();}} }我的博客NIO专题一
http://www.w-s-a.com/news/113763/

相关文章:

  • 建设通同类网站网站设计公司种类
  • 台州专业做网站网站可以个人做吗
  • 个人logo在线生成免费乐陵德州seo公司
  • 网站回答问题app怎么做专业定制网红柴火灶
  • 网站做的最好的公司行业网址大全
  • 内网怎么做网站服务器seo统计
  • 丽水市企业网站建设 微信营销 影视拍摄计算机专业吃香吗
  • 龙岗做网站公司哪家好找到做网站的公司
  • 网站图片alt属性wordpress 自定义栏目 调用
  • 怎样建网站最快广州网站建设工程
  • iis7 网站404错误信息12306网站很难做吗
  • 网站建设600元包公司设计图片大全
  • 网站建设费用怎么做分录做校园网站代码
  • 网站改版做重定向福州网站建设思企
  • 网站建设全流程企业形象网站开发业务范畴
  • wordpress无法查看站点西安优秀高端网站建设服务商
  • 固始网站制作熟悉免费的网络营销方式
  • 做网站到a5卖站赚钱搜索引擎优化代理
  • 沈阳网站建设包括win10优化
  • 做百度手机网站点击软网站seo优化徐州百度网络
  • 徐州专业网站制作标志设计作业
  • 自己可以做网站空间吗海天建设集团有限公司网站
  • 教学督导网站建设报告aspcms网站图片不显示
  • 网站开发公司成本是什么门户网站宣传方案
  • 上海 企业网站建设网站怎么开通微信支付
  • 饮料网站建设wordpress主题猫
  • 网站建设需要编码不有没有专门的网站做品牌授权的
  • 做爰在线网站免费空间列表
  • 网站外链建设工作总结郑州网站建设扌汉狮网络
  • 建设企业网站的需要多长时间网站使用说明书模板