使用WebSocket建立服务器与客户端的双向连接
import http from '@ohos.net.htt
import util from '@ohos.util'
import promptAction from '@ohos.promptAction'
import { webSocket } from '@kit.NetworkKit'
import { BusinessError } from '@kit.BasicServicesKit'
@Entry
@Component
struct ChatMessage {
@State
contentStr: string = ""
@State
messList: MessageItem[] = []
scroller: Scroller = new Scroller()
@State
sendIng: boolean = false
token: string = ""
ws: webSocket.WebSocket = webSocket.createWebSocket()
aboutToAppear() {
this.getToken()
}
aboutToDisappear(): void {
this.ws.close()
}
async getToken() {
const instance = http.createHttp()
const res = await instance.request('https://toutiao.itheima.net/v1_0/authorizations', {
header: {
"Content-Type": "application/json",
},
method: http.RequestMethod.POST,
extraData: { mobile: '13912345678', code: '246810' }
})
const tokenObj = JSON.parse(res.result as string) as ResponseResult<Token>
this.token = tokenObj.data.token
this.createConnect()
}
// 创建连接
async createConnect() {
if (this.token) {
try {
// 1.连接
this.ws.connect('ws://toutiao.itheima.net/socket.io/?EIO=4&transport=websocket&token=' + this.token,
(err: BusinessError, value: boolean) => {
if (!err) {
console.log("hmsocket connect success");
} else {
console.log("hmsocket connect fail, err:" + JSON.stringify(err));
}
})
// 2.收消息
this.ws.on('message', (err: BusinessError<void>, value: string | ArrayBuffer) => {
console.log("hmsocket on message, message:" + value);
// 协议1:收到0开头回复40
if ((value as string).startsWith('0')) {
this.ws.send('40')
} else if ((value as string).startsWith('2')) {
// 协议2:收到2回复3
this.ws.send('3')
} else if ((value as string).startsWith('42')) {
// 协议3:收到42开头,type为'message'是消息,current是心跳
console.log('hmsocket message value slice:' + (value as string).replace('42', ''))
if (JSON.parse((value as string).replace('42', ''))[0] === 'message') {
// promptAction.showToast({
// message: JSON.parse((value as string).replace('42', ''))[1]['msg']
// })
this.messList.push(new MessageItem(JSON.parse((value as string).replace('42', ''))[1]['msg'],false))
this.scroller.scrollEdge(Edge.Bottom)
}
}
});
// 3.打开链接(发消息依赖此状态)
this.ws.on("open", (err: BusinessError, value: Object) => {
console.log("hmsocket on open, status:" + JSON.stringify(value));
})
// 4.监听关闭(发消息依赖此状态)
this.ws.close((err: BusinessError) => {
if (!err) {
console.log("hmsocket close success");
} else {
console.log("hmsocket close fail, err is " + JSON.stringify(err));
}
});
} catch (error) {
promptAction.showToast({ message: error.message })
}
}
}
async sendMessage() {
if (!this.contentStr) {
return
}
this.sendIng = true
try {
const testData: [string, MessageStruct] = ['message', {
timestamp: Date.now(),
msg: this.contentStr
}]
const data = JSON.stringify(testData)
this.ws.send('42' + data, (err: BusinessError, value: boolean) => {
if (!err) {
console.log("hmsocket send success");
} else {
console.log("hmsocket send fail, err:" + JSON.stringify(err))
}
})
this.messList.push(new MessageItem(this.contentStr))
this.scroller.scrollEdge(Edge.Bottom)
this.sendIng = false
this.contentStr = ""
} catch (error) {
AlertDialog.show({ message: JSON.stringify(error) })
}
}
build() {
Stack({ alignContent: Alignment.Bottom }) {
Column() {
Row() {
Text("🙉 老婆大人")
// .minFontSize(50)
}
.width('100%')
.height(50)
.justifyContent(FlexAlign.Center)
List({ space: 20, scroller: this.scroller }) {
ForEach(this.messList, (item: MessageItem) => {
ListItem() {
// 一左一右
MessageCom({
item, delMessage: (id: string) => {
// 删除消息
const index = this.messList.findIndex(item => item.id === id)
this.messList.splice(index, 1)
}
})
}
})
}
.padding({
bottom: 80
})
.layoutWeight(1)
}.height('100%')
// 消息列表
Row({ space: 20 }) {
TextInput({ text: this.contentStr })
.layoutWeight(1)
.backgroundColor(Color.White)
.borderRadius(2)
.height(40)
.onChange((value) => {
this.contentStr = value
})
.onSubmit(() => {
if (this.contentStr) {
this.sendMessage()
}
})
Button() {
Row() {
if (this.sendIng) {
LoadingProgress()
.width(20)
.height(20)
.color('#fff')
.margin({
right: 4
})
} else {
Text("发送")
.fontColor("#fff")
}
}
}
.onClick(async () => {
if (!this.sendIng) {
await this.sendMessage()
}
})
.enabled(!!this.contentStr)
.height(30)
.width(60)
}
.width('100%')
.backgroundColor('#f6f6f6')
.height(70)
.padding({
left: 10,
right: 10
})
}
.backgroundColor('#ededed')
.height('100%')
}
}
@Component
struct MessageCom {
@Prop
item: Partial<MessageItem> = new MessageItem('')
@State
showMenu: boolean = false
delMessage: (id: string) => void = () => {
}
@Builder
getDelContent() {
Button("删除")
.backgroundColor(Color.White)
.fontColor("#0c2803")
.height(30)
.onClick(() => {
this.delMessage(this.item.id!)
})
}
build() {
Row() {
Image(this.item.avatar)
.height(40)
.width(40)
.borderRadius(6)
Row() {
Text(this.item.content)
.backgroundColor(this.item.self ? "#8bec73" : Color.White)
.fontColor("#0c2803")
.padding(10)
.lineHeight(24)
.margin({
left: 10,
right: 10
})
.borderRadius(5)
.gesture(
LongPressGesture()
.onAction(() => {
this.showMenu = true // 显示菜单
})
)
.bindPopup(this.showMenu, {
builder: this.getDelContent,
placement: Placement.Top,
onStateChange: (event) => {
this.showMenu = event.isVisible
}
})
}.layoutWeight(5)
.justifyContent(this.item.self ? FlexAlign.End : FlexAlign.Start)
Text().layoutWeight(1)
}
.width('100%')
.padding({
left: 20,
right: 20
})
.alignItems(VerticalAlign.Top)
.direction(this.item.self ? Direction.Rtl : Direction.Ltr)
}
}
export class MessageItem {
content: string = "" // 消息内容
avatar: ResourceStr = "" // 用户头像
username: string = "" // 用户名称
self: boolean = false // 是不是用户自己
timestamp: number = Date.now() // 消息收到时的时间戳
id: string = util.generateRandomUUID() // 唯一的id
constructor(content: string, self: boolean = true) {
this.content = content
this.self = self
this.avatar = self?'https://foruda.gitee.com/avatar/1705232317138324256/1759638_itcast_panpu_1705232317.png':'http://toutiao.itheima.net/images/user_head.jpg'
}
}
interface ResponseResult<T> {
data: T
message: string
}
interface Token {
token: string
}
interface MessageStruct {
msg: string
timestamp: number
}