代码提交
This commit is contained in:
parent
2b47aa76d7
commit
eb8d03fca2
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ node_modules/
|
|||||||
src/.vuepress/.cache/
|
src/.vuepress/.cache/
|
||||||
src/.vuepress/.temp/
|
src/.vuepress/.temp/
|
||||||
src/.vuepress/dist/
|
src/.vuepress/dist/
|
||||||
|
.idea/*
|
||||||
|
12
.idea/blog.iml
Normal file
12
.idea/blog.iml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/blog.iml" filepath="$PROJECT_DIR$/.idea/blog.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -5,7 +5,7 @@ import HitokotoBlogHero from "vuepress-theme-hope/presets/HitokotoBlogHero.js";
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BlogHero>
|
<BlogHero>
|
||||||
<template #info="info" #bg>
|
<template #info="info">
|
||||||
<HitokotoBlogHero v-bind="info" />
|
<HitokotoBlogHero v-bind="info" />
|
||||||
</template>
|
</template>
|
||||||
</BlogHero>
|
</BlogHero>
|
||||||
|
@ -26,6 +26,7 @@ export default sidebar({
|
|||||||
{
|
{
|
||||||
text: "Java",
|
text: "Java",
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
expanded: true,
|
||||||
icon: "mdi:language-java",
|
icon: "mdi:language-java",
|
||||||
prefix: "java/",
|
prefix: "java/",
|
||||||
children: [
|
children: [
|
||||||
@ -38,7 +39,7 @@ export default sidebar({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "工具箱",
|
text: "工具箱",
|
||||||
icon: "simple-icons:framework",
|
icon: "mdi:tools",
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
prefix: "工具箱/",
|
prefix: "工具箱/",
|
||||||
children: "structure",
|
children: "structure",
|
||||||
|
@ -9,4 +9,4 @@ date: 2025-05-06
|
|||||||
## 自我介绍
|
## 自我介绍
|
||||||
|
|
||||||
|
|
||||||
> 你好哇哈哈哈哈的武器恶趣味大苏打撒旦eqweqwesadasdwewcasdqweqwfc1231233dsczxcxzc
|
> 一个瞎鼓捣得程序员
|
456
src/programming/java/工具箱/WebSocket和HTTP关系.md
Normal file
456
src/programming/java/工具箱/WebSocket和HTTP关系.md
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
---
|
||||||
|
icon: bi:arrows-expand
|
||||||
|
date: 2025-05-08
|
||||||
|
category:
|
||||||
|
- JAVA
|
||||||
|
- 通信协议
|
||||||
|
tag:
|
||||||
|
- websocket
|
||||||
|
- http
|
||||||
|
---
|
||||||
|
|
||||||
|
# WebSocket和HTTP关系
|
||||||
|
<!-- more -->
|
||||||
|
## 1. WebSocket简介
|
||||||
|
|
||||||
|
WebSocket 是 HTML5 提供的一种在单个 TCP 连接上进行全双工通讯的协议。它使客户端和服务器之间的数据交换变得更加简单高效,并允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需完成一次握手,便可创建持久性的连接,实现双向数据传输。
|
||||||
|
<!-- more -->
|
||||||
|
## 2. WebSocket与HTTP的关系
|
||||||
|
|
||||||
|
### 2.1 协议转换过程
|
||||||
|
|
||||||
|
WebSocket依赖于HTTP连接初始化,但随后会进行协议升级。具体转换过程如下:
|
||||||
|
|
||||||
|
1. **初始HTTP请求**:每个WebSocket连接都始于一个HTTP请求。客户端发送标准的HTTP请求,但包含特殊的头信息,表明希望升级为WebSocket协议:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /chat HTTP/1.1
|
||||||
|
Host: server.example.com
|
||||||
|
Upgrade: websocket
|
||||||
|
Connection: Upgrade
|
||||||
|
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||||||
|
Origin: http://example.com
|
||||||
|
Sec-WebSocket-Version: 13
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **服务器响应升级**:如果服务器支持WebSocket协议,会返回101状态码,表示协议正在切换:
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP/1.1 101 Switching Protocols
|
||||||
|
Upgrade: websocket
|
||||||
|
Connection: Upgrade
|
||||||
|
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **协议切换完成**:此时HTTP请求已完成其使命,如果协议升级成功,客户端会触发`onopen`事件;否则触发`onerror`事件。此后的所有通信都使用WebSocket协议,不再依赖HTTP。
|
||||||
|
|
||||||
|
### 2.2 为什么WebSocket要依赖HTTP协议连接?
|
||||||
|
|
||||||
|
WebSocket选择依赖HTTP协议有几个重要原因:
|
||||||
|
|
||||||
|
1. **设计理念**:WebSocket设计之初就是为HTTP增强通信能力(尤其是全双工通信),因此在HTTP协议基础上扩展是自然的选择,能够复用HTTP的基础设施。
|
||||||
|
|
||||||
|
2. **最大兼容性**:基于HTTP连接可获得最广泛的兼容支持。即使服务器不支持WebSocket,也能建立HTTP通信并返回相应错误,这比完全无响应要好得多。
|
||||||
|
|
||||||
|
3. **防火墙友好**:大多数网络环境允许HTTP流量通过(80和443端口),基于HTTP的WebSocket更容易穿越防火墙和代理服务器。
|
||||||
|
|
||||||
|
4. **减少实现复杂度**:复用现有的HTTP基础设施,无需从零开始构建新的协议栈。
|
||||||
|
|
||||||
|
### 2.3 HTTP与WebSocket的主要区别
|
||||||
|
|
||||||
|
|特性|HTTP|WebSocket|
|
||||||
|
|---|---|---|
|
||||||
|
|连接类型|无状态、短连接|有状态、长连接|
|
||||||
|
|通信方式|单向(请求-响应)|双向(全双工)|
|
||||||
|
|数据交互模式|客户端主动请求|双方均可主动发送|
|
||||||
|
|数据传输量|每次请求都有HTTP头|握手后数据传输更轻量|
|
||||||
|
|实时性|弱(通常需轮询)|强(可即时推送)|
|
||||||
|
|使用场景|传统网页请求、RESTful API|聊天应用、实时数据更新、在线游戏等|
|
||||||
|
|
||||||
|
## 3. WebSocket的优势与应用场景
|
||||||
|
|
||||||
|
相比传统HTTP,WebSocket具有以下优势:
|
||||||
|
|
||||||
|
1. **降低延迟**:一旦建立连接,通信双方可随时发送数据,无需重复建立连接。
|
||||||
|
|
||||||
|
2. **减少网络流量**:WebSocket在握手后的通信中没有HTTP头的开销,数据传输更高效。
|
||||||
|
|
||||||
|
3. **实时双向通信**:服务器可以主动推送信息给客户端,无需客户端轮询。
|
||||||
|
|
||||||
|
4. **保持连接状态**:WebSocket是有状态协议,可维护连接上下文信息。
|
||||||
|
|
||||||
|
典型应用场景:
|
||||||
|
- 实时通讯应用(聊天室、即时通讯工具)
|
||||||
|
- 在线协作工具(协同编辑文档)
|
||||||
|
- 实时数据展示(股票行情、体育赛事直播)
|
||||||
|
- 游戏应用(在线多人游戏)
|
||||||
|
- 物联网设备通信
|
||||||
|
|
||||||
|
## 4. 总结
|
||||||
|
|
||||||
|
WebSocket与HTTP是相辅相成的关系,而非替代关系。WebSocket通过HTTP协议完成初始握手,随后升级为独立的协议,实现更高效的双向通信。两种协议各有优势,应根据应用场景选择合适的通信方式。在需要实时性、双向通信的场景中,WebSocket展现出明显优势;而对于简单的数据获取和传统网页浏览,HTTP仍然是最佳选择。
|
||||||
|
|
||||||
|
## 5. 案例(服务端)
|
||||||
|
### 5.1 项目结构
|
||||||
|

|
||||||
|
|
||||||
|
### 5.2 依赖配置
|
||||||
|
```xml
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.4.3</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
|
||||||
|
<artifactId>websocket</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>websocket</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 主应用类
|
||||||
|
```java
|
||||||
|
package com.mangmang;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class WebSocketDemoApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(WebSocketDemoApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 5.4 WebSocket配置类
|
||||||
|
```java
|
||||||
|
package com.mangmang.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||||
|
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||||
|
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||||
|
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSocketMessageBroker
|
||||||
|
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||||
|
// 注册STOMP协议的节点(endpoint)
|
||||||
|
registry.addEndpoint("/ws").setAllowedOrigins("http://10.6.212.39:5173/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||||
|
// 配置消息代理,前缀为/topic的消息将会被路由到消息代理
|
||||||
|
registry.enableSimpleBroker("/topic");
|
||||||
|
// 以/app开头的消息将会被路由到@MessageMapping注解的方法中
|
||||||
|
registry.setApplicationDestinationPrefixes("/app");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 5.5 消息实体类
|
||||||
|
```java
|
||||||
|
package com.mangmang.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Message {
|
||||||
|
private String content;
|
||||||
|
private String sender;
|
||||||
|
private MessageType type;
|
||||||
|
|
||||||
|
|
||||||
|
public enum MessageType {
|
||||||
|
CHAT,
|
||||||
|
JOIN,
|
||||||
|
LEAVE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.6 消息控制类
|
||||||
|
```
|
||||||
|
package com.mangmang.controller;
|
||||||
|
|
||||||
|
import com.mangmang.model.Message;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
|
import org.springframework.messaging.handler.annotation.Payload;
|
||||||
|
import org.springframework.messaging.handler.annotation.SendTo;
|
||||||
|
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Controller
|
||||||
|
public class MessageController {
|
||||||
|
|
||||||
|
@MessageMapping("/chat.sendMessage")
|
||||||
|
@SendTo("/topic/public")
|
||||||
|
public Message sendMessage(@Payload Message message) {
|
||||||
|
log.info(message.toString());
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@MessageMapping("/chat.addUser")
|
||||||
|
@SendTo("/topic/public")
|
||||||
|
public Message addUser(@Payload Message message, SimpMessageHeaderAccessor headerAccessor) {
|
||||||
|
log.info(message.toString());
|
||||||
|
// 将用户名添加到WebSocket会话
|
||||||
|
Objects.requireNonNull(headerAccessor.getSessionAttributes()).put("username", message.getSender());
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.7 websocket断连通知类
|
||||||
|
```java
|
||||||
|
package com.mangmang.listener;
|
||||||
|
|
||||||
|
import com.mangmang.model.Message;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||||
|
import org.springframework.messaging.simp.SimpMessageSendingOperations;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class WebSocketEventListener {
|
||||||
|
|
||||||
|
private final SimpMessageSendingOperations messagingTemplate;
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
|
||||||
|
// 获取会话属性
|
||||||
|
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(event.getMessage());
|
||||||
|
|
||||||
|
// 从会话中获取用户名
|
||||||
|
String username = (String) Objects.requireNonNull(headerAccessor.getSessionAttributes()).get("username");
|
||||||
|
|
||||||
|
if (username != null) {
|
||||||
|
log.info("用户断开连接: {}", username);
|
||||||
|
|
||||||
|
// 创建一个离开消息
|
||||||
|
Message message = Message.builder()
|
||||||
|
.type(Message.MessageType.LEAVE)
|
||||||
|
.sender(username)
|
||||||
|
.content("下线了")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 发送消息到公共主题
|
||||||
|
messagingTemplate.convertAndSend("/topic/public", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 案例(客户端)
|
||||||
|
### 6.1 封装websocket
|
||||||
|
```ts
|
||||||
|
import {Client} from '@stomp/stompjs';
|
||||||
|
|
||||||
|
interface ChatMessage {
|
||||||
|
content: string;
|
||||||
|
sender: string;
|
||||||
|
type: 'CHAT' | 'JOIN' | 'LEAVE';
|
||||||
|
}
|
||||||
|
|
||||||
|
class WebSocketService {
|
||||||
|
private stompClient: Client | null = null;
|
||||||
|
|
||||||
|
connect(username: string, onMessageReceived: (msg: ChatMessage) => void) {
|
||||||
|
// 创建原生 WebSocket 连接
|
||||||
|
const socket = new WebSocket('ws://10.6.212.39:8099/ws'); // 确保这里是 WebSocket 协议
|
||||||
|
|
||||||
|
this.stompClient = new Client({
|
||||||
|
webSocketFactory: () => socket,
|
||||||
|
onConnect: () => {
|
||||||
|
console.log('STOMP connected');
|
||||||
|
// 订阅公共主题
|
||||||
|
this.stompClient?.subscribe('/topic/public', (message) => {
|
||||||
|
const chatMsg: ChatMessage = JSON.parse(message.body);
|
||||||
|
onMessageReceived(chatMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发送用户加入消息
|
||||||
|
this.sendAddUserMessage(username);
|
||||||
|
},
|
||||||
|
onStompError: (frame) => {
|
||||||
|
console.error('Broker reported error: ', frame.headers['message']);
|
||||||
|
console.error('Additional details: ', frame.body);
|
||||||
|
},
|
||||||
|
// 关闭时清理资源
|
||||||
|
onDisconnect: () => {
|
||||||
|
console.log('Disconnected from STOMP');
|
||||||
|
this.stompClient = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 激活客户端
|
||||||
|
this.stompClient.activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(chatMessage: Omit<ChatMessage, 'type'>) {
|
||||||
|
if (this.stompClient?.connected) {
|
||||||
|
this.stompClient.publish({
|
||||||
|
destination: '/app/chat.sendMessage',
|
||||||
|
body: JSON.stringify(chatMessage),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn('WebSocket not connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendAddUserMessage(username: string) {
|
||||||
|
if (this.stompClient?.connected) {
|
||||||
|
this.stompClient.publish({
|
||||||
|
destination: '/app/chat.addUser',
|
||||||
|
body: JSON.stringify({content: "加入聊天", sender: username, type: 'JOIN'}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
if (this.stompClient) {
|
||||||
|
this.stompClient.deactivate();
|
||||||
|
this.stompClient = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new WebSocketService();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 页面
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||||
|
import wsService from '../utils/websocket.service';
|
||||||
|
|
||||||
|
const messages = ref<{ content: string; sender: string; type: 'CHAT' | 'JOIN' | 'LEAVE' }[]>([]);
|
||||||
|
const messageInput = ref('');
|
||||||
|
const username = ref(''); // 可替换为动态用户名
|
||||||
|
|
||||||
|
// 处理发送消息
|
||||||
|
function handleSendMessage() {
|
||||||
|
const content = messageInput.value.trim();
|
||||||
|
if (!content) return;
|
||||||
|
|
||||||
|
wsService.sendMessage({
|
||||||
|
content,
|
||||||
|
sender: username.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
messageInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接收消息回调
|
||||||
|
function onMessageReceived(msg: { content: string; sender: string; type: 'CHAT' | 'JOIN' | 'LEAVE' }) {
|
||||||
|
messages.value.push(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConnect(){
|
||||||
|
// 建立连接并注册消息回调
|
||||||
|
wsService.connect(username.value, onMessageReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
// 组件卸载前断开连接
|
||||||
|
wsService.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="chat-container">
|
||||||
|
<h2>WebSocket 聊天室</h2>
|
||||||
|
<input v-model="username" placeholder="输入姓名" @keyup.enter="createConnect">
|
||||||
|
<div class="messages">
|
||||||
|
<div v-for="(msg, index) in messages" :key="index" class="message">
|
||||||
|
<strong>{{ msg.sender }}:</strong> {{ msg.content }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
v-model="messageInput"
|
||||||
|
@keyup.enter="handleSendMessage"
|
||||||
|
placeholder="输入消息..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.chat-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.messages {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.message {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
```
|
BIN
src/programming/java/工具箱/assert/1746684922168.png
Normal file
BIN
src/programming/java/工具箱/assert/1746684922168.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user