惠州做棋牌网站建设多少钱,阿里巴巴电脑版,国内ui做的好的网站有哪些,网页自动跳转JAVA BIO深入剖析1. Java BIO 基本介绍2. Java BIO 工作机制3. 传统BIO编程4. BIO模式下的多发和多收消息5. BIO模式下接收多个客户端6. 伪异步I/O编程7. 基于BIO的文件上传8. Java BIO模式下的端口转发9. 基于BIO模式下的即时通信功能清单简单说明项目启动与演示1. Java BIO 基…
JAVA BIO深入剖析1. Java BIO 基本介绍2. Java BIO 工作机制3. 传统BIO编程4. BIO模式下的多发和多收消息5. BIO模式下接收多个客户端6. 伪异步I/O编程7. 基于BIO的文件上传8. Java BIO模式下的端口转发9. 基于BIO模式下的即时通信功能清单简单说明项目启动与演示1. Java BIO 基本介绍
Java BIO 就是传统的 java io 编程其相关的类和接口在 java.ioBIO(blocking I/O) 同步阻塞服务器实现模式为一个连接一个线程即客户端有连接请求时服务器端就需 要启动一个线程进行处理如果这个连接不做任何事情会造成不必要的线程开销可以通过线程池机制改善(实现多个客户连接服务器).
2. Java BIO 工作机制 对 BIO 编程流程的梳理
服务器端启动一个 ServerSocket注册端口调用accpet方法监听客户端的Socket连接。客户端启动 Socket 对服务器进行通信默认情况下服务器端需要对每个客户 建立一个线程与之通讯
3. 传统BIO编程
网络编程的基本模型是Client/Server模型也就是两个进程之间进行相互通信其中服务端提供位置信绑定IP地址和端口客户端通过连接操作向服务端监听的端口地址发起连接请求基于TCP协议下进行三次握手连接连接成功后双方通过网络套接字Socket进行通信。 传统的同步阻塞模型开发中服务端ServerSocket负责绑定IP地址启动监听端口客户端Socket负责发起连接操作。连接成功后双方通过输入和输出流进行同步阻塞式通信。 基于BIO模式下的通信客户端 - 服务端是完全同步完全耦合的。
/**目标: Socket网络编程。Java提供了一个包java.net下的类都是用于网络通信。Java提供了基于套接字端口Socket的网络通信模式我们基于这种模式就可以直接实现TCP通信。只要用Socket通信那么就是基于TCP可靠传输通信。功能1客户端发送一个消息服务端接口一个消息通信结束创建客户端对象1创建一个Socket的通信管道请求与服务端的端口连接。2从Socket管道中得到一个字节输出流。3把字节流改装成自己需要的流进行数据的发送创建服务端对象1注册端口2开始等待接收客户端的连接,得到一个端到端的Socket管道3从Socket管道中得到一个字节输入流。4把字节输入流包装成自己需要的流进行数据的读取。Socket的使用构造器public Socket(String host, int port)方法 public OutputStream getOutputStream()获取字节输出流public InputStream getInputStream() :获取字节输入流ServerSocket的使用构造器public ServerSocket(int port)小结通信是很严格的对方怎么发你就怎么收对方发多少你就只能收多少*/public class Server {public static void main(String[] args){System.out.println(服务端 启动 );try {//1.定义一个ServerSocket对象进行服务端的端口注册ServerSocket serverSocket new ServerSocket(9999);//2.监听客户端的连接请求Socket socket serverSocket.accept();//3.从socket管道中得到字节数入流对象InputStream is socket.getInputStream();//4.把字节输入流包装成字符输入流,再包装成缓冲字符输入流BufferedReader br new BufferedReader(new InputStreamReader(is));//5.打印接收数据String msg;while ((msg br.readLine()) ! null){System.out.println(服务端接收到msg);}} catch (IOException e) {e.printStackTrace();}}
}public class Client {public static void main(String[] args) {try {//1.创建socket对象请求服务器的连接Socket socket new Socket(127.0.0.1,9999);//2.从socket中获取字节输出流OutputStream os socket.getOutputStream();//3.把字节输出流包装成打印流PrintStream ps new PrintStream(os);//4.向服务端发数据ps.print(hello! i am from client !); // 没有换行符客户端执行完socket断掉服务端还没读取到一行发现客户端socket断了只能抛出异常了。//ps.println(hello! i am from client !); // 有换行符客户端发送完socket断掉服务器读取到一行数据输出完正常断掉socket。ps.flush();} catch (IOException e) {e.printStackTrace();}}
}小结
在以上通信中服务端会一致等待客户端的消息如果客户端没有进行消息的发送服务端将一直进入阻塞状态。同时服务端是按照行获取消息的这意味着客户端也必须按照行进行消息的发送否则服务端将进入等待消息的阻塞状态
4. BIO模式下的多发和多收消息
/*** 目标* 服务端可以反复接收消息* 客户端可以反复发送消息*/
public class Server {public static void main(String[] args){System.out.println(服务端 启动 );try {//1.定义一个ServerSocket对象进行服务端的端口注册ServerSocket serverSocket new ServerSocket(9999);//2.监听客户端的连接请求Socket socket serverSocket.accept();//3.从socket管道中得到字节数入流对象InputStream is socket.getInputStream();//4.把字节输入流包装成字符输入流,再包装成缓冲字符输入流BufferedReader br new BufferedReader(new InputStreamReader(is));//5.打印接收数据String msg;while ((msg br.readLine()) ! null){System.out.println(服务端接收到msg);}} catch (IOException e) {e.printStackTrace();}}
}public class Client {public static void main(String[] args) {try {//1.创建socket对象请求服务器的连接Socket socket new Socket(127.0.0.1,9999);//2.从socket中获取字节输出流OutputStream os socket.getOutputStream();//3.把字节输出流包装成打印流PrintStream ps new PrintStream(os);//4.向服务端发数据Scanner sc new Scanner(System.in);while (true){System.out.print(请输入);String msg sc.nextLine();ps.println(msg);ps.flush();}} catch (IOException e) {e.printStackTrace();}}
}结果
5. BIO模式下接收多个客户端
在上述的案例中一个服务端只能接收一个客户端的通信请求那么如果服务端需要处理很多个客户端的消息通信请求应该如何处理呢此时我们就需要在服务端引入线程了也就是说客户端每发起一个请求服务端就创建一个新的线程来处理这个客户端的请求这样就实现了一个客户端一个线程的模型图解模式如下
/*** 目标服务端可以同时接收多个客户端的socket通信连接* 思路服务端每接收到一个客户端socket连接请求对象之后交给一个独立的线程去处理数据交互*/
public class Server {public static void main(String[] args){System.out.println(服务端 启动 );try {//1.端口注册ServerSocket serverSocket new ServerSocket(9999);//2.不断接收客户端连接请求while (true){Socket socket serverSocket.accept();new ServerThreadReader(socket).start();}} catch (IOException e) {e.printStackTrace();}}
}public class ServerThreadReader extends Thread{private Socket socket;public ServerThreadReader(Socket socket){this.socket socket;}Overridepublic void run() {try {//3.从socket管道中得到字节数入流对象InputStream is socket.getInputStream();//4.把字节输入流包装成字符输入流,再包装成缓冲字符输入流BufferedReader br new BufferedReader(new InputStreamReader(is));//5.打印接收数据String msg;while ((msg br.readLine()) ! null){System.out.println(服务端接收到msg);}} catch (IOException e) {e.printStackTrace();}}
}客户端同上 小结
1.每个Socket接收到都会创建一个线程线程的竞争、切换上下文影响性能2.每个线程都会占用栈空间和CPU资源3.并不是每个socket都进行IO操作无意义的线程处理4.客户端的并发访问增加时。服务端将呈现1:1的线程开销访问量越大系统将发生线程栈溢出线程创建失败最终导致进程宕机或者僵死从而不能对外提供服务。
6. 伪异步I/O编程
在上述案例中客户端的并发访问增加时。服务端将呈现1:1的线程开销访问量越大系统将发生线程栈溢出线程创建失败最终导致进程宕机或者僵死从而不能对外提供服务。
接下来我们采用一个伪异步I/O的通信框架采用线程池和任务队列实现当客户端接入时将客户端的Socket封装成一个Task(该任务实现java.lang.Runnable线程任务接口)交给后端的线程池中进行处理。JDK的线程池维护一个消息队列和N个活跃的线程对消息队列中Socket任务进行处理由于线程池可以设置消息队列的大小和最大线程数因此它的资源占用是可控的无论多少个客户端并发访问都不会导致资源的耗尽和宕机。
图示如下:
/*** 目标服务端可以同时接收多个客户端的socket通信连接* 思路服务端每接收到一个客户端socket连接请求对象之后交给一个独立的线程去处理数据交互*/
public class Server {public static void main(String[] args){System.out.println(服务端 启动 );try {//端口注册ServerSocket serverSocket new ServerSocket(9999);//初始化线程池对象HandlerSocketServerPool pool new HandlerSocketServerPool(5,10);while (true){//接收客户端连接请求Socket socket serverSocket.accept();//把socket封装成任务对象Runnable target new ServerRunnableTarget(socket);//把任务交给线程池执行pool.execute(target);}} catch (IOException e) {e.printStackTrace();}}
}public class HandlerSocketServerPool {//线程池对象private ExecutorService executorService;/*** 初始化线程池对象* int corePoolSize,* int maximumPoolSize,* long keepAliveTime,* TimeUnit unit,* BlockingQueueRunnable workQueue)*/public HandlerSocketServerPool(int maximumPoolSize, int queueSize){executorService new ThreadPoolExecutor(3, maximumPoolSize,120, TimeUnit.SECONDS,new ArrayBlockingQueueRunnable(queueSize));}//提交任务给队列暂存等线程池来处理public void execute(Runnable target){executorService.execute(target);}
}public class ServerRunnableTarget implements Runnable{private Socket socket;public ServerRunnableTarget(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();}}
}客户端同上
由于核心线程数是3所以当第四个客户端连接进去并发送消息的时候服务器并不能开启新线程处理而是将它放到队列中当之前的客户端连接断开之后就会按队列中的先后顺序去处理客户端连接。
小结
伪异步io采用了线程池实现因此避免了为每个请求创建一个独立线程造成线程资源耗尽的问题但由于底层依然是采用的同步阻塞模型因此无法从根本上解决问题。如果单个消息处理的缓慢或者服务器线程池中的全部线程都被阻塞那么后续socket的i/o消息都将在队列中排队。新的Socket请求将被拒绝客户端会发生大量连接超时。
7. 基于BIO的文件上传
/*** 客户端上传任意类型的文件*/
public class Client {public static void main(String[] args) {try {//1.创建socket对象请求服务器的连接Socket socket new Socket(127.0.0.1,9999);//2.从socket中获取字节输出流把字节输出流包装成数据输出流DataOutputStream dos new DataOutputStream(socket.getOutputStream());//3.把上传文件的后缀给服务器dos.writeUTF(.png);//4.把文件数据发送给服务器InputStream is new FileInputStream(/Users/lilianyun/IdeaProjects/IOTest2/src/com/BIO/demo05/clientFile/music.png);//5.向服务端发数据byte[] buffer new byte[1024];int len;while ((len is.read(buffer)) 0){dos.write(buffer,0,len);}dos.flush();dos.close();} catch (IOException e) {e.printStackTrace();}}
}/*** 服务端接收任意类型的数据并保存到磁盘*/
public class Server {public static void main(String[] args){System.out.println(服务端 启动 );try {//1.端口注册ServerSocket serverSocket new ServerSocket(9999);//2.不断接收客户端连接请求while (true){Socket socket serverSocket.accept();//3.交给独立线程处理与客户端的文件通信new ServerThreadReader(socket).start();}} catch (IOException e) {e.printStackTrace();}}
}public class ServerThreadReader extends Thread{private Socket socket;public ServerThreadReader(Socket socket){this.socket socket;}Overridepublic void run() {try {//4.从socket管道中得到字节数入流对象,包装成数据输入流DataInputStream dis new DataInputStream(socket.getInputStream());//5.读取客户端发来的文件类型String suffix dis.readUTF();//6.定义字节输出管道将客户端发来的文件写出去OutputStream os new FileOutputStream(/Users/lilianyun/IdeaProjects/IOTest2/src/com/BIO/demo05/serverFile/ UUID.randomUUID().toString()suffix);//7.从数据输入流中读取数据写到字节输出流中去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) {e.printStackTrace();}}
}小结
客户端怎么发服务端就怎么接收
8. Java BIO模式下的端口转发
需求一个客户端发送的消息可以发送给所有客户端接收群聊思想
9. 基于BIO模式下的即时通信
基于BIO模式下的即时通信我们需要解决客户端到客户端的通信也就是需要实现客户端与客户端的端口消息转发逻辑。
功能清单简单说明 客户端登陆功能 可以启动客户端进行登录客户端登陆只需要输入用户名和服务端ip地址即可。 在线人数实时更新 客户端用户户登陆以后需要同步更新所有客户端的联系人信息栏。 离线人数更新 检测到有客户端下线后需要同步更新所有客户端的联系人信息栏。 群聊 任意一个客户端的消息可以推送给当前所有客户端接收。 私聊 可以选择某个员工点击私聊按钮然后发出的消息可以被该客户端单独接收。 消息 可以选择某个员工然后发出的消息可以该用户但是其他所有人都能 消息用户和消息时间点 服务端可以实时记录该用户的消息时间点然后进行消息的多路转发或者选择。
项目启动与演示