swift - swift上的简单TCP监听器
问题描述
我向远程计算机上运行的应用程序发送消息。我以这种方式发送消息。
C#:
void web_data_work()
{
try
{
TcpClient client = new TcpClient(address, port);
Byte[] data = Encoding.UTF8.GetBytes(string.Format("{0}", message));
NetworkStream stream = client.GetStream();
try
{
if (message != "")
{
stream.Write(data, 0, data.Length);
Byte[] readingData = new Byte[256];
String responseData = String.Empty;
StringBuilder completeMessage = new StringBuilder();
int numberOfBytesRead = 0;
do
{
numberOfBytesRead = stream.Read(readingData, 0, readingData.Length);
completeMessage.AppendFormat("{0}", Encoding.UTF8.GetString(readingData, 0, numberOfBytesRead));
}
while (stream.DataAvailable);
responseData = completeMessage.ToString();
this.Invoke((MethodInvoker)delegate ()
{
output_list.Items.Add(string.Format("Sended – {0}", responseData));
message = "";
});
}
}
finally
{
stream.Close();
client.Close();
}
}
catch
{
this.Invoke((MethodInvoker)delegate ()
{
output_list.Items.Add("Not sended");
message = "";
});
}
}
迅速:
func web_data_work()
{
let address = address_box.stringValue
let port = port_box.intValue
let task = URLSession.shared.streamTask(withHostName: address, port: Int(port))
let data = message.data(using: .utf8)!
task.write(data as Data, timeout: 0)
{
error in
//om = "Not sended"
//self.output_message()
}
task.resume()
}
在 c# 中,我可以使用TcpListener读取消息,我该如何快速做到这一点?只使用了两个参数:“地址”——消息发送到的计算机的 IP 地址,该计算机的 TcpListener 正在侦听该地址。“端口”——计算机之间发送和接收的端口。
PS最好没有额外的库。
解决方案
您只需使用两个类即可实现此目的:SocketPort
& FileHandle
。
在复制和粘贴下面的示例之前,请阅读我最后的评论。
import Cocoa
class TcpEchoClient {
var readToEndOfFileCompletionHandler: (() -> Void)?
let fileHandle: FileHandle
var observer: NSObjectProtocol?
init(fileHandle: FileHandle) {
self.fileHandle = fileHandle
// Register observer for the data & EOF
self.observer = NotificationCenter.default.addObserver(forName: .NSFileHandleReadToEndOfFileCompletion,
object: fileHandle,
queue: OperationQueue.main) { [weak self] note in
self?.handleReadToEndOfFileCompletion(notification: note)
}
// Instruct the handle to read till the EOF & notify
fileHandle.readToEndOfFileInBackgroundAndNotify()
}
func handleReadToEndOfFileCompletion(notification note: Notification) {
defer {
// No matter what happens, call the completion handle by the end
readToEndOfFileCompletionHandler?()
}
// Is there an error?
if let errorCode = note.userInfo?["NSFileHandleError"] as? NSNumber {
print("Client \(fileHandle.fileDescriptor) error: File handle error \(errorCode.intValue)")
return
}
// No error, we should have data available
guard let data = note.userInfo?[NSFileHandleNotificationDataItem] as? Data else {
print("Client \(fileHandle.fileDescriptor) error: Unable to get data")
return
}
// Convert them to UTF-8 string
guard let text = String(data: data, encoding: .utf8) else {
print("Client \(fileHandle.fileDescriptor) error: Unable to convert data to UTF-8 string")
return
}
// Print the text
print("Client \(fileHandle.fileDescriptor) received: \(text)")
}
deinit {
// Remove observer for the data & EOF
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
// Close the handle
try? fileHandle.close()
}
}
class TcpServer {
let port: SocketPort
let fileHandle: FileHandle
var clients: Dictionary<Int32, TcpEchoClient>
var observer: NSObjectProtocol?
init?(tcpPort: UInt16) {
guard let socketPort = SocketPort(tcpPort: tcpPort) else {
return nil
}
// Keep the socket port around otherwise you'll get error 38
port = socketPort
// No clients for now
clients = [:]
// Create handle from the socket
fileHandle = FileHandle(fileDescriptor: port.socket)
// Register observer for the connection accepted
observer = NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted,
object: fileHandle,
queue: OperationQueue.main) { [weak self] note in
if let handle = note.object as? FileHandle {
// Ask immediately for another accepted connection notification
handle.acceptConnectionInBackgroundAndNotify()
}
self?.handleConnectionAccepted(notification: note)
}
// Instruct the handle to accept connection & notify
fileHandle.acceptConnectionInBackgroundAndNotify()
}
func handleConnectionAccepted(notification note: Notification) {
// Is there an error?
if let errorCode = note.userInfo?["NSFileHandleError"] as? NSNumber {
print("Server error: File handle error \(errorCode.intValue)")
return
}
// No, we should have received the client file handle
guard let clientFileHandle = note.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else {
print("Server error: Unable to get accepted connection file handle")
return
}
let fileDescriptor = clientFileHandle.fileDescriptor
// Create new client from the file handle
let client = TcpEchoClient(fileHandle: clientFileHandle)
// Once it finishes, remove it from the clients dictionary
client.readToEndOfFileCompletionHandler = { [weak self] in
guard let self = self else { return }
self.clients.removeValue(forKey: fileDescriptor)
print("Server: Client removed \(fileDescriptor) (total \(self.clients.count))")
}
// Store the client in the clients dictionary
clients[fileDescriptor] = client
print("Server: New client \(fileDescriptor) added (total \(self.clients.count))")
}
deinit {
// Remove all clients
clients.removeAll()
// Close the file handle
try? fileHandle.close()
// Invalidate the socket port
port.invalidate()
// Remove connection accepted observer
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
}
}
然后创建一个属性 ( var server: TcpServer?
) 并初始化它 ( server = TcpServer(tcpPort: 8080)
)。
测试:
parallel -j 1 echo -n Foo '|' nc 127.0.0.1 8080 ::: {1..4}
控制台输出:
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
注释:
- 这个例子只是一个你可以开始的基本骨架。
- 不要被
parallel
test 命令误导。- 如果我想一次运行多个命令(相同的命令),我正在使用它。
- 此示例中的所有内容都在主队列上运行。
- 阅读我使用的所有这些类、方法和通知的文档。有问题,你必须熟悉运行循环等。
推荐阅读
- qt - Qt:将 QGraphicsPixmapItems 列表添加到场景组?
- sql - 如何提高子查询性能?
- nestjs - 如何在 NestJS 中插入具有 OneToMany 关系的实体?
- vb.net-2010 - Visual basic ListView:如何在第一行禁用自动选择?
- machine-learning - 预定义实体的 NER
- python - 如何使用线性同余生成器
- laravel-5 - 确认屏幕页面后如何存储数据
- amazon-web-services - 单体架构和三层架构有什么区别?
- jquery - 如果 jQuery 已经存在数组中的特定元素,则更新它
- python - 给定元素长度的 Python 子集和问题