Socket,ServerSocket,WebSocket

2024-08-29 08:18

本文主要是介绍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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1117332

相关文章

Java Websocket实例【服务端与客户端实现全双工通讯】

Java Websocket实例【服务端与客户端实现全双工通讯】 现很多网站为了实现即时通讯,所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发 出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request 的模式带来很明显的缺点 – 浏 览器需要不断的向服务器发出请求,然而HTTP

Java Socket服务器端与客户端的编程步骤总结

一,InetAddress类: InetAddress类没有构造方法,所以不能直接new出一个对象; 可以通过InetAddress类的静态方法获得InetAddress的对象; InetAddress.getLocalHost(); InetAddress.getByName(""); 类主要方法: String - address.getHostName(); String - addre

VC环境下window网络程序:UDP Socket程序

最近在学Windows网络编程,正好在做UDPsocket的程序,贴上来: 服务器框架函数:              socket();    bind();    recfrom();  sendto();  closesocket(); 客户机框架函数:            socket();      recfrom();  sendto();  closesocket();

springboot websocket 服务端

在Spring Boot中使用WebSocket实现服务端和Java客户端的实时通信,可以分为几个步骤来完成。这里将详细介绍服务端和Java客户端的具体实现。 服务端设置 添加依赖: 在pom.xml文件中添加Spring WebSocket的依赖。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spr

socket()接口与内核协议栈的挂接

最近在看Brdige的代码,发现一个问题,同样的调用ioctl接口实现添加网桥、删除网桥、网桥增加网卡、网桥删除网卡等操作,一个应用层的接口,却通过两条路径实现,sock_ioctl和RTNETLINK(这本就不是一个级别的东西),而应用层的brctl-utils源码中并没有直接使用PF_NETLINK协议簇的情况,让我感到非常奇怪,因此想把glibc到系统调用,到协议簇注册,以及和VFS的关系再

linux下的Socket网络编程教程

套接字概念 Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。

socket函数接收发送详解

http://blog.csdn.net/g_brightboy/article/details/12854117 http://blog.csdn.net/liangkaiyang/article/details/5931901 send。。。 这里只描述同步Socket的send函数的执行流程。 当调用该函数时,send先比较待发送数据的长度

linux下socket常用函数

1、setprotoent(打开网络协议的数据文件) 相关函数  getprotobyname, getprotobynumber, endprotoent 表头文件  #include <netdb.h> 定义函数  void setprotoent (int stayopen); 函数说明      setprotoent()用来打开/etc/protocols,如果参数

Spring boot 项目作为客户端调用 服务端websocket

文章目录 java客户端请求websocketSpring boot 导入包客户端调用方法测试执行方法connectWebSocketHandshakeMessagesendHandshakeWebSocketConfig.queue.take方法对应实体类配置 yaml 资源WebSocketConfig 配置类注入配置websocketUrl:LinkedBlockingQueueLin

WebSocket+Spring boot 构建一个完整消息服务

1、添加依赖 compile project(":faas-spring-boot-starter-data-websocket") 2、定义WebSocketHandler Socket 服务入口(Header接收 jwt-token 同应用登录的Token(直接解决鉴权问题),然后定义请求的自定义参数,方便后续消息推送、支持群发、私发、模糊匹配) @Component@WebSock