Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
smart_hotel_app
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张宏
smart_hotel_app
Commits
9d09840c
Commit
9d09840c
authored
Jun 06, 2026
by
张宏
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
tcp
parent
a1177508
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
391 additions
and
0 deletions
+391
-0
index.dart
lib/utils/tcp/index.dart
+3
-0
index.dart
lib/utils/tcp/service/index.dart
+6
-0
tcp_client_cubit.dart
lib/utils/tcp/service/tcp_client_cubit.dart
+186
-0
tcp_client_state.dart
lib/utils/tcp/service/tcp_client_state.dart
+37
-0
tcp_connection_status.dart
lib/utils/tcp/service/tcp_connection_status.dart
+18
-0
tcp_server_cubit.dart
lib/utils/tcp/service/tcp_server_cubit.dart
+107
-0
tcp_server_state.dart
lib/utils/tcp/service/tcp_server_state.dart
+34
-0
tcp使用手册.md
lib/utils/tcp/tcp使用手册.md
+0
-0
No files found.
lib/utils/tcp/index.dart
0 → 100644
View file @
9d09840c
library
tcp
;
export
'service/index.dart'
;
\ No newline at end of file
lib/utils/tcp/service/index.dart
0 → 100644
View file @
9d09840c
export
'tcp_client_cubit.dart'
;
export
'tcp_client_state.dart'
;
export
'tcp_server_cubit.dart'
;
export
'tcp_server_state.dart'
;
export
'tcp_connection_status.dart'
;
\ No newline at end of file
lib/utils/tcp/service/tcp_client_cubit.dart
0 → 100644
View file @
9d09840c
import
'dart:async'
;
import
'dart:io'
;
import
'dart:typed_data'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'tcp_client_state.dart'
;
import
'tcp_connection_status.dart'
;
class
TcpClientCubit
extends
Cubit
<
TcpClientState
>
{
Socket
?
_socket
;
String
?
_address
;
int
?
_port
;
int
_timeout
=
5000
;
int
_maxRetryCount
=
3
;
int
_retryInterval
=
2000
;
int
_currentRetry
=
0
;
Timer
?
_reconnectTimer
;
Timer
?
_heartbeatTimer
;
bool
_isManualDisconnect
=
false
;
bool
_isConnecting
=
false
;
final
StreamController
<
Uint8List
>
_dataController
=
StreamController
.
broadcast
();
/// 接收到的数据流
Stream
<
Uint8List
>
get
onData
=>
_dataController
.
stream
;
TcpClientCubit
()
:
super
(
const
TcpClientState
());
/// 配置连接参数
void
config
({
required
String
address
,
required
int
port
,
int
timeout
=
5000
,
int
maxRetry
=
3
,
int
retryInterval
=
2000
,
})
{
_address
=
address
;
_port
=
port
;
_timeout
=
timeout
;
_maxRetryCount
=
maxRetry
;
_retryInterval
=
retryInterval
;
emit
(
state
.
copyWith
(
address:
address
,
port:
port
));
}
/// 连接服务器
Future
<
bool
>
connect
()
async
{
if
(
_address
==
null
||
_port
==
null
)
{
emit
(
state
.
copyWith
(
error:
'未配置地址或端口'
));
return
false
;
}
if
(
state
.
isConnected
)
return
true
;
if
(
_isConnecting
)
return
false
;
_isConnecting
=
true
;
_isManualDisconnect
=
false
;
emit
(
state
.
copyWith
(
connectionStatus:
TcpConnectionStatus
.
connecting
,
clearError:
true
));
try
{
_socket
=
await
Socket
.
connect
(
_address
!,
_port
!,
timeout:
Duration
(
milliseconds:
_timeout
),
);
_socket
!.
setOption
(
SocketOption
.
tcpNoDelay
,
true
);
_listenSocket
();
_currentRetry
=
0
;
emit
(
state
.
copyWith
(
connectionStatus:
TcpConnectionStatus
.
connected
));
_startHeartbeat
();
return
true
;
}
catch
(
e
)
{
emit
(
state
.
copyWith
(
connectionStatus:
TcpConnectionStatus
.
error
,
error:
e
.
toString
(),
));
_tryReconnect
();
return
false
;
}
finally
{
_isConnecting
=
false
;
}
}
/// 监听 Socket 数据
void
_listenSocket
()
{
_socket
!.
listen
(
(
data
)
{
final
uint8List
=
Uint8List
.
fromList
(
data
);
if
(!
_dataController
.
isClosed
)
{
_dataController
.
add
(
uint8List
);
}
},
onError:
(
e
)
{
emit
(
state
.
copyWith
(
connectionStatus:
TcpConnectionStatus
.
error
,
error:
e
.
toString
(),
));
_tryReconnect
();
},
onDone:
()
{
if
(!
_isManualDisconnect
)
{
_tryReconnect
();
}
},
);
}
/// 自动重连
void
_tryReconnect
()
{
if
(
_isManualDisconnect
)
return
;
if
(
_currentRetry
>=
_maxRetryCount
)
{
emit
(
state
.
copyWith
(
connectionStatus:
TcpConnectionStatus
.
disconnected
));
return
;
}
_currentRetry
++;
emit
(
state
.
copyWith
(
connectionStatus:
TcpConnectionStatus
.
reconnecting
));
_reconnectTimer
?.
cancel
();
_reconnectTimer
=
Timer
(
Duration
(
milliseconds:
_retryInterval
),
()
{
connect
();
});
}
/// 发送 Uint8List
Future
<
bool
>
send
(
Uint8List
data
)
async
{
if
(
_socket
==
null
||
!
state
.
isConnected
)
return
false
;
try
{
_socket
!.
add
(
data
);
await
_socket
!.
flush
();
return
true
;
}
catch
(
e
)
{
return
false
;
}
}
/// 发送十六进制字符串
Future
<
bool
>
sendHex
(
String
hex
)
async
{
hex
=
hex
.
replaceAll
(
RegExp
(
r'\s+'
),
''
);
if
(
hex
.
length
%
2
!=
0
)
return
false
;
try
{
List
<
int
>
bytes
=
[];
for
(
int
i
=
0
;
i
<
hex
.
length
;
i
+=
2
)
{
bytes
.
add
(
int
.
parse
(
hex
.
substring
(
i
,
i
+
2
),
radix:
16
));
}
return
await
send
(
Uint8List
.
fromList
(
bytes
));
}
catch
(
e
)
{
return
false
;
}
}
/// 发送字符串 UTF8
Future
<
bool
>
sendString
(
String
msg
)
async
{
return
await
send
(
Uint8List
.
fromList
(
msg
.
codeUnits
));
}
/// 心跳保活(每 15 秒发送一次)
void
_startHeartbeat
()
{
_heartbeatTimer
?.
cancel
();
_heartbeatTimer
=
Timer
.
periodic
(
const
Duration
(
seconds:
15
),
(
timer
)
{
if
(
state
.
isConnected
)
{
sendHex
(
'00 00 00 00'
);
}
});
}
/// 手动断开
void
disconnect
()
{
_isManualDisconnect
=
true
;
_reconnectTimer
?.
cancel
();
_heartbeatTimer
?.
cancel
();
_socket
?.
close
();
_socket
=
null
;
emit
(
state
.
copyWith
(
connectionStatus:
TcpConnectionStatus
.
disconnected
));
}
@override
Future
<
void
>
close
()
{
disconnect
();
_dataController
.
close
();
return
super
.
close
();
}
}
\ No newline at end of file
lib/utils/tcp/service/tcp_client_state.dart
0 → 100644
View file @
9d09840c
import
'package:equatable/equatable.dart'
;
import
'tcp_connection_status.dart'
;
class
TcpClientState
extends
Equatable
{
final
TcpConnectionStatus
connectionStatus
;
final
String
?
address
;
final
int
?
port
;
final
String
?
error
;
const
TcpClientState
({
this
.
connectionStatus
=
TcpConnectionStatus
.
disconnected
,
this
.
address
,
this
.
port
,
this
.
error
,
});
TcpClientState
copyWith
({
TcpConnectionStatus
?
connectionStatus
,
String
?
address
,
int
?
port
,
String
?
error
,
bool
clearError
=
false
,
})
{
return
TcpClientState
(
connectionStatus:
connectionStatus
??
this
.
connectionStatus
,
address:
address
??
this
.
address
,
port:
port
??
this
.
port
,
error:
clearError
?
null
:
(
error
??
this
.
error
),
);
}
bool
get
isConnected
=>
connectionStatus
==
TcpConnectionStatus
.
connected
;
@override
List
<
Object
?>
get
props
=>
[
connectionStatus
,
address
,
port
,
error
];
}
\ No newline at end of file
lib/utils/tcp/service/tcp_connection_status.dart
0 → 100644
View file @
9d09840c
/// TCP 连接状态枚举
enum
TcpConnectionStatus
{
/// 未连接
disconnected
,
/// 连接中
connecting
,
/// 已连接
connected
,
/// 重连中
reconnecting
,
/// 错误
error
,
}
\ No newline at end of file
lib/utils/tcp/service/tcp_server_cubit.dart
0 → 100644
View file @
9d09840c
import
'dart:async'
;
import
'dart:io'
;
import
'dart:typed_data'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'tcp_server_state.dart'
;
class
TcpServerCubit
extends
Cubit
<
TcpServerState
>
{
ServerSocket
?
_serverSocket
;
final
List
<
Socket
>
_clients
=
[];
final
StreamController
<
Map
<
String
,
dynamic
>>
_dataController
=
StreamController
.
broadcast
();
/// 接收到的客户端数据流
Stream
<
Map
<
String
,
dynamic
>>
get
onClientData
=>
_dataController
.
stream
;
TcpServerCubit
()
:
super
(
const
TcpServerState
());
/// 设置端口
void
setPort
(
int
port
)
{
emit
(
state
.
copyWith
(
port:
port
));
}
/// 启动服务端
Future
<
void
>
start
()
async
{
try
{
_serverSocket
=
await
ServerSocket
.
bind
(
InternetAddress
.
anyIPv4
,
state
.
port
);
emit
(
state
.
copyWith
(
isRunning:
true
,
port:
_serverSocket
!.
port
));
_listenClients
();
}
catch
(
e
)
{
if
(
e
.
toString
().
contains
(
'already in use'
))
{
emit
(
state
.
copyWith
(
port:
state
.
port
+
1
));
start
();
}
else
{
emit
(
state
.
copyWith
(
error:
e
.
toString
()));
}
}
}
/// 监听客户端连接
void
_listenClients
()
{
_serverSocket
!.
listen
((
client
)
{
_clients
.
add
(
client
);
emit
(
state
.
copyWith
(
connectedClients:
_clients
.
length
));
_handleClient
(
client
);
});
}
/// 处理单个客户端数据
void
_handleClient
(
Socket
client
)
{
client
.
listen
(
(
data
)
{
_dataController
.
add
({
'client'
:
client
,
'address'
:
client
.
remoteAddress
.
address
,
'port'
:
client
.
remotePort
,
'data'
:
Uint8List
.
fromList
(
data
),
});
},
onDone:
()
=>
_removeClient
(
client
),
onError:
(
e
)
=>
_removeClient
(
client
),
);
}
/// 群发字节数据给所有客户端
void
sendToAll
(
Uint8List
data
)
{
for
(
var
c
in
_clients
)
{
try
{
c
.
add
(
data
);
}
catch
(
_
)
{}
}
}
/// 群发十六进制字符串给所有客户端
void
sendHexToAll
(
String
hex
)
{
hex
=
hex
.
replaceAll
(
RegExp
(
r'\s+'
),
''
);
List
<
int
>
bytes
=
[];
for
(
int
i
=
0
;
i
<
hex
.
length
;
i
+=
2
)
{
bytes
.
add
(
int
.
parse
(
hex
.
substring
(
i
,
i
+
2
),
radix:
16
));
}
sendToAll
(
Uint8List
.
fromList
(
bytes
));
}
/// 移除客户端
void
_removeClient
(
Socket
client
)
{
_clients
.
remove
(
client
);
emit
(
state
.
copyWith
(
connectedClients:
_clients
.
length
));
client
.
destroy
();
}
/// 停止服务端
Future
<
void
>
stop
()
async
{
for
(
var
c
in
_clients
)
{
await
c
.
close
();
}
_clients
.
clear
();
await
_serverSocket
?.
close
();
emit
(
state
.
copyWith
(
isRunning:
false
,
connectedClients:
0
));
}
@override
Future
<
void
>
close
()
async
{
await
stop
();
_dataController
.
close
();
return
super
.
close
();
}
}
\ No newline at end of file
lib/utils/tcp/service/tcp_server_state.dart
0 → 100644
View file @
9d09840c
import
'package:equatable/equatable.dart'
;
class
TcpServerState
extends
Equatable
{
final
bool
isRunning
;
final
int
port
;
final
int
connectedClients
;
final
String
?
error
;
const
TcpServerState
({
this
.
isRunning
=
false
,
this
.
port
=
0
,
this
.
connectedClients
=
0
,
this
.
error
,
});
TcpServerState
copyWith
({
bool
?
isRunning
,
int
?
port
,
int
?
connectedClients
,
String
?
error
,
bool
clearError
=
false
,
})
{
return
TcpServerState
(
isRunning:
isRunning
??
this
.
isRunning
,
port:
port
??
this
.
port
,
connectedClients:
connectedClients
??
this
.
connectedClients
,
error:
clearError
?
null
:
(
error
??
this
.
error
),
);
}
@override
List
<
Object
?>
get
props
=>
[
isRunning
,
port
,
connectedClients
,
error
];
}
\ No newline at end of file
lib/utils/tcp/tcp使用手册.md
0 → 100644
View file @
9d09840c
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment