Socket是最基础的网络编程技术,HttpURLConnection和HttpClient都是基于Socket的。下面来看一个从服务器端下载文件到客户端的例子。
服务器端:
package org.huodong.action; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class FilmServer { public static void main(String[] args) { FilmServer ms = new FilmServer(); try { ms.server(); } catch (Exception e) { e.printStackTrace(); } } /** * 服务器端响应请求 标签: * * @throws Exception */ public void server() throws Exception { // 0.建立服务器端的server的socket ServerSocket ss = new ServerSocket(8089); while (true) { // 1.打开socket连接 // 等待客户端的请求 final Socket server = ss.accept(); //阻塞,一直等待直到有客户端的请求过来,如果客户端是两个线程的话,那么一定是一个线程的客户端的连接关闭了才能接到另一个线程的客户端的请求。 System.out.println("服务-----------请求开始start"); // 2.打开socket的流信息,准备下面的操作 final InputStream is = server.getInputStream(); byte b[] = new byte[1024]; int readCount = is.read(b); String str = new String(b); str = str.trim(); final String serverFileName = str; // 3.对流信息进行读写操作 System.out.println("客户端传过来的信息是:" + str); System.out.println("线程" + Thread.currentThread().getName() + "启动"); try { FileInputStream fileInputStream = new FileInputStream( serverFileName); OutputStream os = server.getOutputStream(); // 往客户端写文件 byte[] bfile = new byte[1024]; while (fileInputStream.read(bfile) > 0) { os.write(bfile); } fileInputStream.close(); os.close(); // 4.关闭socket // 先关闭输入流 is.close(); // 最后关闭socket server.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("服务-----------请求结束over"); } } }
客户端:
package org.huodong.action; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; import java.util.Date; public class FilmClient{ public static void main(String[] args) { for (int i = 1; i <= 2; i++) { Client client = new Client(); client.i = i; client.start(); } } } class Client extends Thread { int i; @Override public void run() { Date date = new Date(); // 1.建立scoket连接 Socket client; try { client = new Socket("127.0.0.1", 8089); // 2.打开socket的流信息,准备下面的操作 OutputStream os = client.getOutputStream(); // 3.写信息 os.write(("d://film//音乐.rar").getBytes()); //这个是服务器端的文件地址 String filmName = "e://io"+i+".rar"; //这是要下载到客户端的地址及文件名,这里相当于下载了两遍2.rmvb,只不过保存在客户端的时候起了两个不同的文件名,方便比较 FileOutputStream fileOutputStream = new FileOutputStream(filmName); System.out.println("Time="+date.getTime()); InputStream is = client.getInputStream();// 接收服务器端的文件并写到客户端,这里会一直等服务器端发消息过来,如果服务器sleep10秒才发送过来,客户端也会一直等, // 这就导致整个线程都会阻塞在这里 byte b[] = new byte[1024]; while(is.read(b)>0){ fileOutputStream.write(b); } // 4.关闭socket // 先关闭输出流 os.close(); // 最后关闭socket client.close(); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("Time1="+date.getTime()); } }
客户端启动了2个线程进行下载电影的工作,先启动服务端,再运行客户端,会看笔者本地的硬盘C分区到有如下效果。
可以看到线程2的下载任务一直是0字节,等第一个线程下载完成后呢,线程2的下载任务才能进行。
服务端的代码造成的问题就是使用传统的sokect网络通讯,那么另一个客户端的线程请求到server端的时候就发生了阻塞的情况,也就是说,服务端相当一个厕所,厕所就有只有一个坑位,来了一个人,相当于客户端请求,那这个人相当于就把坑位给占了,write操作和read操作会阻塞,这个人还没解决完问题呢,下个人就来了,没办法,哥们儿先在门外等等啊,等前一个客户爽完了再给您提供服务好吧。那么如何解决这个占着坑位不让别人用的情况呢?
3. 阻塞的多线程
为了解决以上问题,那么之后很多Server肯定不可能像以上程序那么做,不过以前很多Server都是基于单线程服务改造一下,做成多线程的Server的通讯,修改一下上面的Server代码,如下
package org.huodong.action; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * 以上的Server就是在原始的socket基础上加了线程,每一个Client请求过来后,整个Server主线程不必处于阻塞状态, * 接收请求后直接另起一个新的线程来处理和客户端的交互,就是往客户端发送二进制包。这个在新线程中虽然阻塞, * 但是对于服务主线程没有阻塞的影响,主线程依然通过死循环监听着客户端的一举一动。 * 另一个客户端的线程发起请求后就再起一个新的线程对象去为客户端服务。 */ public class FilmServerNewThread { public static void main(String[] args) { FilmServerNewThread ms = new FilmServerNewThread(); try { ms.server(); } catch (Exception e) { e.printStackTrace(); } } /** * 服务器端响应请求 * * @throws Exception */ public void server() throws Exception { // 0.建立服务器端的server的socket ServerSocket ss = new ServerSocket(8089); while (true) { // 1.打开socket连接 // 等待客户端的请求 final Socket server = ss.accept(); System.out.println("服务-----------请求开始start"); // 2.打开socket的流信息,准备下面的操作 final InputStream is = server.getInputStream(); byte b[] = new byte[1024]; int readCount = is.read(b); String str = new String(b); str = str.trim(); final String serverFileName = str; // 3.对流信息进行读写操作 System.out.println("客户端传过来的信息是:" + str); if (readCount > 0) { new Thread() { @Override public void run() { System.out.println("线程" + Thread.currentThread().getName() + "启动"); try { FileInputStream fileInputStream = new FileInputStream( serverFileName); // 3.1 服务器回复客户端信息(response) OutputStream os = server.getOutputStream(); byte[] bfile = new byte[1024]; // 往客户端写 while (fileInputStream.read(bfile) > 0) { os.write(bfile); } fileInputStream.close(); os.close(); // 4.关闭socket // 先关闭输入流 is.close(); // 最后关闭socket server.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }.start(); } System.out.println("服务-----------请求结束over"); } } }
执行效果如下
2个线程互不影响,各自下载各自的。当然从非常严格的意义来讲,str变量在十分高并发的情况下有线程安全问题,这个咱暂且忽略,就着眼于低并发的情况。这个问题是什么呢,就是如果客户端请求比较多了,那么为每一个客户端开辟一个新的线程对象来处理网络传输的请求,需要创建个线程对象,而且这个线程对象从时间上来讲还是处于长连接,这个就比较消费系统资源,这个打开进程管理器就可以看到。而且每一个线程内部都是阻塞的,也没有说完全利用好这个新创建的线程。还拿刚才上厕所举例子,好比现在不止一个坑位了,来了一个用户我这边就按照工程师的厕所坑位图建立一个新的坑位,客户来了,不用等待老坑位,用新创建的坑位就行了。等那个老坑位用完了,自然有垃圾回收器去消灭那个一次性的坑位的,腾出资源位置为了建立新的坑位。长时间连接的意思,相当于这个人上厕所的时间非常长,便秘??需要拉一天才能爽完……老的坑位一时半会儿回收不了,新的坑位需要有空间为其建造茅房以便满足客户端的“急切方便”需要。久而久之,线程数目一多,系统就挂了的概率就增多了(谁也别想上,全玩完了)。
NIO方式与BIO(Socket)的方式的最大区别就是NIO是一请求一线程,而BIO是一连接一线程,后者就导致如果服务器未来得及响应客户端就要一直等一直等,该连接就一直占用着这个线程。如果是NIO的话则可以复用连接,也就是无须等待服务器端的响应就可以继续给服务器端发送消息,发送和接收是异步响应的。
http和socket 区别
5、Socket连接与HTTP连接
由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网 络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。
而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。
很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数 据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求, 不仅可以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。
相关推荐
用于博文https://blog.csdn.net/lyz_zyx/article/details/104062815《Android网络编程(十四) 之 Socket与NIO》中演示Socket与NIO使用的Demo
在Android开发中,Socket通信是应用层与传输层之间的接口,用于实现设备间的网络通信。传统的Socket通信通常基于BIO(Blocking I/O)模型,但随着高性能和高并发需求的增加,开发者开始转向NIO(Non-blocking I/O)...
总结来说,这个"网络编程(socket、NIO、mina)---demo"涵盖了网络编程的基础与进阶,从基础的Socket通信,到提高性能的NIO,再到高级的Mina框架,这些都是开发分布式系统、网络服务和实时通信应用不可或缺的技术。...
Socket通信在IT行业中是网络编程的基础,特别是在Java领域,它提供了服务器与客户端间进行数据交换的接口。NIO(Non-blocking I/O)是Java提供的一个高效I/O模型,相较于传统的IO模型,NIO具有非阻塞、多路复用等...
**Socket编程在NIO中的应用**: 1. **ServerSocketChannel**:用于监听客户端连接,通过调用`ServerSocketChannel.open()`创建,然后绑定到指定的IP和端口,并调用`accept()`方法接收客户端连接。 2. **...
**RPC(Remote Procedure Call)与Socket** RPC是一种远程调用技术,使得客户端可以像调用本地方法一样调用远程服务器上的方法。在Socket通信中,RPC可以借助于序列化和反序列化机制将方法调用和参数转换成可传输的...
《NIO与Socket编程技术指南》是一本深入探讨Java NIO(New Input/Output)和Socket编程的专业书籍,由高洪岩撰写。本书主要针对Java开发者,旨在帮助他们理解和掌握这两种在开发网络应用中至关重要的技术。 Java ...
与BIO不同,NIO允许一个线程处理多个连接,提高了服务器的并发能力。在NIO模式下,当读写操作没有准备好时,不会阻塞,而是返回一个状态值,从而降低了对系统资源的消耗。 **NIO示例:** ```java Selector ...
然后,我们引入NIO,它是Java 1.4版本引入的新特性,与传统的阻塞I/O模型相比,NIO的核心在于通道(Channel)和选择器(Selector)。通道是可以读写数据的连接,而选择器则可以监视多个通道的状态变化,例如连接建立...
在探讨如何使用Java NIO实现Socket通信之前,我们需要先理解NIO(Non-blocking I/O,非阻塞I/O)与传统阻塞I/O之间的区别。 **传统阻塞I/O模型**:在传统的Java IO编程中,当我们调用`read()`或`write()`方法时,...
本文将通过一个对比实例,探讨一般Socket客户端与Mina NIO (Non-blocking I/O) Socket客户端的差异和特点,帮助开发者理解这两种技术在实际应用中的选择。 首先,普通Socket客户端基于BIO(Blocking I/O)模型,它...
基于NIO的socket举例 基于NIO的socket举例 基于NIO的socket举例 基于NIO的socket举例 基于NIO的socket举例基于NIO的socket举例 基于NIO的socket举例
`Socket`在NIO中的实现是`SocketChannel`,它代表了网络上的一个连接。`ServerSocketChannel`则用于监听客户端的连接请求。通过`ServerSocketChannel`的`accept()`方法,服务器可以接收新的客户端连接,然后将其注册...
非常详细地讲解了NIO中的缓冲区、通道、选择器、编码,以及使用Socket技术实现TCP/IP和UDP编程,细化到了演示全部SocketOption的特性,这对理解基于NIO和Socket技术为基础所开发的NIO框架是非常有好处的,本书以案例...
与`Socket`不同的是,`SocketChannel`是非阻塞的,这意味着当没有数据可读或无法写入时,线程不会被阻塞,而是可以执行其他任务。 2. **ServerSocketChannel**:对应于服务端的监听通道,它用于代替`ServerSocket`...
Socket NIO 单 Reactor 模式是一种在 Java 中实现高性能网络编程的技术,它结合了非阻塞I/O(New I/O,即NIO)和Reactor设计模式。本示例代码旨在帮助开发者理解如何使用Java NIO和Reactor模式构建网络服务。尽管...
在Java编程中,NIO(New Input/Output)提供了一种不同于传统IO模型的I/O操作方式,其核心特点是能够进行多路复用,即一个线程可以同时处理多个连接,这使得NioSocket在高并发场景下具有较好的性能表现。 服务器端...
它与传统的BIO(Blocking IO)模型不同,NIO提供了更高效的数据读写方式,特别适合于高并发、低延迟的网络应用,如Socket服务器的构建。本篇将基于给定的标题“采用NIO实现一个Socket服务器”来详细阐述如何使用NIO...
使用NIO socket不需要多线程来处理多个连接的请求,效率非常高 可以作为NIO socket入门的例子,Reactor模式,重点理解key.attach, jar文件里包含了源代码 1,运行server.bat启动服务器,可以打开编辑,修改端口号 ...