HarmonyOS鸿蒙Next模拟器做tcp/IP服务器,我用PAD程序跟它不能连接
HarmonyOS鸿蒙Next模拟器做tcp/IP服务器,我用PAD程序跟它不能连接 用的是鸿蒙API20,一直不能连接到,估计是IP地址问题,现在服务器监看的是“0.0.0.0”,监看“127.0.0.1”也不行,如果用电脑的IP地址“192.168.0.101”初始化就报错。以下是代码。客户端程序连接我用别的开发的电脑程序一切正常,就是跟模拟器的客户端不能成功连接通讯。
// TcpServerPage.ets
import { TcpServerManager, ClientInfo, ReceivedMessage } from '../TcpServerManager';
import { util } from '[@kit](/user/kit).ArkTS';
import { abilityAccessCtrl, Permissions } from '[@kit](/user/kit).AbilityKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';
@Entry
@Component
struct TcpServerPage {
// TCP服务管理器实例
private serverManager: TcpServerManager = new TcpServerManager();
// 状态变量
@State serverStatus: string = '未启动';
@State serverPort: string = '5798';
@State isRunning: boolean = false;
@State clientList: ClientInfo[] = [];
@State selectedClientId: string = '';
@State logMessages: string[] = [];
@State inputMessage: string = '';
@State broadcastMessage: string = '';
// 权限列表
private requiredPermissions: Array<Permissions> = ['ohos.permission.INTERNET'];
aboutToAppear(): void {
// 请求权限
this.requestPermissions();
// 设置服务端回调
this.setupServerCallbacks();
}
aboutToDisappear(): void {
// 页面销毁时停止服务
this.stopServer();
}
/**
* 请求必要权限
*/
private requestPermissions(): void {
const atManager = abilityAccessCtrl.createAtManager();
atManager.requestPermissionsFromUser(getContext(this), this.requiredPermissions)
.then((data) => {
this.addLog('权限申请成功');
})
.catch((err: BusinessError) => {
this.addLog(`权限申请失败: ${err.message}`);
});
}
/**
* 设置服务端事件回调
*/
private setupServerCallbacks(): void {
// 客户端连接回调
this.serverManager.setOnClientConnected((clientInfo: ClientInfo) => {
this.addLog(`[连接] 客户端 ${clientInfo.id} (${clientInfo.remoteAddress}:${clientInfo.remotePort}) 已连接`);
this.refreshClientList();
});
// 客户端断开回调
this.serverManager.setOnClientDisconnected((clientId: string) => {
this.addLog(`[断开] 客户端 ${clientId} 已断开`);
if (this.selectedClientId === clientId) {
this.selectedClientId = '';
}
this.refreshClientList();
});
// 消息接收回调
this.serverManager.setOnMessageReceived((message: ReceivedMessage) => {
const text = this.serverManager.arrayBufferToString(message.message);
this.addLog(`[收到] 来自 ${message.clientId}: ${text}`);
});
// 服务端错误回调
this.serverManager.setOnServerError((error: Error) => {
this.addLog(`[错误] ${error.message}`);
});
// 服务启动回调
this.serverManager.setOnServerStarted((address: string, port: number) => {
this.serverStatus = `运行中 - ${address}:${port}`;
this.isRunning = true;
this.addLog(`[系统] 服务已启动,监听 ${address}:${port}`);
});
// 服务停止回调
this.serverManager.setOnServerStopped(() => {
this.serverStatus = '未启动';
this.isRunning = false;
this.clientList = [];
this.selectedClientId = '';
this.addLog('[系统] 服务已停止');
});
}
/**
* 刷新客户端列表
*/
private refreshClientList(): void {
this.clientList = this.serverManager.getClients();
}
/**
* 添加日志信息
*/
private addLog(message: string): void {
const timestamp = new Date().toLocaleTimeString();
this.logMessages.unshift(`[${timestamp}] ${message}`);
// 限制日志条数,防止内存溢出
if (this.logMessages.length > 100) {
this.logMessages.pop();
}
}
/**
* 启动服务
*/
private async startServer(): Promise<void> {
const port = parseInt(this.serverPort);
if (isNaN(port) || port < 1 || port > 65535) {
this.addLog('[错误] 端口号无效,请输入 1-65535 之间的数字');
return;
}
try {
await this.serverManager.start(port,"0.0.0.0");
} catch (error) {
this.addLog(`[错误] 启动失败: ${JSON.stringify(error)}`);
}
}
/**
* 停止服务
*/
private async stopServer(): Promise<void> {
try {
await this.serverManager.stop();
} catch (error) {
this.addLog(`[错误] 停止失败: ${JSON.stringify(error)}`);
}
}
/**
* 发送消息给选中的客户端
*/
private async sendToSelectedClient(): Promise<void> {
if (!this.selectedClientId) {
this.addLog('[提示] 请先选择一个客户端');
return;
}
if (!this.inputMessage.trim()) {
this.addLog('[提示] 消息内容不能为空');
return;
}
try {
const data = this.serverManager.stringToArrayBuffer(this.inputMessage);
await this.serverManager.sendToClient(this.selectedClientId, data);
this.addLog(`[发送] 向 ${this.selectedClientId}: ${this.inputMessage}`);
this.inputMessage = '';
} catch (error) {
this.addLog(`[错误] 发送失败: ${JSON.stringify(error)}`);
}
}
/**
* 广播消息
*/
private async broadcastToAll(): Promise<void> {
if (!this.broadcastMessage.trim()) {
this.addLog('[提示] 广播内容不能为空');
return;
}
try {
const data = this.serverManager.stringToArrayBuffer(this.broadcastMessage);
const count = await this.serverManager.broadcast(data);
this.addLog(`[广播] 已向 ${count} 个客户端发送: ${this.broadcastMessage}`);
this.broadcastMessage = '';
} catch (error) {
this.addLog(`[错误] 广播失败: ${JSON.stringify(error)}`);
}
}
/**
* 断开指定客户端
*/
private disconnectClient(clientId: string): void {
this.serverManager.disconnectClient(clientId);
this.addLog(`[操作] 主动断开客户端 ${clientId}`);
this.refreshClientList();
if (this.selectedClientId === clientId) {
this.selectedClientId = '';
}
}
build() {
Column() {
// 标题栏
Text('TCP 服务端')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.width('100%')
.padding({ top: 20, bottom: 10, left: 16, right: 16 })
.textAlign(TextAlign.Center)
// 服务控制区
Column() {
Row() {
Text('端口:')
.fontSize(16)
.width(60)
TextInput({ text: this.serverPort, placeholder: '端口号' })
.type(InputType.Number)
.fontSize(16)
.layoutWeight(1)
.onChange((value: string) => {
this.serverPort = value;
})
.enabled(!this.isRunning)
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 10 })
Row() {
Text(`状态: ${this.serverStatus}`)
.fontSize(14)
.fontColor(this.isRunning ? '#00AA00' : '#666666')
.layoutWeight(1)
if (!this.isRunning) {
Button('启动服务')
.fontSize(14)
.onClick(() => this.startServer())
} else {
Button('停止服务')
.fontSize(14)
.backgroundColor('#FF4444')
.onClick(() => this.stopServer())
}
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 10 })
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.padding({ bottom: 10 })
.backgroundColor('#F1F3F5')
.borderRadius(8)
.margin({ left: 16, right: 16, bottom: 10 })
// 客户端列表
Column() {
Row() {
Text('已连接客户端')
.fontSize(16)
.fontWeight(FontWeight.Medium)
Text(`(${this.clientList.length})`)
.fontSize(14)
.fontColor('#666666')
.margin({ left: 5 })
}
.width('100%')
.padding({ left: 16, right: 16, top: 10, bottom: 5 })
if (this.clientList.length === 0) {
Text('暂无客户端连接')
.fontSize(14)
.fontColor('#999999')
.width('100%')
.textAlign(TextAlign.Center)
.padding({ top: 20, bottom: 20 })
} else {
List() {
ForEach(this.clientList, (client: ClientInfo) => {
ListItem() {
Row() {
Column() {
Text(client.id)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(`${client.remoteAddress}:${client.remotePort}`)
.fontSize(12)
.fontColor('#666666')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Button('断开')
.fontSize(12)
.height(30)
.backgroundColor('#FF8888')
.onClick(() => this.disconnectClient(client.id))
}
.width('100%')
.padding(10)
.backgroundColor(this.selectedClientId === client.id ? '#E3F2FD' : '#FFFFFF')
.borderRadius(4)
.onClick(() => {
this.selectedClientId = client.id;
})
}
.margin({ bottom: 5 })
})
}
.width('100%')
.height(200)
.padding({ left: 16, right: 16 })
}
}
.width('100%')
.margin({ left: 16, right: 16, bottom: 10 })
.backgroundColor('#F1F3F5')
.borderRadius(8)
// 消息发送区
Column() {
Text('发送消息')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.width('100%')
.padding({ left: 16, right: 16, top: 10, bottom: 5 })
// 定向发送
Row() {
Text('目标:')
.fontSize(14)
.width(50)
Text(this.selectedClientId || '未选择')
.fontSize(14)
.fontColor(this.selectedClientId ? '#333333' : '#999999')
.layoutWeight(1)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 5 })
Row() {
TextInput({ text: this.inputMessage, placeholder: '输入消息内容' })
.fontSize(14)
.layoutWeight(1)
.onChange((value: string) => {
this.inputMessage = value;
})
.enabled(this.isRunning && !!this.selectedClientId)
Button('发送')
.fontSize(14)
.margin({ left: 10 })
.enabled(this.isRunning && !!this.selectedClientId)
.onClick(() => this.sendToSelectedClient())
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 10 })
// 广播发送
Row() {
TextInput({ text: this.broadcastMessage, placeholder: '广播消息内容' })
.fontSize(14)
.layoutWeight(1)
.onChange((value: string) => {
this.broadcastMessage = value;
})
.enabled(this.isRunning && this.clientList.length > 0)
Button('广播')
.fontSize(14)
.margin({ left: 10 })
.enabled(this.isRunning && this.clientList.length > 0)
.onClick(() => this.broadcastToAll())
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 10 })
}
.width('100%')
.margin({ left: 16, right: 16, bottom: 10 })
.backgroundColor('#F1F3F5')
.borderRadius(8)
// 日志区域
Column() {
Row() {
Text('消息日志')
.fontSize(16)
.fontWeight(FontWeight.Medium)
Blank()
Button('清空')
.fontSize(12)
.height(28)
.onClick(() => {
this.logMessages = [];
})
}
.width('100%')
.padding({ left: 16, right: 16, top: 10, bottom: 5 })
List() {
ForEach(this.logMessages, (log: string) => {
ListItem() {
Text(log)
.fontSize(12)
.fontColor('#333333')
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.width('100%')
}
})
}
.width('100%')
.layoutWeight(1)
.padding({ left: 16, right: 16, bottom: 10 })
}
.width('100%')
.layoutWeight(1)
.margin({ left: 16, right: 16, bottom: 16 })
.backgroundColor('#F1F3F5')
.borderRadius(8)
}
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
}
}
TcpServerManager.ets
import { socket } from '[@kit](/user/kit).NetworkKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';
import { util } from '[@kit](/user/kit).ArkTS';
export interface ClientInfo {
id: string;
connection: socket.TCPSocketConnection;
remoteAddress: string;
remotePort: number;
connectTime: number;
}
export interface ReceivedMessage {
clientId: string;
message: ArrayBuffer;
timestamp: number;
}
export class TcpServerManager {
private server: socket.TCPSocketServer | null = null;
private clients: Map<string, ClientInfo> = new Map();
private isRunning: boolean = false;
private serverPort: number = 0;
private serverAddress: string = '0.0.0.0';
private onClientConnectedCallback?: (clientInfo: ClientInfo) => void;
private onClientDisconnectedCallback?: (clientId: string) => void;
private onMessageReceivedCallback?: (message: ReceivedMessage) => void;
private onServerErrorCallback?: (error: Error) => void;
private onServerStartedCallback?: (address: string, port: number) => void;
private onServerStoppedCallback?: () => void;
private generateClientId(address: string, port: number): string {
return `${address}:${port}_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
}
arrayBufferToString(buffer: ArrayBuffer): string {
const decoder: util.TextDecoder = new util.TextDecoder('utf-8');
const uint8Array: Uint8Array = new Uint8Array(buffer);
return decoder.decodeWithStream(uint8Array, { stream: false });
}
stringToArrayBuffer(str: string): ArrayBuffer {
const encoder: util.TextEncoder = new util.TextEncoder();
return encoder.encodeInto(str).buffer as ArrayBuffer;
}
async start(port: number, address: string = '0.0.0.0'): Promise<void> {
if (this.isRunning) {
console.warn('服务器已在运行中');
return;
}
try {
this.serverPort = port;
this.serverAddress = address;
this.server = socket.constructTCPSocketServerInstance();
// ✅ 关键修正:回调函数使用 async
this.server.on('connect', async (connection: socket.TCPSocketConnection) => {
await this.handleClientConnection(connection);
});
this.server.on('error', (err: BusinessError) => {
console.error('服务端错误', JSON.stringify(err));
this.onServerErrorCallback?.(new Error(err.message));
});
const netAddress: socket.NetAddress = {
address: this.serverAddress,
port: this.serverPort,
family: 1
};
await this.server.listen(netAddress);
this.isRunning = true;
console.info(`服务器已启动 ${this.serverAddress}:${this.serverPort}`);
this.onServerStartedCallback?.(this.serverAddress, this.serverPort);
} catch (error) {
console.error('启动服务器失败', JSON.stringify(error));
throw new Error(`启动失败: ${JSON.stringify(error)}`);
}
}
private async handleClientConnection(connection: socket.TCPSocketConnection): Promise<void> {
try {
// ✅ 使用 await 获取异步结果
const remoteAddress: socket.NetAddress = await connection.getRemoteAddress();
// 端口校验
console.error('获取客户端端口失败,连接将被拒绝5');
if (remoteAddress.port === undefined) {
console.error('获取客户端端口失败,连接将被拒绝');
connection.close();
更多关于HarmonyOS鸿蒙Next模拟器做tcp/IP服务器,我用PAD程序跟它不能连接的实战教程也可以访问 https://www.itying.com/category-93-b0.html
好的,感谢您的反馈。若后续还有其他问题,欢迎您的提问。
更多关于HarmonyOS鸿蒙Next模拟器做tcp/IP服务器,我用PAD程序跟它不能连接的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
开发者您好,
本地测试手机A,安装上述代码打包生成的应用,手机B安装tcp测试软件,测试可以正常连接。绑定地址为0.0.0.0:5798,权限已配置ohos.permission.INTERNET,手机A(API20)
本地通过两个模拟器测试,通过hdc端口映射,发现也可以正常连接,排除了代码问题。
排查方案:
- 查看PAD连接的ip是否是手机A的IP
- 电脑(同样连该 Wi-Fi)分别
ping手机和 PAD,ping 不通手机,说明手机开启了防火墙或路由器有隔离。或者手机和PAD互相ping试下。 - 尝试更换一个不常用的端口
- 关闭手机代理(如果开启的话)
- PAD连接是拒绝连接还是连接超时
- 手机和PAD连接另一个手机热点测试
- 确保两台设备的 IP 网段一致。有些路由器或多频合一(2.4G/5G)路由器,会将设备分配到不同的虚拟网段,导致看似在一个 Wi-Fi 下其实无法直接寻址。
谢谢您的回复,我这里这个代码还是不通,但我用的另一段基本相同的代码又通了,搞得比较糊涂,后面如果有问题再请教。再次感谢。
开发者您好,
模拟器访问互联网实际上利用的是本地计算机的以太网或者WLAN,与本地计算机共享同一网络资源。模拟器运行在宿主机的虚拟网络中,对外部是不可见的。
- 可以先使用hdc映射主机到模拟器的端口:hdc -t 127.0.0.1:5555 fport tcp:5798 tcp:5798
- 使用hdc -t 127.0.0.1:5555 fport ls查看是否创建成功
- 用 Windows 自带的网络工具把真实 IP 的流量转给回环地址:netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=5798 connectaddress=127.0.0.1 connectport=5798
- netstat -ano | findstr :5798查看应该只有两个结果
- 发起请求可以正常连接。
"ohos.permission.INTERNET"权限也给了
希望HarmonyOS能继续加强在安全性方面的研发,保护用户的隐私和数据安全。
我明白你是把我刚才那段话又发回来了,是想确认步骤对不对,还是照着做了没用/找不到选项?
你直接告诉我这三种情况里哪种:
找不到“开发人员选项”
开发人员里没有“启用5G”
关了还是卡,想别的办法
我按你实际情况给你改对应步骤。
鸿蒙Next模拟器作为TCP/IP服务器时,PAD程序无法连接,通常涉及网络配置问题。请检查模拟器与PAD是否在同一网络段,并确认模拟器防火墙未阻止TCP端口。确保服务器程序正确绑定IP地址(如0.0.0.0)并监听指定端口。PAD客户端需使用模拟器的正确IP和端口进行连接。


