What is a WebSocket?

  1. WebSocket is a TCP – based full-duplex communication protocol and belongs to IETF
  2. The WebSocket API is a Web API owned by the W3C
  3. The two specifications are published independently

What does WebSocket have to do with HTTP?

  • WebSocket has nothing to do with Http. It’s just a supplement to Http. Why is it a supplement to Http?
  • WebSocket borrows Http to complete part of the handshake. First, the browser sends an Http GET request, followed by the server returning 101 Switching Protocols. The client gets this response code when it notifies the server that it wants to switch to a protocol other than HTTP. Normally, the HTTP client closes the TCP connection with the server after receiving a 101 response from the server. The 101 response code means that the client is no longer an HTTP client, but a different client. After sending this response, upgrade HTTP to webSocket.
  • Connection: Upgrade must be set
  • Upgrade: Websocket must be set for the field
  • Sec-websocket-key: is a Base64 encode value, which is a random string generated by the browser for validation.
  • The Origin: field is optional and is usually used to indicate the page from which the Websocket connection was initiated in the browser, similar to Referer. However, unlike Referer, Origin contains only the protocol and host name.

WebSocket advantages?

  1. WebSocket is a persistent protocol, whereas HTTP is a non-persistent stateless protocol
  2. HTTP is passive. A Request corresponds to a Response, which cannot be initiated actively
  3. Ajax polling works by having the browser send a request every few seconds to ask the server if there is any new information, requiring fast processing speed and resources from the server
  4. Long pool to the principle of ajax polling, adopt the way of polling, but take block model, the client when a connection is initiated if no news, has not returned to the Response to the client, until the news return, return, after the client to connect again, cycle, need to have high concurrency server

WebSocket API

ws.readyState describe
0 Are connected
1 Indicates that the connection is successful and the communication is normal
2 Connection closing
3 Indicates that the connection is closed or failed to open
bufferedAmount
Read only bufferedAmount Number of UTF-8 text bytes that have been queued by Send () for transmission but have not yet been sent
The event Event handler describe
open ws.onopen Triggered when the connection is established
message ws.onmessage Triggered when data is received
error ws.onerror Triggered when a communication error occurs
close ws.onclose Triggered when the connection is closed
methods describe
ws.send() Use the connection to send data
ws.close() Close links

WebSocket API compatibility?

WebSocket is commonly implemented

To address compatibility issues, websocket is commonly implemented through socket.io.

Vue + koa

