HarmonyOS 网络编程实战:HTTP、WebSocket 与 Socket 通信详解## 一、前言网络通信是现代移动应用的基石。HarmonyOS 为开发者提供了完备的网络编程能力,从高层的 HTTP 请求到底层的 Socket 通信,覆盖了各种网络场景。本文将以 HarmonyOS 5.0.0(API 12)为基础,通过完整可运行的代码示例,深入讲解三种核心网络通信方式。## 二、网络权限配置在 module.json5 中配置网络权限:{
"module": {
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.GET_NETWORK_INFO" }
]
}
}
三、HTTP 请求:@kit.NetworkKit### 3.1 GET 请求import { http } from '@kit.NetworkKit';
@Component
struct HttpGetDemo {
@State response: string = '等待请求...';
@State isLoading: boolean = false;
async fetchData() {
this.isLoading = true;
this.response = '请求中...';
const httpRequest = http.createHttp();
try {
const result = await httpRequest.request(
'https://httpbin.org/get?name=HarmonyOS',
{
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' },
connectTimeout: 10000,
readTimeout: 15000
}
);
if (result.responseCode === 200) {
const data = JSON.parse(result.result as string);
this.response = `请求成功! URL: ${data.url}`;
} else {
this.response = `请求失败: ${result.responseCode}`;
}
} catch (error) {
this.response = `网络错误: ${JSON.stringify(error)}`;
} finally {
this.isLoading = false;
httpRequest.destroy(); // 销毁请求实例
}
}
build() {
Column({ space: 20 }) {
Text('HTTP GET 请求示例').fontSize(24).fontWeight(FontWeight.Bold)
Button('发送 GET 请求').fontSize(16).enabled(!this.isLoading)
.onClick(() => { this.fetchData() })
Text(this.response).fontSize(14).padding(12)
.backgroundColor('#F5F5F5').borderRadius(8).width('100%')
if (this.isLoading) {
LoadingProgress().width(40).height(40).color('#007AFF')
}
}.width('100%').height('100%').padding(20)
}
}
3.2 POST 请求(JSON)import { http } from '@kit.NetworkKit';
@Component
struct HttpPostDemo {
@State response: string = '';
@State isSubmitting: boolean = false;
async submitData(username: string, password: string) {
this.isSubmitting = true;
const httpRequest = http.createHttp();
try {
const requestBody = JSON.stringify({
username, password, client: 'HarmonyOS-App', version: '1.0.0'
});
const result = await httpRequest.request('https://httpbin.org/post', {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token-here'
},
extraData: requestBody,
connectTimeout: 10000,
readTimeout: 15000
});
if (result.responseCode === 200) {
const data = JSON.parse(result.result as string);
this.response = `提交成功! 服务端返回: ${data.json}`;
} else {
this.response = `提交失败: ${result.responseCode}`;
}
} catch (error) {
this.response = `网络错误: ${error}`;
} finally {
this.isSubmitting = false;
httpRequest.destroy();
}
}
build() {
Column({ space: 20 }) {
Text('HTTP POST 请求示例').fontSize(24).fontWeight(FontWeight.Bold)
Button('模拟登录提交').fontSize(16).enabled(!this.isSubmitting)
.onClick(() => { this.submitData('testuser', '123456') })
Text(this.response).fontSize(14).padding(12)
.backgroundColor('#F5F5F5').borderRadius(8).width('100%')
.maxLines(15).textOverflow({ overflow: TextOverflow.Ellipsis })
}.width('100%').height('100%').padding(20)
}
}
3.3 文件上传import { http } from '@kit.NetworkKit';
@Component
struct FileUploadDemo {
@State uploadProgress: number = 0;
@State status: string = '等待上传...';
async uploadFile(filePath: string) {
this.status = '上传中...';
const httpRequest = http.createHttp();
try {
httpRequest.on('dataReceiveProgress', (receivedSize: number, totalSize: number) => {
this.uploadProgress = Math.round((receivedSize / totalSize) * 100);
this.status = `上传中: ${this.uploadProgress}%`;
});
const result = await httpRequest.request('https://httpbin.org/post', {
method: http.RequestMethod.POST,
header: { 'Content-Type': 'multipart/form-data' },
extraData: { file: filePath, description: '测试文件上传' },
connectTimeout: 30000, readTimeout: 60000
});
this.status = result.responseCode === 200 ? '上传成功!' : `上传失败: ${result.responseCode}`;
} catch (error) {
this.status = `错误: ${error}`;
} finally {
httpRequest.destroy();
}
}
build() {
Column({ space: 20 }) {
Text('文件上传示例').fontSize(24).fontWeight(FontWeight.Bold)
Progress({ value: this.uploadProgress, total: 100 }).width('80%')
Text(this.status).fontSize(16).fontColor('#007AFF')
Button('上传文件').fontSize(16)
.onClick(async () => { await this.uploadFile('/data/storage/el2/base/files/demo.txt') })
}.width('100%').padding(20)
}
}
四、WebSocket:实时双向通信### 4.1 WebSocket 客户端import { webSocket } from '@kit.NetworkKit';
@Entry
@Component
struct WebSocketDemo {
@State messages: string\[\] = \[\];
@State connectionStatus: string = '未连接';
@State inputText: string = '';
private ws?: webSocket.WebSocket;
connectWebSocket() {
this.ws = webSocket.createWebSocket();
this.ws.on('open', (err: Object, value: webSocket.OpenResult) => {
this.connectionStatus = '已连接';
});
this.ws.on('message', (err: Object, value: string | ArrayBuffer) => {
const message = typeof value === 'string' ? value : '收到二进制数据';
this.messages.push(`[${new Date().toLocaleTimeString()}] 服务端: ${message}`);
});
this.ws.on('close', (err: Object, value: webSocket.CloseResult) => {
this.connectionStatus = '已断开';
console.info(`WebSocket 关闭: code=${value.code}, reason=${value.reason}`);
});
this.ws.on('error', (err: Object) => {
this.connectionStatus = '连接错误';
console.error(`WebSocket 错误: ${JSON.stringify(err)}`);
});
this.ws.connect('wss://echo.websocket.org', (err: Object, value: boolean) => {
if (!err) {
this.connectionStatus = '已连接';
this.messages.push('[系统] 连接建立成功');
}
});
}
sendMessage() {
if (!this.ws || !this.inputText.trim()) return;
this.ws.send(this.inputText.trim(), (err: Object, value: boolean) => {
if (!err) {
this.messages.push([${new Date().toLocaleTimeString()}] 我: ${this.inputText});
this.inputText = '';
}
});
}
disconnect() {
if (this.ws) {
this.ws.close({ code: 1000, reason: '用户主动断开' });
}
}
build() {
Column({ space: 12 }) {
Row() {
Text('💬 WebSocket 聊天').fontSize(24).fontWeight(FontWeight.Bold)
}.width('100%')
Row({ space: 8 }) {
Circle().width(8).height(8)
.fill(this.connectionStatus === '已连接' ? '#4CAF50' : '#FF4444')
Text(this.connectionStatus).fontSize(14).fontColor('#666')
}
Button(this.connectionStatus === '已连接' ? '断开连接' : '连接服务器')
.fontSize(16)
.backgroundColor(this.connectionStatus === '已连接' ? '#FF4444' : '#007AFF')
.width('100%')
.onClick(() => {
this.connectionStatus === '已连接' ? this.disconnect() : this.connectWebSocket()
})
Divider()
List({ space: 6 }) {
ForEach(this.messages, (msg: string) => {
ListItem() {
Text(msg).fontSize(13)
.fontColor(msg.includes('我:') ? '#007AFF' : '#333')
.width('100%').padding(8)
.backgroundColor(msg.includes('系统') ? '#FFF8E1' :
msg.includes('我:') ? '#E3F2FD' : '#F5F5F5')
.borderRadius(6)
}
})
}.layoutWeight(1).width('100%')
Row({ space: 8 }) {
TextInput({ placeholder: '输入消息...', text: this.inputText })
.layoutWeight(1).height(44)
.onChange((value: string) => { this.inputText = value })
Button('发送').fontSize(14)
.enabled(this.connectionStatus === '已连接' && this.inputText.trim() !== '')
.onClick(() => { this.sendMessage() })
}.width('100%')
}.width('100%').height('100%').padding(16)
}
}
4.2 心跳保活function setupHeartbeat(ws: webSocket.WebSocket, intervalMs: number = 30000) {
let heartbeatTimer: number | null = null;
const startHeartbeat = () => {
heartbeatTimer = setInterval(() => {
if (ws) {
ws.send('ping', (err: Object) => {
if (err) console.error('心跳发送失败, 尝试重连...');
});
}
}, intervalMs);
};
ws.on('message', (err: Object, value: string | ArrayBuffer) => {
if (value === 'pong') console.info('收到 pong');
});
ws.on('open', () => { startHeartbeat() });
ws.on('close', () => {
if (heartbeatTimer !== null) { clearInterval(heartbeatTimer); heartbeatTimer = null }
});
return { startHeartbeat };
}
五、Socket:底层网络通信### 5.1 TCP Socket 客户端import { socket } from '@kit.NetworkKit';
@Entry
@Component
struct TcpSocketDemo {
@State log: string\[\] = \[\];
@State isConnected: boolean = false;
private tcp?: socket.TCPSocket;
addLog(text: string) {
this.log.push([${new Date().toLocaleTimeString()}] ${text});
}
async connectTcp(host: string, port: number) {
this.tcp = socket.constructTCPSocketInstance();
const bindAddress: socket.NetAddress = { address: '0.0.0.0', port: 0, family: 1 };
try {
await this.tcp.bind(bindAddress);
this.addLog('Socket 绑定成功');
const remoteAddress: socket.NetAddress = { address: host, port, family: 1 };
await this.tcp.connect(remoteAddress);
this.isConnected = true;
this.addLog(`已连接到 ${host}:${port}`);
this.tcp.on('message', (value: socket.SocketMessage) => {
const decoder = new util.TextDecoder('utf-8');
const message = decoder.decodeWithStream(new Uint8Array(value.message), { stream: false });
this.addLog(`收到: ${message}`);
});
this.tcp.on('close', () => { this.isConnected = false; this.addLog('连接已关闭') });
this.tcp.on('error', (err: Object) => { this.addLog(`错误: ${JSON.stringify(err)}`) });
await this.tcp.send({ data: 'Hello Server!' });
} catch (error) {
this.addLog(`连接失败: ${error}`);
}
}
async sendData(data: string) {
if (!this.tcp || !this.isConnected) return;
try {
await this.tcp.send({ data });
this.addLog(发送: ${data});
} catch (error) {
this.addLog(发送失败: ${error});
}
}
async disconnect() {
if (this.tcp) { await this.tcp.close(); this.tcp = undefined; this.isConnected = false }
}
build() {
Column({ space: 12 }) {
Text('🔌 TCP Socket 示例').fontSize(24).fontWeight(FontWeight.Bold)
Text(状态: ${this.isConnected ? '🟢 已连接' : '🔴 未连接'}).fontSize(16)
Row({ space: 8 }) {
Button('连接服务器').fontSize(14).enabled(!this.isConnected)
.onClick(() => { this.connectTcp('127.0.0.1', 8080) })
Button('断开连接').fontSize(14).backgroundColor('#FF4444').enabled(this.isConnected)
.onClick(() => { this.disconnect() })
}
Divider()
List({ space: 4 }) {
ForEach(this.log, (msg: string) => {
ListItem() {
Text(msg).fontSize(12).fontFamily('monospace').width('100%')
.padding(6).backgroundColor('#F5F5F5').borderRadius(4)
}
})
}.layoutWeight(1).width('100%')
}.width('100%').height('100%').padding(16)
}
}
六、网络状态监听import { connection } from '@kit.NetworkKit';
@Entry
@Component
struct NetworkMonitor {
@State networkType: string = '检测中...';
@State isOnline: boolean = true;
private netConnection?: connection.NetConnection;
aboutToAppear() {
this.netConnection = connection.createNetConnection();
this.netConnection.register((error: Object) => {
if (error) console.error(`网络监听注册失败: ${error}`);
});
this.netConnection.on('netAvailable', () => { this.isOnline = true });
this.netConnection.on('netUnavailable', () => { this.isOnline = false });
this.netConnection.on('netConnectionPropertiesChange',
(data: connection.NetConnectionProperty) => {
if (data.networkCap) {
const caps = data.networkCap;
if (caps.includes(connection.NetCapability.NET_CAPABILITY_WIFI)) {
this.networkType = 'WiFi';
} else if (caps.includes(connection.NetCapability.NET_CAPABILITY_CELLULAR)) {
this.networkType = '蜂窝网络';
} else {
this.networkType = '以太网';
}
}
}
);
this.checkCurrentNetwork();
}
async checkCurrentNetwork() {
try {
const netHandle = await connection.getDefaultNet();
const netCaps = await connection.getNetCapabilities(netHandle);
if (netCaps.networkCap?.includes(connection.NetCapability.NET_CAPABILITY_WIFI)) {
this.networkType = 'WiFi';
} else if (netCaps.networkCap?.includes(connection.NetCapability.NET_CAPABILITY_CELLULAR)) {
this.networkType = '蜂窝网络';
}
} catch (error) {
console.error(获取网络状态失败: ${error});
}
}
build() {
Column({ space: 16 }) {
Text('📶 网络状态监控').fontSize(24).fontWeight(FontWeight.Bold)
Row({ space: 12 }) {
Text('网络状态:').fontSize(16)
Circle().width(10).height(10).fill(this.isOnline ? '#4CAF50' : '#FF4444')
Text(this.isOnline ? '在线' : '离线').fontSize(16)
.fontColor(this.isOnline ? '#4CAF50' : '#FF4444')
}
Row({ space: 12 }) {
Text('网络类型:').fontSize(16)
Text(this.networkType).fontSize(16).fontWeight(FontWeight.Medium).fontColor('#007AFF')
}
Button('刷新网络状态').fontSize(16)
.onClick(() => { this.checkCurrentNetwork() })
}.width('100%').padding(20)
}
}
七、完整实战:天气查询应用import { http, connection } from '@kit.NetworkKit';
interface WeatherData {
city: string; temperature: string; weather: string;
humidity: string; wind: string;
}
@Entry
@Component
struct WeatherApp {
@State weather: WeatherData = { city: '', temperature: '', weather: '', humidity: '', wind: '' };
@State isLoading: boolean = false;
@State error: string = '';
@State isOnline: boolean = true;
async fetchWeather(city: string) {
if (!this.isOnline) { this.error = '网络未连接'; return }
this.isLoading = true; this.error = '';
const httpRequest = http.createHttp();
try {
const result = await httpRequest.request(
`https://wttr.in/${encodeURIComponent(city)}?format=j1`,
{ method: http.RequestMethod.GET, connectTimeout: 10000, readTimeout: 15000 }
);
if (result.responseCode === 200) {
const data = JSON.parse(result.result as string);
const current = data.current_condition[0];
this.weather = {
city,
temperature: `${current.temp_C}°C`,
weather: current.weatherDesc[0].value,
humidity: `${current.humidity}%`,
wind: `${current.winddir16Point} ${current.windspeedKmph}km/h`
};
} else {
this.error = `请求失败: ${result.responseCode}`;
}
} catch (error) {
this.error = `网络错误: ${error}`;
} finally {
this.isLoading = false;
httpRequest.destroy();
}
}
build() {
Column({ space: 16 }) {
Text('🌤 天气查询').fontSize(28).fontWeight(FontWeight.Bold)
if (!this.isOnline) {
Text('⚠️ 网络未连接').fontSize(14).fontColor('#FF4444')
.padding(8).backgroundColor('#FFEBEE').borderRadius(8).width('100%')
}
Row({ space: 8 }) {
TextInput({ placeholder: '输入城市名, 如: Beijing' }).layoutWeight(1).height(44)
}.width('100%')
Button('查询天气').fontSize(16).enabled(this.isOnline && !this.isLoading).width('100%')
.onClick(() => { this.fetchWeather('Beijing') })
if (this.isLoading) { LoadingProgress().width(40).height(40) }
if (this.error) { Text(this.error).fontSize(14).fontColor('#FF4444') }
if (this.weather.temperature) {
Column({ space: 12 }) {
Text(`📍 ${this.weather.city}`).fontSize(22).fontWeight(FontWeight.Bold)
Text(this.weather.temperature).fontSize(48).fontWeight(FontWeight.Bold).fontColor('#007AFF')
Text(this.weather.weather).fontSize(18).fontColor('#666')
Row({ space: 24 }) {
Text(`💧 ${this.weather.humidity}`).fontSize(14).fontColor('#888')
Text(`💨 ${this.weather.wind}`).fontSize(14).fontColor('#888')
}
}
.width('100%').padding(24).backgroundColor('#F0F8FF').borderRadius(16)
.alignItems(HorizontalAlign.Center)
}
}.width('100%').height('100%').padding(20)
}
}