Java中的Socket的用法——Socket、NioSocket

一、Java Socket的分类

Java中的Socket分为普通的Socket和NioSocket。

二、普通Socket

Java中的网络通信时通过Socket实现的,Socket分为ServerSocket和Socket两大类,ServerSocket用于服务器端,可以通过accept方法监听请求,监听请求后返回Socket,Socket用于完成具体数据传输,客户端也可以使用Socket发起请求并传输数据。ServerSocket的使用可以分为三步:

创建ServerSocket。ServerSocket的构造方法有5个,其中最方便的是ServerSocket(int port),只需要一个port就可以了。
调用创建出来的ServerSocket的accept方法进行监听。accept方法是阻塞方法,也就是说调用accept方法后程序会停下来等待连接请求,在接受请求之前程序将不会继续执行,当接收到请求后accept方法返回一个Socket。
使用accept方法返回的Socket与客户端进行通信
  如下代码,我们在服务器端创建ServerSocket,并调用accept方法监听Client的请求,收到请求后返回一个Socket。为了方便用浏览器测试,响应是http协议格式。

public class Server {public static void main(String[] args) {// TODO Auto-generated method stubtry {ServerSocket server = new ServerSocket(8080);while (true){Socket socket = server.accept();InputStreamReader in = new InputStreamReader(socket.getInputStream());BufferedReader br =  new BufferedReader(in);String content;System.out.println(1);//while ((content = br.readLine()) != null && content != ""){ //用content != ""或者不判断content是否为空,会导致阻塞while ((content = br.readLine()) != null && !"".equals(content)){System.out.println(content);}System.out.println(2);PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);printWriter.println("HTTP/1.1 200 OK");printWriter.println("Content-Type:text/html;charset=utf-8");String body = "hello,nio1";printWriter.println("Content-Length:" + body.getBytes().length);printWriter.println();printWriter.println(body);printWriter.close();socket.close();}        } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

然后我们再看看客户端的Socket代码,Socket的使用也是一样,首先创建一个Socket,Socket的构造方法非常多,这里用的是Socket(String host, int port),把目标主机的地址和端口号传入即可ServerSocket和Client在同一主机下,那么Client中的IP地址需要更改为:127.0.0.1,Socket创建的过程就会跟服务器端建立连接,创建完Socket后,再创建Writer和Reader来传输数据,数据传输完成后释放资源关闭连接。

public class Client {public static void main(String[] args) {//创建客户端socket建立连接,指定服务器地址和端口try {//Socket socket = new Socket("www.jianshu.com",80);Socket socket = new Socket("127.0.0.1",8080);//获取输出流,向服务器端发送信息OutputStream outputStream = socket.getOutputStream();//字节输出流PrintWriter pw = new PrintWriter(outputStream); //将输出流包装为打印流pw.write("用户名:admin;密码:123");pw.flush();socket.shutdownOutput();//获取输入流,读取服务器端的响应InputStream inputStream = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));String info = null;while((info = br.readLine())!=null){//System.out.println("我是客户端,服务器说:"+info);System.out.println(info);}socket.shutdownInput();//关闭资源br.close();inputStream.close();pw.close();outputStream.close();socket.close();} catch (IOException e) {e.printStackTrace();}}
}

二、NioSocket的用法

从JDK1.4开始,Java增加了新的IO模式-nio(new IO),nio在底层采用了新的处理方式,极大提高了IO的效率。我们使用的Socket也是IO的一种,nio提供了相应的工具:ServerSocketChannel和SocketChannel,他们分别对应原来的ServerSocket和Socket。

在了解NioSocket之前我们先了解Buffer、Channel、Selector。为了方便理解,我们来看个例子,要过圣诞节了,需要给同学们发贺卡和苹果,班长这时候又是最辛苦的,每次拿一个苹果和一张贺卡发给一个同学,发送完成后回来再取一张贺卡和一个苹果发给另一个同学,直到全班同学都拿到贺卡和苹果为止,这就是普通Socket处理方式,来一个请求,ServerSocket就进行处理,处理完成后继续接受请求,这种方式效率很低啊!还是圣诞节的例子,班长发现班委不止他一个,就通知了生活委员(女)和组织委员(男)来帮助他发贺卡和苹果,女生的贺卡是粉色的,男生的贺卡是蓝色的,生活委员负责从全班的贺卡中挑选女生的贺卡,而组织委员则负责男生的贺卡,然后生活委员和组织委员分别以宿舍为单位通知宿舍长来领取宿舍同学的贺卡和苹果,班长将圣诞节发苹果和贺卡的工作布置给两个班委后,就可以继续干其他工作了。这就是NioSocket,Buffer就是所有传递的货物,也就是例子中的苹果和贺卡,而Channel就是传递货物的通道,也就是例子中的宿舍长,负责将礼物搬回自己宿舍,而生活委员和组织委员充当了Selector的职责,负责礼物的分拣。

ServerSocketChannel可以使用自己的静态工厂方法open创建,每个ServerSocketChannel对应一个ServerSocket(通过调用其socket()获取),如果直接使用获取的ServerSocket来监听请求,那么还是普通ServerSocket,而通过将获取的ServerSocket绑定端口号来实现NioSocket。ServerSocketChannel可以通过configureBlocking方法来设置是否采用阻塞模式,如果设置为非阻塞模式,就可以调用register方法注册Selector来使用了。

Selector可以通过其静态工厂方法open创建,创建后通过Channel的register方法注册到ServerSocketChannel或者SocketChannel上,注册完成后Selector就可以通过select方法来等待请求,select方法有一个long类型参数,代表最长等待时间,如果在这段时间内收到相应操作的请求则返回可以处理的请求的数量,否则在超时后返回0,如果传入的参数为0或者无参数的重载方法,select方法会采用阻塞模式知道有相应操作请求的出现。当接收到请求后Selector调用selectdKeys方法返回SelectionKey集合。

SelectionKey保存了处理当前请求的Channel和Selector,并且提供了不同的操作类型。Channel在注册Selector时可以通过register的第二个参数选择特定的操作(请求操作、连接操作、读操作、写操作),只有在register中注册了相应的操作Selector才会关心相应类型操作的请求。

介绍了这么多估计大家也烦了,我们就来看看服务器端NioSocket的处理过程吧:

1、创建ServerSocketChannel并设置相应的端口号、是否为阻塞模式
2、创建Selector并注册到ServerSocketChannel上
3、调用Selector的selector方法等待请求
4、Selector接收到请求后使用selectdKeys返回SelectionKey集合
5、使用SelectionKey获取到channel、selector和操作类型并进行具体操作。

public class NettyHttpServer {public static void main(String[] args) throws InterruptedException {int port = 8080;EventLoopGroup bossGroup = new NioEventLoopGroup(2);EventLoopGroup workerGroup = new NioEventLoopGroup(16);try {ServerBootstrap b = new ServerBootstrap();b.option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_KEEPALIVE, true).childOption(ChannelOption.SO_REUSEADDR, true).childOption(ChannelOption.SO_RCVBUF, 32 * 1024).childOption(ChannelOption.SO_SNDBUF, 32 * 1024).childOption(EpollChannelOption.SO_REUSEPORT, true).childOption(ChannelOption.SO_KEEPALIVE, true).childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new HttpInitializer());Channel ch = b.bind(port).sync().channel();System.out.println("开启netty http服务器,监听地址和端口为 http://127.0.0.1:" + port + '/');ch.closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public class HttpInitializer extends ChannelInitializer<SocketChannel> {@Overridepublic void initChannel(SocketChannel ch) {ChannelPipeline p = ch.pipeline();p.addLast(new HttpServerCodec());//p.addLast(new HttpServerExpectContinueHandler());p.addLast(new HttpObjectAggregator(1024 * 1024));p.addLast(new HttpHandler());}
}
public class HttpHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) {ctx.flush();}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {try {//logger.info("channelRead流量接口请求开始,时间为{}", startTime);FullHttpRequest fullRequest = (FullHttpRequest) msg;String uri = fullRequest.uri();//logger.info("接收到的请求url为{}", uri);if (uri.contains("/test")) {handlerTest(fullRequest, ctx, "hello,kimmking");} else {handlerTest(fullRequest, ctx, "hello,others");}} catch(Exception e) {e.printStackTrace();} finally {ReferenceCountUtil.release(msg);}}private void handlerTest(FullHttpRequest fullRequest, ChannelHandlerContext ctx, String body) {FullHttpResponse response = null;try {String value = body; //            httpGet ...  http://localhost:8801
//            返回的响应,"hello,nio";
//            value = reponse....response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(value.getBytes("UTF-8")));response.headers().set("Content-Type", "application/json");response.headers().setInt("Content-Length", response.content().readableBytes());} catch (Exception e) {System.out.println("处理出错:"+e.getMessage());response = new DefaultFullHttpResponse(HTTP_1_1, NO_CONTENT);} finally {if (fullRequest != null) {if (!HttpUtil.isKeepAlive(fullRequest)) {ctx.write(response).addListener(ChannelFutureListener.CLOSE);} else {response.headers().set(CONNECTION, KEEP_ALIVE);ctx.write(response);}ctx.flush();}}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}

客户端代码通普通Socket一样,Socket socket = new Socket(“127.0.0.1”,8080);表示与服务器端建立连接,从而执行服务器端的handleAccept()方法,给ServerSocketChannel注册selector以及添加SelectionKey.OP_READ参数,表示selector关心读方法。然后通过PrintWrite在客户端将内容发送给服务器端,服务器端执行handleRead方法对接收到的内容进行处理,并将结果返回给客户端,客户端通过BufferedReader接受数据,最后关闭连接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/554803.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringMVC中Controller为什么能够处理并发访问?Springboot中的定时任务是否会发生阻塞?

文章目录SpringMVC中Controller为什么能够处理并发访问&#xff1f;当多个请求同时访问服务器的时候Controller、Service、DAO是线程安全的吗&#xff1f;关于类中的变量Controller、Service、DAO等类都默认为单例模式Controller、Service、DAO等类中的方法当中的并发问题关于D…

Java进阶 - 易错知识点整理

转载&#xff1a;https://blog.csdn.net/qq_33934427/article/details/125903960 文章目录1、JavaEE2、网络基础3、Mysql4、Spring/SpringMVC&#xff08;IOC装配、AOP增强、常用注解&#xff09;5、Spring Boot/Spring Cloud1&#xff09;SpringBoot部分2&#xff09;SpringCl…

如何在高版本谷歌Chrome浏览器中用VLC播放海康、大华RTSP实时视频?

一、背景 随着互联网基础设施的完善以及4G、5G等技术的大规模商用&#xff0c;在Chrome、Firefox、Edge等浏览器播放RTSP视频流也慢慢成为了信息化系统的行业标准。 早些年还可用VLC播放器在网页中播放RTSP视频流&#xff0c;好景不长&#xff0c;2015年Chrome、Firefox等浏览…

MySQL 视图(详解) navicat如何创建视图

文章目录MySQL 视图&#xff08;详解一&#xff0c;视图概念使用视图的原因二&#xff0c;创建视图&#xff08;1&#xff09;基本语法&#xff08;2&#xff09;创建基于单表的视图【实例 1】【实例 2】&#xff08;3&#xff09;创建基于多表的视图【实例 3】&#xff08;4&a…

使用set集合去除重复元素@EqualsAndHashCode注解

如何使用set集合去重 ​ 我们都知道&#xff0c;set集合是无序的&#xff0c;这样也导致set集合里面的元素是不能重复的&#xff0c;因为这一个特性&#xff0c;所以我们经常用set集合进行去重操作&#xff0c;我们下面以一个简单的例子说明set集合是如何进行去重的。 创建去…

BigDecimal 类的 compareTo() 和 equals()方法

BigDecimal 类的 compareTo() 和 equals()方法 1. compareTo()源码 /*** Compares this BigDecimal with the specified BigDecimal. * Two BigDecimal objects that are equal in value but have * a different scale (like 2.0 and 2.00) are considered equal* by this met…

缺少构造方法:Cause java.sql.SQLDataException Unsupported conversion from LONG to java.sql.Timestamp

今天遇到了一个奇怪的错误&#xff0c;报错如下图所示&#xff1a; org.springframework.dao.DataIntegrityViolationException: Error attempting to get column question_id from result set. Cause: java.sql.SQLDataException: Unsupported conversion from LONG to java…

Collectors.reducing总结Collectors.mapping+Collectors.reducing+TreeSet等等

Collectors.reducing总结 1. 方法签名 一个参数 public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op)参数说明 BinaryOperator op 归集操作函数 输入参数T返回T 测试代码 我们这里实现一个简单的求和功能&#xff0…

vim退出快捷键

退出vim的快捷键 不需要进入命令编辑模式 按住shift zz 保存退出 zq 不保存退出&#xff0c;q表示放弃 之所以按住shift&#xff0c;其实是切换大小写 在命令编辑模式下&#xff1a; :q 不保存退出 :q! 不保存强制退出 :wq 保存退出&#xff0c;w表示写入&#xff0c;…

SpringBoot瘦身打包部署

一、前言 最近做的项目由于引入第三方库导致在运行mvn clean package 打jar时&#xff0c;编译出来的 Jar 包很大&#xff08;服务器多达500MB&#xff09;。 二、瘦身前的Jar包 SpringBoot编译出来的Jar包中&#xff0c;磁盘占用大的&#xff0c;是一些外部依赖库&#xff…

XShell直接拖拽文件到服务器,不使用Xftp等文件上传工具

很多情况下&#xff0c;我们使用 Xshell 工具时&#xff0c;如果遇到文件的上传和下载会不可避免的要用到另外一个工具 Xftp&#xff0c;但是频繁的使用 Xftp 会比较麻烦&#xff0c;那么有没有一种更加直接简单的方法呢&#xff1f; 当我们所需要上传的文件比较小的时候&…

System.getProperty()方法获取系统变量

今天在阅读JDBC的DriverManager类源码时&#xff0c;看到了这么一句代码&#xff1a; System.getProperty(“jdbc.drivers”)&#xff1b;getProperty()这个方法是获取指定键指示的系统属性的&#xff0c;也就是说上面的代码获取的是jdbc.drivers这个属性。我写了个测试测试输…

Spring Boot集成Druid异常discard long time none received connection.

Spring Boot集成Druid异常 在Spring Boot集成Druid项目中&#xff0c;发现错误日志中频繁的出现如下错误信息&#xff1a; discard long time none received connection. , jdbcUrl : jdbc:mysql://******? useSSLfalse&allowPublicKeyRetrievaltrue&useUnicodetrue…

局部变量为什么必须赋值才可以使用

在java内存模型中规定&#xff0c;一个新的变量只能在主存中初始化&#xff0c;不允许在工作内存中直接使用一个未被初始化的变量。 工作内存可以理解为局部变量定义的内存区域&#xff0c;也就是线程的工作内存。所谓局部变量就是线程私有的不共享的空间。 类加载准备阶段 类变…

Java 赋值 “=” 讲解

前言 我们从接触java第一天&#xff0c;就是到 是赋值的意思&#xff0c;把等号右边结果的值&#xff0c;赋给等号左边的变量&#xff0c;那具体是怎样赋值呢&#xff1f;你有了解过吗&#xff1f; 1.0版本 大家都知道&#xff0c;java中有 8大基本类型&#xff0c;对于基本…

Linux 系统管理命令:时间、进程、网络、磁盘、关机重启等 top命令用法详解

文章目录系统管理常用命令1. 日期1.1 查看日历: cal1.2 查看/设置时间: date2. 进程2.1 查看进程信息: ps2.2 动态显示进程信息: top2.3 终止进程: kill2.4 服务的管理: service3. 网络3.1 网卡信息查询与配置: ifconfig3.2 检测远程主机连通性: ping3.3 查看网络状态(监听端口…

BigDecimal 加减乘除顺序验证

结论&#xff1a;BigDecimal 与常识的加减乘除计算优先顺序无关联&#xff0c;采用的是方法调用顺序&#xff0c;顺序执行。 public static void main(String[] args) {BigDecimal two new BigDecimal("2");BigDecimal one new BigDecimal("1");System.…

LocalDate详解

LocalDate使用 localDate时间创建方式 /*** localDate时间创建方式* 获取当前时间*/Testpublic void localDateCreate() {LocalDate yyyyMMdd LocalDate.now();LocalTime HHmmssSSS LocalTime.now();LocalDateTime yyyyMMddHHmmssSSS LocalDateTime.now();System.out.print…

Java8中计算时间的四种方式及区别Period、Duration、ChronoUnit、Until 时间区间Duration的简单使用

一.简述 在Java8中&#xff0c;我们可以使用以下类来计算日期时间差异&#xff1a; 1.Period 2.Duration 3.ChronoUnit二.Period类 Period类计算只有年、月、日 计算的是LocalDate两个时间间隔的年月日 public static void main(String[] args) {LocalDate startTime Loc…

[JAVA基础] 成员变量和局部变量(一看就懂的总结归纳篇)

引言 成员变量和局部变量在每种编程语言中都有涉及&#xff0c;如果之前了解过其他语言的成员变量或者局部变量&#xff0c;那么在学习java中的成员变量和局部变量时可以看看有那些联系和不同&#xff0c;这一块的东西也不能说难&#xff0c;如果第一次接触可能会感觉有点乱&a…