WebSocket是什么?
WebSocket是一个基于TCP的全双工通讯协议,归属于IETF WebSocket API 是一个 Web API,归属于W3C 两个规范是独立发布的
WebSocket与HTTP有什么关系?
WebSocket和Http并没有关系他只是HTTP协议上的一个补充,为什么说是HTTP协议的补充呢,来看下面的两张图。 WebSocket会借用Http协议来完成一部分握手,首先浏览器发送HTTP的get请求,紧接着服务端会返回101状态码,101是Switching Protocols,当客户端通过在请求里使用Upgrade报头,以通知服务器它想改用除HTTP协议之外的其他协议时,客户端将获得此响应代码,通常HTTP客户端会在收到服务器发来的101响应后关闭与服务器的TCP连接,101响应代码意味着该客户端不再是一个HTTP客户端,而将成为另一种客户端,在发送这个响应后,将http升级到webSocket。 Connection:必须设置Upgrade Upgrade:字段必须设置Websocket Sec-WebSocket-Key:是一个Base64 encode的值,这个是浏览器随机生成的字符串,用于验证。 Origin:字段可选,通常用来表示在浏览器中发起此Websocket连接所在的页面,类似于Referer。但是,与Referer不同的是,Origin只包含了协议和主机名称。
WebSocket优点?
WebSocket是一个持久化的协议,而HTTP是非持久的无状态协议 HTTP是被动的,一个Request对应一个Response,Response不能主动发起 Ajax轮询的原理是让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息,需要服务器有很快的处理速度和资源 long pool的原理跟ajax轮询差不多,都是采用轮询的方式,不过采取的是阻塞模型,客户端发起连接后,如果没消息,就一直不返回Response给客户端,直到有消息才返回,返回完之后,客户端再次建立连接,不断循环,需要服务器有很高的并发
WebSocket API
ws.readyState 描述 0 正在连接 1 表示连接成功,可以通信 2 连接正在关闭 3 表示连接已关闭或打开连接失败
bufferedAmount 描述 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数
事件 事件处理程 描述 open ws.onopen 连接建立时触发 message ws.onmessage 接收数据时触发 error ws.onerror 通信错误时触发 close ws.onclose 连接关闭时触发
方法 描述 ws.send() 使用连接发送数据 ws.close() 关闭链接
WebSocket API 兼容性?
仍是存在不兼容的浏览器
WebSocket常用实现
为了解决兼容问题,websocket常用实现通常是通过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-foot" >
< input id= "input" type= "text" placeholder= "请输入..." v- model= "inputText" / >
< span @click= "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;
this . socket = io ( 'ws://localhost:3000' ) ;
this . socket. on ( 'recvmsg' , data=> {
console. log ( data, "监听全局消息" ) ;
this . flag = true ;
this . socketData = data;
this . userInto ( data. from ) ;
} )
setTimeout ( ( ) => {
this . userInto ( this . userId) ;
this . getMsgList ( ) ;
document. addEventListener ( "keydown" , this . keyDownEvent) ;
} ) ;
console. log ( "我的信息=======" , 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>
koa index.js
const Koa = require ( 'koa' ) ;
const app = new 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) {
console. log ( 'socket connection' ) ;
socket. on ( 'sendmsg' , function ( data) {
const { from , to, msg} = data;
const chatid = [ from , to] . sort ( ) . join ( '_' )
Chat. create ( { chatid, from , to, content: msg} , function ( err, doc) {
console. log ( doc, "创建消息" ) ;
io. emit ( 'recvmsg' , Object. assign ( { } , doc. _doc) )
} )
} )
} )
const cors = require ( 'koa2-cors' ) ;
app. use ( cors ( {
origin: [ 'xxx:8080' ] ,
credentials: true
} ) ) ;
const bodyParser = require ( 'koa-bodyparser' ) ;
app. use ( bodyParser ( ) ) ;
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' ;
} ) ;
server. listen ( 3000 , ( ) => {
console. log ( 'Server is running at port 3000...' ) ;
} ) ;
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)
}
}
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: '成功' ,
data: {
msgs: res,
users: users
}
} ;
} ) . catch ( err=> {
ctx. body = {
code: 500 ,
message: err
} ;
} ) ;
} )
} ) ;
module. exports = router;