[fix] add server send message to client

This commit is contained in:
wujiawei 2024-01-23 14:50:56 +08:00
parent be9cf25233
commit 2ec8dad5ef
14 changed files with 374 additions and 6 deletions

View File

@ -0,0 +1,28 @@
package wu.framework.lazy.cloud.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import wu.framework.lazy.cloud.heartbeat.common.NettyProxyMsg;
import wu.framework.lazy.cloud.heartbeat.common.advanced.client.AbstractDistributeSingleClientMessageTypeAdvanced;
/**
* 接收服务端发送过来的聊天信息
*/
@Slf4j
@Component
public class ClientDistributeSingleClientMessageTypeAdvanced extends AbstractDistributeSingleClientMessageTypeAdvanced<NettyProxyMsg> {
/**
* 处理当前数据
*
* @param channel 当前通道
* @param nettyProxyMsg 通道数据
*/
@Override
protected void doHandler(Channel channel, NettyProxyMsg nettyProxyMsg) {
byte[] clientId = nettyProxyMsg.getClientId();
byte[] data = nettyProxyMsg.getData();
log.info("接收客户端:{},发送过来的聊天信息:{}",new String( clientId),new String( data ));
}
}

View File

@ -31,6 +31,14 @@ public class HeartbeatClientConfiguration {
public ClientDistributeSingleClientRealAutoReadConnectTypeAdvanced clientDistributeSingleClientRealAutoReadConnectTypeAdvanced(){
return new ClientDistributeSingleClientRealAutoReadConnectTypeAdvanced();
}
/**
* 处理 接收服务端发送过来的聊天信息
* @return ClientDistributeSingleClientMessageTypeAdvanced
*/
@Bean
public ClientDistributeSingleClientMessageTypeAdvanced clientDistributeSingleClientMessageTypeAdvanced(){
return new ClientDistributeSingleClientMessageTypeAdvanced();
}
@Bean
public ClientDistributeSingleClientRealCloseVisitorTypeAdvanced clientDistributeSingleClientRealCloseVisitorTypeAdvanced(){
return new ClientDistributeSingleClientRealCloseVisitorTypeAdvanced();

View File

@ -73,6 +73,13 @@ public class MessageType {
*/
public static final byte REPORT_SINGLE_CLIENT_CLOSE_VISITOR = 0X08;
/**
* 上报 客户端消息到另一个客户端
*
* @see MessageTypeEnums#REPORT_SINGLE_CLIENT_MESSAGE
* @see AbstractReportSingleClientMessage2OtherClientTypeAdvanced
*/
public static final byte REPORT_SINGLE_CLIENT_MESSAGE = 0X09;
/**
* 下发 客户端接收连接成功通知
@ -134,5 +141,11 @@ public class MessageType {
*/
public static final byte DISTRIBUTE_SINGLE_CLIENT_REAL_CLOSE_VISITOR = -0X08;
/**
* 下发 客户端消息
*
* @see MessageTypeEnums#DISTRIBUTE_SINGLE_CLIENT_MESSAGE
* @see AbstractDistributeSingleClientMessageTypeAdvanced
*/
public static final byte DISTRIBUTE_SINGLE_CLIENT_MESSAGE = -0X09;
}

View File

@ -0,0 +1,25 @@
package wu.framework.lazy.cloud.heartbeat.common.advanced.client;
import wu.framework.lazy.cloud.heartbeat.common.NettyProxyMsg;
import wu.framework.lazy.cloud.heartbeat.common.advanced.AbstractChannelTypeAdvanced;
import wu.framework.lazy.cloud.heartbeat.common.advanced.ChannelTypeAdvanced;
import wu.framework.lazy.cloud.heartbeat.common.enums.MessageTypeEnums;
/**
* 下发 客户端关闭代理服务通道
*/
public abstract class AbstractDistributeSingleClientMessageTypeAdvanced<MSG> extends AbstractChannelTypeAdvanced<NettyProxyMsg> implements ChannelTypeAdvanced {
/**
* 是否支持当前类型
*
* @param msg 通道数据
* @return 布尔类型
*/
@Override
public boolean doSupport(NettyProxyMsg msg) {
return MessageTypeEnums.DISTRIBUTE_SINGLE_CLIENT_MESSAGE.getTypeByte() == msg.getType();
}
}

View File

@ -0,0 +1,27 @@
package wu.framework.lazy.cloud.heartbeat.common.advanced.server;
import wu.framework.lazy.cloud.heartbeat.common.NettyProxyMsg;
import wu.framework.lazy.cloud.heartbeat.common.advanced.AbstractChannelTypeAdvanced;
import wu.framework.lazy.cloud.heartbeat.common.advanced.ChannelTypeAdvanced;
import wu.framework.lazy.cloud.heartbeat.common.enums.MessageTypeEnums;
/**
* 服务端处理客户端 关闭一个访客
* REPORT_SINGLE_CLIENT_CLOSE_VISITOR
*/
public abstract class AbstractReportSingleClientMessage2OtherClientTypeAdvanced<MSG> extends AbstractChannelTypeAdvanced<NettyProxyMsg> implements ChannelTypeAdvanced {
/**
* 是否支持当前类型
*
* @param msg 通道数据
* @return 布尔类型
*/
@Override
public boolean doSupport(NettyProxyMsg msg) {
return MessageTypeEnums.REPORT_SINGLE_CLIENT_MESSAGE.getTypeByte() == msg.getType();
}
}

View File

@ -46,6 +46,10 @@ public enum MessageTypeEnums {
* @see AbstractReportSingleClientCloseVisitorTypeAdvanced
*/
REPORT_SINGLE_CLIENT_CLOSE_VISITOR(MessageType.REPORT_SINGLE_CLIENT_CLOSE_VISITOR, "上报 客户端关闭一个访客通道"),
/**
* @see AbstractReportSingleClientMessage2OtherClientTypeAdvanced
*/
REPORT_SINGLE_CLIENT_MESSAGE(MessageType.REPORT_SINGLE_CLIENT_MESSAGE, "上报 客户端消息到另一个客户端"),
/**
* @see AbstractDistributeConnectSuccessNotificationTypeAdvanced
*/
@ -83,6 +87,10 @@ public enum MessageTypeEnums {
* @see AbstractDistributeSingleClientRealCloseVisitorTypeAdvanced
*/
DISTRIBUTE_SINGLE_CLIENT_REAL_CLOSE_VISITOR(MessageType.DISTRIBUTE_SINGLE_CLIENT_REAL_CLOSE_VISITOR, "下发 客户端关闭代理服务通道"),
/**
* @see AbstractDistributeSingleClientMessageTypeAdvanced
*/
DISTRIBUTE_SINGLE_CLIENT_MESSAGE(MessageType.DISTRIBUTE_SINGLE_CLIENT_MESSAGE, "下发 客户端消息"),
;

View File

@ -1,12 +1,12 @@
spring:
lazy:
netty:
# inet-host: 127.0.0.1
# inet-port: 7001
# inet-path: middleground-on-cloud-heartbeat-server
inet-host: 124.222.48.62 # 服务端地址
inet-port: 30676 #服务端端口
inet-host: 127.0.0.1
inet-port: 7001
inet-path: middleground-on-cloud-heartbeat-server
# inet-host: 124.222.48.62 # 服务端地址
# inet-port: 30676 #服务端端口
# inet-path: middleground-on-cloud-heartbeat-server
client-id: local # 客户端ID
data:
redis:

View File

@ -104,4 +104,10 @@ public interface NettyClientStateApplication {
Result<NettyClientState> remove(NettyClientStateRemoveCommand nettyClientStateRemoveCommand);
/**
* 通过客户端心跳通道发送客户端请求
* @param nettyClientMessageCommand 发送请求到客户端
* @return {@link Result<Void>}
*/
Result<Void> sendMessage2HeartbeatClient(NettyClientMessageCommand nettyClientMessageCommand);
}

View File

@ -0,0 +1,36 @@
package wu.framework.lazy.cloud.heartbeat.server.application.command.netty.client.state;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import wu.framework.lazy.cloud.heartbeat.common.enums.NettyClientStatus;
import java.time.LocalDateTime;
/**
* describe 发送请求到客户端
*
* @author Jia wei Wu
* @date 2023/12/27 03:46 下午
* @see com.wu.framework.inner.lazy.persistence.reverse.lazy.ddd.DefaultDDDLazyRemoveCommand
**/
@Data
@Accessors(chain = true)
@Schema(title = "netty_client_message_command", description = "客户端状态")
public class NettyClientMessageCommand {
/**
* 客户端ID
*/
@Schema(description = "客户端ID", name = "clientId", example = "")
private String clientId;
/**
* 发送的消息
*/
@Schema(description = "发送的消息", name = "message", example = "")
private String message;
}

View File

@ -2,7 +2,11 @@ package wu.framework.lazy.cloud.heartbeat.server.application.impl;
import com.wu.framework.response.ResultFactory;
import io.netty.channel.Channel;
import wu.framework.lazy.cloud.heartbeat.common.ChannelContext;
import wu.framework.lazy.cloud.heartbeat.common.MessageType;
import wu.framework.lazy.cloud.heartbeat.common.NettyProxyMsg;
import wu.framework.lazy.cloud.heartbeat.server.application.NettyClientStateApplication;
import wu.framework.lazy.cloud.heartbeat.server.application.assembler.NettyClientStateDTOAssembler;
import wu.framework.lazy.cloud.heartbeat.server.application.command.netty.client.state.NettyClientStateStoryCommand;
@ -15,6 +19,7 @@ import com.wu.framework.inner.lazy.database.expand.database.persistence.domain.L
import com.wu.framework.response.Result;
import jakarta.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;
/**
@ -143,4 +148,28 @@ public class NettyClientStateApplicationImpl implements NettyClientStateApplicat
return nettyClientStateRepository.remove(nettyClientState);
}
/**
* 通过客户端心跳通道发送客户端请求
*
* @param nettyClientMessageCommand 发送请求到客户端
* @return {@link Result<Void>}
*/
@Override
public Result<Void> sendMessage2HeartbeatClient(NettyClientMessageCommand nettyClientMessageCommand) {
// 获取客户端ID
String clientId = nettyClientMessageCommand.getClientId();
String message = nettyClientMessageCommand.getMessage();
ChannelContext.ClientChannel clientChannel = ChannelContext.get(clientId);
if(clientChannel==null){
return ResultFactory.errorOf("客户端:"+clientId+"不存在");
}
// 发送消息到客户端
Channel channel = clientChannel.getChannel();
NettyProxyMsg nettyProxyMsg = new NettyProxyMsg();
nettyProxyMsg.setClientId("服务端");
nettyProxyMsg.setData(message.getBytes(StandardCharsets.UTF_8));
nettyProxyMsg.setType(MessageType.DISTRIBUTE_SINGLE_CLIENT_MESSAGE);
channel.writeAndFlush(nettyProxyMsg);
return ResultFactory.successOf();
}
}

View File

@ -136,4 +136,19 @@ public class NettyClientStateProvider {
public Result<NettyClientState> remove(@ModelAttribute NettyClientStateRemoveCommand nettyClientStateRemoveCommand){
return nettyClientStateApplication.remove(nettyClientStateRemoveCommand);
}
/**
* describe 通过客户端心跳通道发送客户端请求
* @param nettyClientMessageCommand 发送请求到客户端
* @return {@link Result<NettyClientState>}
* @author Jia wei Wu
* @date 2023/12/27 03:46 下午
**/
@Operation(summary = "通过客户端心跳通道发送客户端请求")
@PostMapping("/sendMessage2HeartbeatClient")
public Result<Void> sendMessage2HeartbeatClient(@RequestBody NettyClientMessageCommand nettyClientMessageCommand){
return nettyClientStateApplication.sendMessage2HeartbeatClient(nettyClientMessageCommand);
}
}

View File

@ -11,6 +11,10 @@ module.exports = {
url: "/netty/client/state/remove",
type: "delete",
},
sendMessage2HeartbeatClient: {
url: "/netty/client/state/sendMessage2HeartbeatClient",
type: "post",
},
visitorPage: {
url: "/netty/server/visitor/findPage",
type: "get",

View File

@ -63,9 +63,24 @@
>
{{ buttons.del.name }}
</el-button>
<el-button
v-permission="['sendMessage']"
@click.prevent="handleArouse2SendMessage(row)"
type="primary"
size="small"
>
{{ buttons.sendMessage.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!--发送消息到客户端-->
<cloud-server-send-message2-clinet
v-if="showDialog"
:rowData="rowData"
:showDialog="showDialog"
@closeDialog="handelDialog($event)"
/>
</div>
</template>
<script>
@ -79,6 +94,7 @@ export default {
add: { name: "添加" },
edit: { name: "编辑" },
del: { name: "删除" },
sendMessage: { name: "发送消息" },
export: { name: "导出用户" },
},
// type 0: 1 2
@ -99,10 +115,15 @@ import {
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
import CloudServerSendMessage2Clinet from "@/views/layoutpages/cloud_network/components/CloudServerSendMessage2Clinet.vue";
const { proxy } = getCurrentInstance();
const queryForm = ref(null);
const tableData = ref([]);
const rowData = ref(null);
const showDialog = ref(false);
const params = reactive({
clientId: "",
size: 10,
@ -111,6 +132,25 @@ const params = reactive({
});
const { clientId, size, current, total } = toRefs(params);
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handelDialog = (e) => {
showDialog.value = e;
getDataList();
};
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleArouse2SendMessage = (row = null) => {
showDialog.value = true;
rowData.value = row;
};
/**
* @description:
* @param {*}

View File

@ -0,0 +1,129 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<el-form-item label="客户端ID" prop="clientId">
<el-input v-model="clientId" placeholder="" disabled></el-input>
</el-form-item>
<el-form-item label="发送的消息" prop="describe">
<el-input v-model="message" placeholder="" clearable></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">发送</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
ref,
defineProps,
defineEmits,
onMounted,
} from "vue";
const rules = {
clientId: [
{
required: true,
message: "请输入选择客户端",
trigger: "blur",
},
],
clientTargetIp: [
{
required: true,
message: "请输入客户端目标IP",
trigger: "blur",
},
],
clientTargetPort: [
{
required: true,
message: "请输入客户端目标端口",
trigger: "blur",
},
],
visitorPort: [
{
required: true,
message: "请选择访客端口",
trigger: "blur",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
message: "",
clientId: "",
});
const { clientId, message } = toRefs(form);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value && (clientId.value = rowData.value.clientId);
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res = (res =
await VE_API.cloudNetwork.sendMessage2HeartbeatClient(form));
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
onMounted(async () => {});
</script>
<style lang="scss" scoped></style>