赞
踩
Socket,套接字就是两台主机之间逻辑连接的端点。TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。Socket是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远程主机的IP地址、远程进程的协议端口。
Socket编程主要涉及到客户端和服务端两个方面,首先是在服务器端创建一个服务器套接字(ServerSocket),并把它附加到一个端口上,服务器从这个端口监听连接。端口号的范围是0到65536,但是0到1024是为特权服务保留的端口号,可以选择任意一个当前没有被其他进程使用的端口。
客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。
服务端代码:
public class ServerDemo { public static void main(String[] args) throws Exception { //1.创建一个线程池,如果有客户端连接就创建一个线程, 与之通信 ExecutorService executorService = Executors.newCachedThreadPool(); //2.创建 ServerSocket 对象 ServerSocket serverSocket = new ServerSocket(9999); System.out.println("服务器已启动"); while (true) { //3.监听客户端 final Socket socket = serverSocket.accept(); System.out.println("有客户端连接"); //4.开启新的线程处理 executorService.execute(new Runnable() { @Override public void run() { handle(socket); } }); } } public static void handle(Socket socket) { try { System.out.println("线程ID:" + Thread.currentThread().getId() + " 线程名称:" + Thread.currentThread().getName()); //从连接中取出输入流来接收消息 InputStream is = socket.getInputStream(); byte[] b = new byte[1024]; int read = is.read(b); System.out.println("客户端:" + new String(b, 0, read)); //连接中取出输出流并回话 OutputStream os = socket.getOutputStream(); os.write("没钱".getBytes()); } catch (Exception e) { e.printStackTrace(); } finally { try { //关闭连接 socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
客户端代码:
public class ClientDemo { public static void main(String[] args) throws Exception { while (true) { //1.创建 Socket 对象 Socket s = new Socket("127.0.0.1", 9999); //2.从连接中取出输出流并发消息 OutputStream os = s.getOutputStream(); System.out.println("请输入:"); Scanner sc = new Scanner(System.in); String msg = sc.nextLine(); os.write(msg.getBytes()); //3.从连接中取出输入流并接收回话 InputStream is = s.getInputStream(); byte[] b = new byte[1024]; int read = is.read(b); System.out.println("老板说:" + new String(b, 0, read).trim()); //4.关闭 s.close(); } } }
Java NIO 全称java non-blocking IO ,是指 JDK 提供的新 API。从 JDK1.4 开始,Java 提供了一系列改进的输入/输出的新特性,被统称为 NIO(即 New IO),是同步非阻塞的.
一张图描述 NIO 的 Selector 、 Channel 和 Buffer 的关系
Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty 提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO 程序。 Netty 是一个基于 NIO 的网络编程框架,使用Netty 可以帮助你快速、简单的开发出一 个网络应用,相当于简化和流程化了 NIO 的开发过程。 作为当前最流行的 NIO 框架,Netty 在互联网领域、大数据分布式计算领域、游戏行业、 通信行业等获得了广泛的应用,知名的 Elasticsearch 、Dubbo 框架内部都采用了 Netty。
Netty 的强大之处:零拷贝、可拓展事件模型;支持 TCP、UDP、HTTP、WebSocket 等协议;提供安全传输、压缩、大文件传输、编解码支持等等。
在NIO中通过Selector的轮询当前是否有IO事件,根据JDK NIO api描述,Selector的select方法会一直阻塞,直到IO事件达到或超时,但是在Linux平台上这里有时会出现问题,在某些场景下select方法会直接返回,即使没有超时并且也没有IO事件到达,这就是著名的epollbug,这是一个比较严重的bug,它会导致线程陷入死循环,会让CPU飙到100%,极大地影响系统的可靠性,到目前为止,JDK都没有完全解决这个问题。
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.42.Final</version>
</dependency>
服务端实现步骤:
/** * Netty服务端 */ public class NettyServer { public static void main(String[] args) throws InterruptedException { //1. 创建bossGroup线程组: 处理网络事件--连接事件 EventLoopGroup bossGroup = new NioEventLoopGroup(1); //2. 创建workerGroup线程组: 处理网络事件--读写事件 2*处理器线程数 EventLoopGroup workerGroup = new NioEventLoopGroup(); //3. 创建服务端启动助手 ServerBootstrap serverBootstrap = new ServerBootstrap(); //4. 设置bossGroup线程组和workerGroup线程组 serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) //5. 设置服务端通道实现为NIO .option(ChannelOption.SO_BACKLOG, 128)//6. 参数设置 .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)//6. 参数设置 .childHandler(new ChannelInitializer<SocketChannel>() { //7. 创建一个通道初始化对象 @Override protected void initChannel(SocketChannel ch) throws Exception { //8. 向pipeline中添加自定义业务处理handler ch.pipeline().addLast(new NettyServerHandler()); } }); //9. 启动服务端并绑定端口,同时将异步改为同步 ChannelFuture future = serverBootstrap.bind(9999); future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { System.out.println("端口绑定成功!"); } else { System.out.println("端口绑定失败!"); } } }); System.out.println("服务端启动成功."); //10. 关闭通道(并不是真正意义上关闭,而是监听通道关闭的状态)和关闭连接池 future.channel().closeFuture().sync(); bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }
自定义服务端handle:
/** * 自定义处理Handler */ public class NettyServerHandler implements ChannelInboundHandler { /** * 通道读取事件 * * @param ctx * @param msg * @throws Exception */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf byteBuf = (ByteBuf) msg; System.out.println("客户端发送过来的消息:" + byteBuf.toString(CharsetUtil.UTF_8)); } /** * 通道读取完毕事件 * * @param ctx * @throws Exception */ @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(Unpooled.copiedBuffer("你好.我是Netty服务端", CharsetUtil.UTF_8));//消息出站 } /** * 通道异常事件 * * @param ctx * @param cause * @throws Exception */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { } }
客户端实现步骤:
/** * 客户端 */ public class NettyClient { public static void main(String[] args) throws InterruptedException { //1. 创建线程组 EventLoopGroup group = new NioEventLoopGroup(); //2. 创建客户端启动助手 Bootstrap bootstrap = new Bootstrap(); //3. 设置线程组 bootstrap.group(group) .channel(NioSocketChannel.class)//4. 设置客户端通道实现为NIO .handler(new ChannelInitializer<SocketChannel>() { //5. 创建一个通道初始化对象 @Override protected void initChannel(SocketChannel ch) throws Exception { //6. 向pipeline中添加自定义业务处理handler ch.pipeline().addLast(new NettyClientHandler()); } }); //7. 启动客户端,等待连接服务端,同时将异步改为同步 ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9999).sync(); //8. 关闭通道和关闭连接池 channelFuture.channel().closeFuture().sync(); group.shutdownGracefully(); } }
自定义handler
/** * 客户端处理类 */ public class NettyClientHandler implements ChannelInboundHandler { /** * 通道就绪事件 * * @param ctx * @throws Exception */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ChannelFuture future = ctx.writeAndFlush(Unpooled.copiedBuffer("你好呀.我是Netty客户端", CharsetUtil.UTF_8)); future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { System.out.println("数据发送成功!"); } else { System.out.println("数据发送失败!"); } } }); } /** * 通道读就绪事件 * * @param ctx * @param msg * @throws Exception */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf byteBuf = (ByteBuf) msg; System.out.println("服务端发送的消息:" + byteBuf.toString(CharsetUtil.UTF_8)); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { } }
1)IP:网络中每一台计算机的唯一标识。
2)端口:用于标识进程的逻辑地址
3)协议:定义通信规则
三次握手:
A:创建客户端的Socket对象
B:建立连接
Socket s = new Socket("192.168.3.100", 10010);//创建了对象也建立了连接,端口号要和服务端一致
C:获取输出流,写数据即可
OutputStream os = s.getOutputStream();
os.write("hello,tcp,我来了".getBytes());
D:释放资源
s.close();
A:创建服务器端Socket对象
ServerSocket ss = new ServerSocket(10010);//端口号要和客户端写的一致
B:监听连接
Socket s = ss.accept();//阻塞式方法
C:获取输入流,读取数据,并显示
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);//阻塞阻塞式方法
String client = new String(bys, 0, len);
System.out.println(client);
D:释放资源
s.close();
A:创建发送端Socket服务对象
DatagramSocket ds = new DatagramSocket();
B:创建数据,并把数据打包
byte[] bys = "黄牛破解12306网站,刷票。说明黄牛也是IT出身".getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length,
InetAddress.getByName("192.168.3.100"), 12306);
C:发送数据
ds.send(dp);
D:释放资源
ds.close();
A:创建接收端Socket服务对象
DatagramSocket ds = new DatagramSocket(12306);
B:创建数据包(接收容器)
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
C:调用接收方法
ds.receive(dp);
D:解析数据包,把数据显示在控制台
String ip = dp.getAddress().getHostAddress();
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println(ip + "---" + s);
E:释放资源
ds.close();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。