本文主要是介绍Socket,ServerSocket,WebSocket,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
最近在看webSocket,忽然想到以前学的Socket和ServerSocket,那么他们之间有什么不同呢?还有来回忆下Socket,和学习下webSocket
(天真的我以为写一个ServerSocket,再写一个webSocket就能实现通信了)
一 区别
首先来说下区别吧,
Socket和ServerSocket 指传输层网络接口协议,是基于套接字的服务端和客户端实现。
而WebScoket是应用层协议,是客户端-服务器的异步通信方法,用于双向推送消息。
二 Socket和ServerSocket
ServerSocket简单实现
ServerSocket server = null;Socket client = null;try {server = new ServerSocket(8888);client = server.accept();BufferedReader input = new BufferedReader(new InputStreamReader(client.getInputStream()));boolean flag = true;while (flag) {String line = input.readLine();System.out.println("客户端:" + line);if (line.equals("exit")) {flag = false;} }} catch (IOException e) {e.printStackTrace();}finally {try {if(client!=null){client.close();}} catch (IOException e) {e.printStackTrace();}}
Socket简单实现
Socket client =null;try {client = new Socket("127.0.0.1", 8888);PrintWriter output =new PrintWriter(client.getOutputStream(), true);Scanner cin = new Scanner(System.in);String words;while (cin.hasNext()) {words = cin.nextLine();output.println(words);System.out.println("写出了数据:" + words);}cin.close();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally{try {if(client!=null){client.close();}} catch (IOException e) {e.printStackTrace();}}
NIO回顾
Java NIO 由以下几个核心部分组成:
Channels
Buffers
Selectors
Channel 和 Buffer
基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
SocketChannel
try {Selector selector = Selector.open(); // 创建一个套接字通道,注意这里必须使用无参形式 SocketChannel channel = SocketChannel.open();// 设置为非阻塞模式,这个方法必须在实际连接之前调用(所以open的时候不能提供服务器地址,否则会自动连接) channel.configureBlocking(false); channel.connect(new InetSocketAddress("127.0.0.1", 8888));channel.register(selector, SelectionKey.OP_CONNECT); while (true) {// 测试等待事件发生,分为直接返回的selectNow()和阻塞等待的select(),另外也可加一个参数表示阻塞超时 // 停止阻塞的方法有两种: 中断线程和selector.wakeup(),有事件发生时,会自动的wakeup() // 方法返回为select出的事件数(参见后面的注释有说明这个值为什么可能为0). // 另外务必注意一个问题是,当selector被select()阻塞时,其他的线程调用同一个selector的register也会被阻塞到select返回为止 // select操作会把发生关注事件的Key加入到selectionKeys中(只管加不管减) if (selector.select() == 0) { // System.out.println("跳过");continue; } // 获取发生了关注时间的Key集合,每个SelectionKey对应了注册的一个通道 Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> it = keys.iterator();while (it.hasNext()) {SelectionKey key = it.next();// OP_CONNECT 两种情况,链接成功或失败这个方法都会返回true if (key.isConnectable()) { // 由于非阻塞模式,connect只管发起连接请求,finishConnect()方法会阻塞到链接结束并返回是否成功 // 另外还有一个isConnectionPending()返回的是是否处于正在连接状态(还在三次握手中) if (channel.finishConnect()) { System.out.println("完成连接");// 链接成功了可以做一些自己的处理,略 Scanner cin = new Scanner(System.in);SocketChannel serverchannel = (SocketChannel) key.channel(); String words = "";ByteBuffer buffer = null;while (cin.hasNext()) {words = cin.nextLine();buffer = ByteBuffer.wrap(words.getBytes("UTF8"));System.out.println(serverchannel.equals(channel));//同一对象channel.write(buffer); System.out.println("写出了数据:" + words);}// 处理完后必须吧OP_CONNECT关注去掉,改为关注OP_READ key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE); } if(channel.isConnectionPending()){System.out.println("正在连接状态");}} // 后略 和服务器端的类似 // ...if (key.isReadable()) { System.out.println("读事件");key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE); } if (key.isWritable()) { System.out.println("写事件");key.interestOps(SelectionKey.OP_READ); } if(key.isConnectable()){System.out.println("连接状态");key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE); }it.remove();}}} catch (IOException e) {e.printStackTrace();}
ServerSocketChannel
try {// 创建一个选择器,可用close()关闭,isOpen()表示是否处于打开状态,他不隶属于当前线程 Selector selector = Selector.open(); // 创建ServerSocketChannel,并把它绑定到指定端口上 ServerSocketChannel server = ServerSocketChannel.open(); server.bind(new InetSocketAddress( 8888)); // 设置为非阻塞模式, 这个非常重要 server.configureBlocking(false); // 在选择器里面注册关注这个服务器套接字通道的accept事件 // ServerSocketChannel只有OP_ACCEPT可用,OP_CONNECT,OP_READ,OP_WRITE用于SocketChannel server.register(selector, SelectionKey.OP_ACCEPT); while(true){// 测试等待事件发生,分为直接返回的selectNow()和阻塞等待的select(),另外也可加一个参数表示阻塞超时 // 停止阻塞的方法有两种: 中断线程和selector.wakeup(),有事件发生时,会自动的wakeup() // 方法返回为select出的事件数(参见后面的注释有说明这个值为什么可能为0). // 另外务必注意一个问题是,当selector被select()阻塞时,其他的线程调用同一个selector的register也会被阻塞到select返回为止 // select操作会把发生关注事件的Key加入到selectionKeys中(只管加不管减) if (selector.select() == 0) { // continue; } // 获取发生了关注时间的Key集合,每个SelectionKey对应了注册的一个通道 Set<SelectionKey> keys = selector.selectedKeys(); // 多说一句selector.keys()返回所有的SelectionKey(包括没有发生事件的) Iterator<SelectionKey> it = keys.iterator();while (it.hasNext()) {SelectionKey key = it.next();// OP_ACCEPT 这个只有ServerSocketChannel才有可能触发 if (key.isAcceptable()) { System.out.println("套接字通道 ");// 得到与客户端的套接字通道 SocketChannel channel = ((ServerSocketChannel) key.channel()).accept(); // 同样设置为非阻塞模式 channel.configureBlocking(false); // 同样将于客户端的通道在selector上注册,OP_READ对应可读事件(对方有写入数据),可以通过key获取关联的选择器 ByteBuffer buffer = ByteBuffer.allocate(1024);channel.register(key.selector(), SelectionKey.OP_READ, buffer); } // OP_READ 有数据可读 if (key.isReadable()) {System.out.println("正在读");SocketChannel channel = (SocketChannel) key.channel(); // 得到附件,就是上面SocketChannel进行register的时候的第三个参数,可为随意Object ByteBuffer buffer = (ByteBuffer) key.attachment(); // 读数据 channel.read(buffer); buffer.flip();byte[] arr = new byte[buffer.limit()];while (buffer.hasRemaining()) {int i = 0;arr[i]=buffer.get();System.out.println("B:"+arr[i]);i++;}System.out.println("C:"+new String(arr,"utf-8"));buffer.clear();// 改变自身关注事件,可以用位或操作|组合时间 key.interestOps( SelectionKey.OP_WRITE|SelectionKey.OP_READ); } // OP_WRITE 可写状态 这个状态通常总是触发的,所以只在需要写操作时才进行关注 if (key.isWritable()) { System.out.println("正在写");// 写数据掠过,可以自建buffer,也可用附件对象(看情况),注意buffer写入后需要flip // ...... // 写完就吧写状态关注去掉,否则会一直触发写事件 key.interestOps(SelectionKey.OP_READ); } // 由于select操作只管对selectedKeys进行添加,所以key处理后我们需要从里面把key去掉 it.remove();}//for (SelectionKey key : keys) { // }}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}
webSocket
再来看看webSocket,spring集成实现
1.添加jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${spring.version}</version>
</dependency>
<dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.0</version><scope>provided</scope>
</dependency>
其他核心jar自行添加,另spring需4.0以上版本
2.实现WebSocketHandler,用于处理信息
package com.fp.controller.websocket;import java.util.ArrayList;
import java.util.List;import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;public class MyWebSocketHandler implements WebSocketHandler{private List<WebSocketSession> sessions = new ArrayList<WebSocketSession>();//建立@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {System.out.println("afterConnectionEstablished");sessions.add(session);}//发送消息@Overridepublic void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {System.out.println("handleMessage");for(WebSocketSession se : sessions){se.sendMessage(message);}System.out.println(message);session.sendMessage(message);}//异常@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {System.out.println("handleTransportError");}//关闭@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {System.out.println("afterConnectionClosed");}@Overridepublic boolean supportsPartialMessages() {System.out.println("supportsPartialMessages");return false;}}
3.实现拦截器HttpSessionHandshakeInterceptor
package com.fp.controller.websocket;import java.util.ArrayList;
import java.util.List;
import java.util.Map;import org.apache.http.HttpMessage;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;public class MyHttpSessionHandshakeInterceptor extends HttpSessionHandshakeInterceptor{@Overridepublic boolean beforeHandshake(ServerHttpRequest request,ServerHttpResponse response, WebSocketHandler wsHandler,Map<String, Object> attributes) throws Exception {// 解决The extension [x-webkit-deflate-frame] is not supported问题if (request.getHeaders().containsKey("Sec-WebSocket-Extensions")) {request.getHeaders().set("Sec-WebSocket-Extensions","permessage-deflate");}System.out.println("Before Handshake");/* String domain = request.getHeaders().getOrigin();System.out.println(domain);HttpHeaders headers = response.getHeaders();headers.setAccessControlAllowOrigin(domain);List<HttpMethod> list = new ArrayList<HttpMethod>();list.add(HttpMethod.GET);list.add(HttpMethod.POST);list.add(HttpMethod.DELETE);list.add(HttpMethod.POST);list.add(HttpMethod.OPTIONS);headers.setAccessControlAllowMethods(list);headers.setAccessControlAllowCredentials(true);*//*headersresponse.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Control-Expose-Headers");response.setHeader("Access-Control-Expose-Headers", "Content-Type"); */return super.beforeHandshake(request, response, wsHandler, attributes);}@Overridepublic void afterHandshake(ServerHttpRequest request,ServerHttpResponse response, WebSocketHandler wsHandler,Exception ex) {System.out.println("After Handshake");/*String domain = request.getHeaders().getOrigin();HttpHeaders headers = response.getHeaders();headers.setAccessControlAllowOrigin(domain);List<HttpMethod> list = new ArrayList<HttpMethod>();list.add(HttpMethod.GET);list.add(HttpMethod.POST);list.add(HttpMethod.DELETE);list.add(HttpMethod.POST);list.add(HttpMethod.OPTIONS);headers.setAccessControlAllowMethods(list);headers.setAccessControlAllowCredentials(true);*/ex.printStackTrace();super.afterHandshake(request, response, wsHandler, ex);}
}
4.spring配置
<bean id="websocket" class="com.fp.controller.websocket.MyWebSocketHandler"/> <websocket:handlers allowed-origins="*"> <!-- path表示对应的连接 --><websocket:mapping path="/websocket" handler="websocket"/> <!-- 这个class是连接的流程控制方法,这个重写HttpSessionHandshakeInterceptor的方法--><websocket:handshake-interceptors> <bean class="com.fp.controller.websocket.MyHttpSessionHandshakeInterceptor"/> </websocket:handshake-interceptors>
</websocket:handlers>
如果前端用sockjs,websocket:handlers需加节点
(这个还没有试过,只是别处的方法)
<websocket:sockjs/>
5.前端页面
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><!--<script src="http://libs.baidu.com/jquery/1.10.0/jquery.min.js"></script>--><script src="js/jquery-1.7.2.min.js" type="text/javascript" charset="utf-8"></script><title></title></head><body><input type="" name="" id="sendtext" value="" /><input type="button" id="send" value="发送" /><div id="return"></div></body><script type="text/javascript"> var url = 'ws://localhost:8080/ws/websocket';//var url = 'ws://echo.websocket.org/';var socket = new WebSocket(url);socket.onopen = function( ){console.log("open ");$("#return").text("open");}socket.onclose = function( ){console.log("close "); $("#return").text("close");}socket.onmessage = function(e){console.log("onmessage ");console.log(e.data);$("#return").text(e.data);}$("#send").click(function(){var text = $("#sendtext").val();socket.send(text);});</script>
</html>
如果报403错误,是跨域没有配置allowed-origins="*"
属性
https://www.cnblogs.com/zhjh256/p/6052102.html
http://ifeve.com/java-nio-all/
这篇关于Socket,ServerSocket,WebSocket的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!