chat.vue
<template> <div class="chat"> <! <van-nav-bar :title="navTitle" left-text="" left-arrow @click-left="onClickLeft" /> <! <div class="box"> <div class="chat-list" V -for="(item,index) in chatmsgs" :key="item.id"> <div v-if="item.from == myUserInfo.userId" class="chat-left"> <img class="user-img" :src='item.avatar'/> <div>{{item.content}}</div> </div> <div v-else class="chat-right"> <div>{{item.content}}</div> <img class="user-img" :src='item.avatar'/> </div> </div> </div> <! <div class="chat "> <input id="input" type="text" placeholder=" placeholder" V-model ="inputText"/> <span @click="send"> send </span> </div> </div> </template> <script> import axios from 'axios'; import url from '@/service.config.js'; import { mapState } from 'vuex'; import io from 'socket.io-client' export default { name: 'chat', components: { }, data() { return { myUserInfo: {}, navTitle: '', userId: '', inputText: '', socket: '', msgsList: [], avatar: require('.. /assets/default_img.jpeg'), flag: false, socketData: {}, chatmsgs:[] }; }, computed: { ... mapState(['userInfo']), }, mounted() { this.islogin = this.userInfo.isLogin; this.myUserInfo = this.userInfo; this.userId = this.$route.query.userid; Socket = IO ('ws://localhost:3000'); This.socket. on('recvmsg', data=>{console.log(data," listening for global messages "); this.flag = true; this.socketData = data; this.userInto(data.from); }) setTimeout(()=>{ this.userInto(this.userId); this.getMsgList(); document.addEventListener("keydown", this.keyDownEvent); }); Console. log(" my info =======", this.myuserinfo)}, methods: {onClickLeft() {this.flag = false; window.history.go(-1); }, userInto(user) { axios({ url: url.userInto, method: 'post', data: { userid: user, } }).then(res=>{ if(res.data.code == 200) { const data = res.data.data; if(! this.flag) { this.navTitle = data.userName; } else { this.avatar = res.data.data.userHead == '' ? require('.. /assets/default_img.jpeg') : res.data.data.userHead; this.$set(this.socketData,'avatar',this.avatar); this.msgsList=[...this.msgsList,this.socketData]; const chatid = [this.userId,this.myUserInfo.userId].sort().join('_'); this.chatmsgs = this.msgsList.filter(v=>v.chatid == chatid); console.log("this.msgsList,this.chatmsgs=============", this.msgsList, this.chatmsgs); } } }).catch(err=>{ console.log(err); }); }, getMsgList() { axios({ url: url.getMsgList, method: 'post', data: { from: this.myUserInfo.userId, } }).then(res=>{ if(res.data.code == 200) { this.msgsList = res.data.data.msgs; this.msgsList.forEach((item,index) => { let avatar = res.data.data.users[item.from].avatar; let userHead = avatar=='' ? require('.. /assets/default_img.jpeg') :avatar this.$set(item,'avatar',userHead); }); const chatid = [this.userId,this.myUserInfo.userId].sort().join('_'); this.chatmsgs = this.msgsList.filter(v=>v.chatid == chatid); } }) }, send() { let text = document.getElementById("input").value const from = this.myUserInfo.userId; const to = this.userId; const msg = text; this.socket.emit('sendmsg',{from,to,msg}) this.inputText=''; }, keyDownEvent(event) { if (event.keyCode === 13) this.send(); }, } } </script>Copy the code
koa index.js
const Koa = require('koa');
const app = new Koa();
/ / associated koa
const server = require('http').Server(app.callback());
const io = require('socket.io')(server);
const chatModel = require('./model/Chat');
const Chat = chatModel.getModel('Chat');
io.on('connection'.function(socket){
	// The socket variable passed in is the current link, and IO is the global link
	console.log('socket connection');
	socket.on('sendmsg'.function(data){
		const {from,to,msg} = data;
		// Two user IDS sort into a string each time the string is queried
		const chatid = [from,to].sort().join('_')
		Chat.create({chatid,from,to,content:msg},function(err,doc){
			console.log(doc,"Create message");
			io.emit('recvmsg'.Object.assign({},doc._doc))
		})
	})
})

// Resolve cross-domain issues (placing the router under the router also causes cross-domain issues)
const cors = require('koa2-cors');
app.use(cors({
	origin: ['xxx:8080'].credentials: true
}));

// Resolve front-end POST requests (post may not be resolved if placed under the router)
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());

// controller imports routes
const Router = require('koa-router');
let chat = require('./controller/chat.js');

let router = new Router();
router.use('/chat', chat.routes());

app.use(router.routes());
app.use(router.allowedMethods());

app.use(async ctx => {
	ctx.body = 'hello word';
});

// "listener" argument must be a function
server.listen(3000, () = > {console.log('Server is running at port 3000... ');
});
Copy the code
koa model/Chat.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const chatSchema = new Schema({
    chatid: {'type':String.'require':true},
    from: {'type':String.'require':true},
    to: {'type':String.'require':true},
    content: {'type':String.'require':true.default:' '});
mongoose.model('Chat', chatSchema);
module.exports = {
	getModel:function(name){
		return mongoose.model(name)
	}
}
Copy the code
koa controller/chat.js
const Router = require('koa-router');
let router = new Router();
const mongoose = require('mongoose');
router.post('/getMsgList'.async ctx => {
	const Chat = mongoose.model('Chat');
    let newChat = new Chat(ctx.request.body);
    const user = newChat.from;
    const User = mongoose.model('User');
    let users = {}
    await User.find({}).exec().then(async(res)=>{
		res.forEach(v= >{
			users[v._id] = {name:v.userName,avatar:v.userHead}
        })
        await Chat.find({'$or': [{from:user},{to:user}]}).exec().then((res) = >{
            ctx.body = {
                code: 200.message: 'success'.data: {
                    msgs:res,
                    users:users
                }
            };
        }).catch(err= >{
            ctx.body = {
                code: 500.message: err }; }); })});module.exports = router;
Copy the code