websocket初略认识
websocket与http协议的区别与联系
1.http是无状态连接,服务器无法确定来自同一客户端的请求是同一个人发出的,且只能客户端主动访问服务端,websocket能够实现服务端主动访问客户端
2. http协议需要经过三次握手,websocket只需要一次
websocket基本实现
前端代码
var userid = Math.round(Math.random() * 1000)
var socketUrl = "ws://127.0.0.1:8080/msgServer/" + userid
window.onload = function () {
// console.log("My ID:" + userid);
socket = new WebSocket(socketUrl)
socket.onclose = function (e) {
// console.log("服务器关闭了" + e.code);
}
socket.onopen = function () {
// console.log("连接服务器成功");
}
//监听来自服务器的消息
socket.onmessage = function (res) {
}
//向服务器发送消息
var msg="hello world"
socket.send(msg)
}
后台代码
1.首先引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.在配置类中配置如下
@Configuration
public class Config {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3.核心代码
import java.io.IOException;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
@Controller
@ServerEndpoint("/msgServer/{userId}")
@Component
@Scope("prototype")
public class WebSocketServer {
/**
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
*/
private static int onlineCount = 0;
/**
* concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
*/
private static ConcurrentHashMap<String, Session> webSocketMap = new ConcurrentHashMap<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收userId
*/
private String userId = "";
@Autowired
private UsersServer userServer;
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
this.session = session;
this.userId = userId;
/**
* 连接被打开:向socket-map中添加session
*/
webSocketMap.put(userId, session);
System.out.println(userId + " - 连接建立成功...");
}
@OnMessage
public void onMessage(String message, Session session) {
try {
Enumeration<String> keys =webSocketMap.keys();
System.out.println("服务器接收到的消息:"+message);
while(keys.hasMoreElements()) {
String key = keys.nextElement();
//判断用户是否还在线
if (webSocketMap.get(key) == null){
webSocketMap.remove(key);
System.err.println(key + " : null");
continue;
}
Session sessionValue = webSocketMap.get(key);
//去除向自己转发
if (key.equals(this.userId)){
System.err.println("my id " + key);
continue;
}
//判断session是否打开
if (sessionValue.isOpen()){
sessionValue.getBasicRemote().sendText(message);
}else {
System.err.println(key + ": not open");
sessionValue.close();
webSocketMap.remove(key);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@OnError
public void onError(Session session, Throwable error) {
System.out.println("连接异常...");
error.printStackTrace();
}
@OnClose
public void onClose() {
System.out.println("连接关闭");
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
代码主要实现了对客户端发来的userid存储在CurruntHAshMap中,目的是方便服务器进行转发(可以实现群发和一对一聊天),OnMessage,onopen,onclose和客户端你功能一样。
到此可以实现web的群聊。一对一聊天就是在转发对象中添加一个筛选
webRTC
主要参考了这位前辈的博客,虽然有点是14年的,但还是有参考意义
webRTC和websocket的区别与联系
1.webrtc的实现是基于websocket的
2.websocket实现的是浏览器和服务器之间的无障碍通讯,webRTC实现的是浏览器与浏览器之间的通讯
webRTC的流程
简述实现流程,假设A浏览器端想和B浏览器视频聊天。首先A需要通过socket向B发送一个Offer和一个candidate 并保存自己的本地信息来确定自己的身份,B在收到A发来的candidate后先把它添加到候选人中(addicecandidate)。收到A发来的offer信息时,把A设为远程信息(setRemoteDescription)并向A发送自己的candidate和answer信息。A收到answer时,把answer设为自己的远端信息,并把candidate保存到自己的候选人中。到此实现A和B的通讯
贴出代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="showWorld"></div>
<video src="" id="iv" width="500px" height="500px" autoplay="autoplay"></video>
<video src="" id="iv2" width="500px" height="500px" autoplay="autoplay"></video>
<input id="sendWorld">
<!-- <button onclick="sends()">anniu</button> -->
<button onclick="cn()">niuniu</button>
</body>
<script>
var text = null
var showText = document.getElementById("showWorld")
var userid = Math.round(Math.random() * 1000)
var socketUrl = "ws://127.0.0.1:8080/msgServer/" + userid
var socket = null
var localStream = null
var pc = null
//连接socket服务器
window.onload = function () {
// console.log("My ID:" + userid);
socket = new WebSocket(socketUrl)
socket.onclose = function (e) {
// console.log("服务器关闭了" + e.code);
}
socket.onopen = function () {
// console.log("连接服务器成功");
}
socket.onmessage = function (res) {
var obj = JSON.parse(res.data)
// console.log(obj);
var type = obj.type
if (type === "offer") {
if (pc) {
console.error('peerConnection已存在!');
return;
}
pc =InitPeerConnetion()
// console.log("get offer");
var rtcs = new RTCSessionDescription(obj)
pc.setRemoteDescription(rtcs)
// console.log("set remotedescription success");
pc.createAnswer(function (desc) {
pc.setLocalDescription(desc)
// console.log("send answer");
// console.log(desc);
socket.send(JSON.stringify(desc))
// console.log("send answer success");
},function(){
// console.log("create answer fail");
})
} else if (type === "answer") {
if (!pc) {
console.error('peerConnection不存在!');
return;
}
var rtcs = new RTCSessionDescription(obj)
pc.setRemoteDescription(rtcs)
} else if (type === "candidate") {
// console.log("get candidate");
// console.log(obj);
var candidate = new RTCIceCandidate({
sdpMLineIndex: obj.sdpMLineIndex,
sdpMid: obj.sdpMid,
candidate: obj.candidate
})
pc.addIceCandidate(candidate)
// console.log("set candidate suceess");
}
}
openVideo()
}
//webrtc 建立连接
function cn() {
// console.log("send msg");
pc =InitPeerConnetion()
pc.createOffer(function (desc) {
// console.log("send offer");
pc.setLocalDescription(desc)
var txt = JSON.stringify(desc)
socket.send(txt)
}, function (err) {
// console.log("create offer fail!!!");
// console.log(err);
})
}
function openVideo() {
navigator.webkitGetUserMedia({ video: true, audio: false },
function (stream) {
localStream = stream
document.getElementById("iv").srcObject = stream;
document.getElementById("iv").play();
},
function (e) {
// console.log(e.code);
return;
}
)
}
function InitPeerConnetion(){
// console.log("init");
var peerconntion =null
try{
peerconntion =new webkitRTCPeerConnection();
}catch(e){
// console.log("connet fail");
// console.log(e.message);
}
peerconntion.onicecandidate =function(evt){
// console.log(evt.candidate);
if(evt.candidate){
// console.log(evt.candidate);
var txt =JSON.stringify({
type:"candidate",
sdpMid:evt.candidate.sdpMid,
sdpMLineIndex:evt.candidate.sdpMLineIndex,
candidate:evt.candidate.candidate
})
// console.log(txt);
// console.log("send candidate");
socket.send(txt)
}
}
// console.log("add local stream");
peerconntion.addStream(localStream)
peerconntion.onaddstream = function (event) {
document.getElementById("iv2").srcObject = event.stream
document.getElementById("iv2").play()
// console.log("绑定远程视频流");
};
return peerconntion
}
</script>
</html>
结合前后端代码就可以实现本地的视频通话。如果要部署服务器,还需要配置ice信令服务器(未涉及)
以上为个人学习笔记,不对之处,请各位大佬斧正