I have problem to read lines. When server (server code in java) sent more than one line code, my secket can not read all lines.
I used this library https://github.com/swiftsocket/SwiftSocket
and socket read method code:
private func sendRequest(data: String, client: TCPClient?) -> (String?) {
// It use ufter connection
if client != nil {
// Send data (WE MUST ADD TO SENDING MESSAGE '\n' )
let (isSuccess, errorMessage) = client!.send(str: "\(data)\n")
if isSuccess {
// Read response data
let data = client!.read(1024*10)
if let d = data {
// Create String from response data
if let str = NSString(bytes: d, length: d.count, encoding: NSUTF8StringEncoding) as? String {
return (data: str)
} else {
return (data: nil)
}
} else {
return (data: nil)
}
} else {
print(errorMessage)
return (data: nil)
}
} else {
return (data: nil)
}
}
I have changed like this : let data = client!.read(1024*40) but again can not read (received data large). it read some received data like this:
338C6EAAD0740ED101860EFBA286A653F42793CF1FC7FB55D244C010941BDE3DCA540223B291639D1CD7285B4240B330EBC7C002003F957790749256D54EAC4DB7FC0AD5E2970FE951DFA0A1635A93DFCB031DBA642BA928B8327B661F9F4F22CA657AE803B25A208C23F72D6F934B95558108C1187F90E6D8DE13F9367534E7EC28DBA5AC6C8597800154033B63A3B2185DF68ABF67BB76AC6B593C52E5D7B0A5D7674BEC7C6AA9B4C57343C32C944FEB5D1C2D6C2A400D454196A0029C23DB30F1B6049423DC5BEE728E03C275F207639C25E226A38A23EAE04AD673132336D9E113FC6CA32DAD1D75191BCE40A281D40549C1D6FDD23BC5A38B472ED1EEB6BA1D80D00EF2A08F4729FD05329993A6AE58C34253660A77C20139C73FE0E4A68D9857E02C3F8589A61B22C4E26B3DA00098158FB6CE0C43F271ABC823BF9AD7DBEB32A4E9BE4E90C284E1FD633956F82EB5387DE2E8D7626C88900D183C7E1F683C27B8A654EE75017E897D11F2431A9AB4C0662CC91B3897D52E630A788A1A8D552ABB04916B5E52A1382DB2803A796A96ACC50C00913C650393AA919100E6477B7D88066FEB6C78D4F5853122AB6D7309540053B9DB98BEC0D518CD8BF41620506E1224FB0F8B240B7E9FD60649E703FA9E6A21E785BB0F646DB028F5C5E64697E41A857F6A2A459B2C1B070C244F7B6FD252FFF016CC0A4F0457711213C025E6165706DF6C35C6C38F53610373C3DE99DFE426102860E2D4CD1BF5F6B97346655F0E26103CD6BF37AFCE705D4D2F78F7F40C5316143725D0D7FB6647A92F98A42570F423D646DC2CA726ABB16ED6C62C
but not all. How to fix this. Please, advise me. Thanks
there is your responsibility to check what did you received and how to process all data received from your socket. try to check this simple example how to read line delimited text from network. you can easily adopt this code for use with your library, or use it as is if you have working socket handle ...
import Darwin
private var line = [UInt8]()
private var excess = [UInt8]()
private func readLine(inout lineBufer: [UInt8], inout excessBufer: [UInt8], var sockHandle: Int32, wait: Bool = true) -> Int{
// (1) received bytes in one cycle and numbers of cycles to receive
// whole line (line delimited text)
var received = 0
var i = 0
// (2) clear the buffer for the extra bytes readed after the \n
excessBufer.removeAll()
var buffer = [UInt8](count: 64, repeatedValue: 0)
// (3) read from network until at least one \n is found
repeat {
received = read(sockHandle, &buffer, buffer.count)
if received == 0 {
close(sockHandle)
sockHandle = -1
print("connection lost")
return -1
}
if received < 0 {
print("\treceived failed:", String.fromCString(strerror(errno))!)
return -1
}
i = 0
while i < received && buffer[i] != UInt8(ascii: "\n") { i++ }
lineBufer.appendContentsOf(buffer[0..<i])
// (4) now consume '\n'
i++
// (5) we have extra bytes for next line received -i > 0
if i < received {
excessBufer.appendContentsOf(buffer[i..<received])
break // from loop
}
// (6) if no extra bytes and no whole line, than received - i < 0 (-1)
// if whole line and no extra bytes, than received - i = 0
} while i != received
return received
}
// (1) prepare buffers
line.removeAll()
line.appendContentsOf(excess)
// (2) read line delimited text ( socket is you uderlying socket handle )
readLine(&line, excessBufer: &excess, sockHandle: socket)
// now your line buffer consist of one line ot text
// do something with the line and repeate the proces as you need .....
// (1) prepare buffers
line.removeAll()
line.appendContentsOf(excess)
// (2) read line delimited text
readLine(&line, excessBufer: &excess, sockHandle: socket)
Looking at the GitHub repository you mentioned, I'm under the impression that this is a relatively new and not well-supported project. Yes, it may provide a somewhat simplified interface to sockets, but it is unknown how buggy it is and what its future is. Instead of using it, one might be better off using well-supported Apple Objective-C frameworks. Please see
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/UsingSocketsandSocketStreams.html
https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/CommonPitfalls/CommonPitfalls.html
It is fairly easy to use the Foundation framework with Swift. Yes, you can definitely call POSIX C networking API from Swift, but that is more work and is not recommended in iOS, as it doesn't activate the cellular radio in the device, as explained in the aforementioned Apple articles.
Related
I tried the following code to create a framer that splits a stream of ASCII bytes into frames separated by the pipe ascii character: "|".
import Network
fileprivate let pipe = Character("|").asciiValue!
class PipeFramer: NWProtocolFramerImplementation {
static let label = "Pipe framer"
static let definition = NWProtocolFramer.Definition(implementation: PipeFramer.self)
var minLengthUntilNextMessage = 1 {
didSet { print("client: minLength set to", minLengthUntilNextMessage) }
}
required init(framer: NWProtocolFramer.Instance) {}
func start(framer: NWProtocolFramer.Instance) -> NWProtocolFramer.StartResult { .ready }
func handleInput(framer: NWProtocolFramer.Instance) -> Int {
while true {
var delimiterPosition: Int?
_ = framer.parseInput(minimumIncompleteLength: minLengthUntilNextMessage, maximumLength: 65535) { buffer, endOfMessage in
if let buffer = buffer {
print("client: parsing buffer: \"\(String(bytes: buffer, encoding: .utf8) ?? buffer.debugDescription)\"")
if let indexOfDelimiter = buffer.firstIndex(of: pipe) {
minLengthUntilNextMessage = 1
delimiterPosition = indexOfDelimiter
} else {
minLengthUntilNextMessage = buffer.count + 1
}
} else {
print("client: no buffer")
}
return 0
}
if let length = delimiterPosition {
guard framer.deliverInputNoCopy(length: length, message: .init(instance: framer), isComplete: true) else {
return 0
}
_ = framer.parseInput(minimumIncompleteLength: 1, maximumLength: 65535) { _,_ in 1 }
} else {
return minLengthUntilNextMessage
}
}
}
func handleOutput(framer: NWProtocolFramer.Instance, message: NWProtocolFramer.Message, messageLength: Int, isComplete: Bool) {
try! framer.writeOutputNoCopy(length: messageLength)
framer.writeOutput(data: [pipe])
}
func wakeup(framer: NWProtocolFramer.Instance) {}
func stop(framer: NWProtocolFramer.Instance) -> Bool { return true }
func cleanup(framer: NWProtocolFramer.Instance) { }
}
The problem is that from the moment I get a chunk that does not end with "|", the framer gets stuck on that chunk. So the other chunks that come after this incomplete chunk never fully arrive in the framer.parseInput(...) call. Because it always parses chunks of minimumIncompleteLength and hence never arrives to the point where the next "|" is.
Here is a simple reproduction of this problem:
Create a TCP server
Setup the server so that it sends chunks of messages when a client connects.
Connect to the server (created in 1.) using the framer from above.
Start receiving messages.
Swift Code:
import Network
let client = DispatchQueue(label: "Server")
let server = DispatchQueue(label: "Client")
let networkParameters = NWParameters.tcp
networkParameters.defaultProtocolStack.applicationProtocols.insert(NWProtocolFramer.Options(definition: PipeFramer.definition), at: 0)
let server = try! NWListener(using: .tcp)
server.newConnectionHandler = { connection in
print("server: new connection from", connection.endpoint)
print("server (client \(connection.endpoint)): state", connection.state)
connection.viabilityUpdateHandler = { viable in
print("server (client \(connection.endpoint)): state", connection.state)
if viable {
print("server: sending")
connection.send(content: "A|Be||Sea".data(using: .utf8)!, isComplete: false, completion: .idempotent)
serverQueue.asyncAfter(deadline: .now() + 5) {
print("server: sending second part")
connection.send(content: " is longer than expected|0|".data(using: .utf8)!, isComplete: true, completion: .idempotent)
}
serverQueue.asyncAfter(deadline: .now() + 8) {
print("server: sending last part")
connection.send(content: "Done|".data(using: .utf8)!, isComplete: true, completion: .idempotent)
}
}
}
connection.start(queue: serverQueue)
}
server.stateUpdateHandler = { state in
print("server:", state)
if state == .ready, let port = server.port {
print("server: listening on", port)
}
}
server.start(queue: serverQueue)
let client = NWConnection(to: .hostPort(host: "localhost", port: server.port!), using: networkParameters)
func receiveNext() {
client.receiveMessage { (data, context, complete, error) in
let content: String
if let data = data {
content = String(data: data, encoding: .utf8) ?? data.description
} else {
content = data?.debugDescription ?? "<no data>"
}
print("client: received \"\(content)\"", context.debugDescription, complete, error?.localizedDescription ?? "No error")
receiveNext()
}
}
client.stateUpdateHandler = { state in
print("client:", state)
if state == .ready {
print("client: receiving")
receiveNext()
}
}
client.start(queue: clientQueue)
Results in:
server: waiting(POSIXErrorCode: Network is down)
server: ready
server: listening on 54894
client: preparing
client: ready
client: receiving
server: new connection from ::1.53179
server (client ::1.53179): state setup
server (client ::1.53179): state ready
server: sending
client: parsing buffer: "A|Be||Sea"
client: minLength set to 1
client: parsing buffer: "Be||Sea"
client: minLength set to 1
client: parsing buffer: "|Sea"
client: minLength set to 1
client: parsing buffer: "Sea"
client: minLength set to 4
client: parsing buffer: ""
client: minLength set to 1
client: received "A" Optional(Network.NWConnection.ContentContext) true No error
client: received "Be" Optional(Network.NWConnection.ContentContext) true No error
client: received "<no data>" Optional(Network.NWConnection.ContentContext) true No error
client: parsing buffer: "Sea"
client: minLength set to 4
server: sending second part
client: parsing buffer: "Sea "
client: minLength set to 5
client: parsing buffer: "Sea i"
client: minLength set to 6
server: sending last part
client: parsing buffer: "Sea is"
client: minLength set to 7
client: parsing buffer: "Sea is "
client: minLength set to 8
Notice that the fourth and fifth message are never received by the client. How should I write the Framer so that it receives messages after an incoming incomplete chunk?
References
A swift package containing the above code
Corresponding discussion on Developer Forums
I had exactly the same problem... The network protocol that I was working with also had a simple delimiter that separated each 'message' and the protocol had no header that told me what to expect. Often at the end of the buffer, there was only a partial message with no delimiter and needed to read more bytes to get the remainder of the message. Something like this:
| PACKET A | PACKET B |
|<message>|<message>|<message><mess...age>|<message><message><message><m...essage>
1 2 4 5a 5b 6 7 8 9a 9b
Note:
delimiter = | - single character
lhsMessage = message 5a
rhsMessage = message 5b
Even after watching WWDC and looking at the other examples from Apple, I still do not completely understand how handleInput and parseInput are supposed to function.
I assumed I could simply return from handleInput with the (lhsMessage.count + 1) and it would keep the partial message in the current buffer AND add additional bytes into the buffer (ie from PACKET B) that parseInput could inspect.
However, it does appear to work that way. Instead I ended up storing the value of lhsMessage in a class var and then returned lhsMessage.count from parseInput, which I believe moves the ‘cursor’ in the buffer to end and forces handleInput to get the new packet (ie packet B).
As part of parseInput, I then check if I have a lhsMessage and then assume if I find a delimiter that it is in fact rhsMessage. I then join LHS and RHS to create a completeMessage. At this point, I also return from parseInput the value of (rhsMessage.count + 1) to move the cursor along again.
Now to send this completeMessage I could not use deliverInputNoCopy as the bytes that make up completeMessage were no longer in the buffer :-)
Instead handleInput sent the message back using deliverInput.
I send messages to applications running on remote computers.
I send messages this ways.
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 = "";
});
}
}
swift:
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()
}
In c# i can read messages using TcpListener, how do i do it in swift?
Only two parameters are used:
"address" – ip address of the computer to which messages are sent and which is listened to by the TcpListener of that computer.
"port" – port of the sending and receiving between computers.
P.S. Preferably without additional libraries.
You can achieve this with just two classes: SocketPort & FileHandle.
Before you copy & paste the example below, please, read my comments at the end.
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)
}
}
}
Then create a property (var server: TcpServer?) and initialize it (server = TcpServer(tcpPort: 8080)).
Test:
parallel -j 1 echo -n Foo '|' nc 127.0.0.1 8080 ::: {1..4}
Console output:
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)
Comments:
This example is only a basic skeleton you can start with.
Don't be misleaded with the parallel test command.
I'm using it if I want to run multiple commands at once (same commands).
Everything in this example operates on the main queue.
Read documentation of all these classes, methods and notifications I do use. There're gotchas, you have to be familiar with run loops, etc.
I'm working on a driver that will read data from the network. It doesn't know how much is in a response, other than that when it tries to read and gets 0 bytes back, it is done. So my blocking Swift code looks naively like this:
func readAllBlocking() -> [Byte] {
var buffer: [Byte] = []
var fullBuffer: [Byte] = []
repeat {
buffer = read() // synchronous, blocking
fullBuffer.append(buffer)
} while buffer.count > 0
return fullBuffer
}
How can I rewrite this as a promise that will keep on running until the entire result is read? After trying to wrap my brain around it, I'm still stuck here:
func readAllNonBlocking() -> EventLoopFuture<[Byte]> {
///...?
}
I should add that I can rewrite read() to instead of returning a [Byte] return an EventLoopFuture<[Byte]>
Generally, loops in synchronous programming are turned into recursion to get the same effect with asynchronous programming that uses futures (and also in functional programming).
So your function could look like this:
func readAllNonBlocking(on eventLoop: EventLoop) -> EventLoopFuture<[Byte]> {
// The accumulated chunks
var accumulatedChunks: [Byte] = []
// The promise that will hold the overall result
let promise = eventLoop.makePromise(of: [Byte].self)
// We turn the loop into recursion:
func loop() {
// First, we call `read` to read in the next chunk and hop
// over to `eventLoop` so we can safely write to `accumulatedChunks`
// without a lock.
read().hop(to: eventLoop).map { nextChunk in
// Next, we just append the chunk to the accumulation
accumulatedChunks.append(contentsOf: nextChunk)
guard nextChunk.count > 0 else {
promise.succeed(accumulatedChunks)
return
}
// and if it wasn't empty, we loop again.
loop()
}.cascadeFailure(to: promise) // if anything goes wrong, we fail the whole thing.
}
loop() // Let's kick everything off.
return promise.futureResult
}
I would like to add two things however:
First, what you're implementing above is to simply read in everything until you see EOF, if that piece of software is exposed to the internet, you should definitely add a limit on how many bytes to hold in memory maximally.
Secondly, SwiftNIO is an event driven system so if you were to read these bytes with SwiftNIO, the program would actually look slightly differently. If you're interested what it looks like to simply accumulate all bytes until EOF in SwiftNIO, it's this:
struct AccumulateUntilEOF: ByteToMessageDecoder {
typealias InboundOut = ByteBuffer
func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState {
// `decode` will be called if new data is coming in.
// We simply return `.needMoreData` because always need more data because our message end is EOF.
// ByteToMessageHandler will automatically accumulate everything for us because we tell it that we need more
// data to decode a message.
return .needMoreData
}
func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState {
// `decodeLast` will be called if NIO knows that this is the _last_ time a decode function is called. Usually,
// this is because of EOF or an error.
if seenEOF {
// This is what we've been waiting for, `buffer` should contain all bytes, let's fire them through
// the pipeline.
context.fireChannelRead(self.wrapInboundOut(buffer))
} else {
// Odd, something else happened, probably an error or we were just removed from the pipeline. `buffer`
// will now contain what we received so far but maybe we should just drop it on the floor.
}
buffer.clear()
return .needMoreData
}
}
If you wanted to make a whole program out of this with SwiftNIO, here's an example that is a server which accepts all data until it sees EOF and then literally just writes back the number of received bytes :). Of course, in the real world you would never hold on to all the received bytes to count them (you could just add each individual piece) but I guess it serves as an example.
import NIO
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer {
try! group.syncShutdownGracefully()
}
struct AccumulateUntilEOF: ByteToMessageDecoder {
typealias InboundOut = ByteBuffer
func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState {
// `decode` will be called if new data is coming in.
// We simply return `.needMoreData` because always need more data because our message end is EOF.
// ByteToMessageHandler will automatically accumulate everything for us because we tell it that we need more
// data to decode a message.
return .needMoreData
}
func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState {
// `decodeLast` will be called if NIO knows that this is the _last_ time a decode function is called. Usually,
// this is because of EOF or an error.
if seenEOF {
// This is what we've been waiting for, `buffer` should contain all bytes, let's fire them through
// the pipeline.
context.fireChannelRead(self.wrapInboundOut(buffer))
} else {
// Odd, something else happened, probably an error or we were just removed from the pipeline. `buffer`
// will now contain what we received so far but maybe we should just drop it on the floor.
}
buffer.clear()
return .needMoreData
}
}
// Just an example "business logic" handler. It will wait for one message
// and just write back the length.
final class SendBackLengthOfFirstInput: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
typealias OutboundOut = ByteBuffer
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
// Once we receive the message, we allocate a response buffer and just write the length of the received
// message in there. We then also close the channel.
let allData = self.unwrapInboundIn(data)
var response = context.channel.allocator.buffer(capacity: 10)
response.writeString("\(allData.readableBytes)\n")
context.writeAndFlush(self.wrapOutboundOut(response)).flatMap {
context.close(mode: .output)
}.whenSuccess {
context.close(promise: nil)
}
}
func errorCaught(context: ChannelHandlerContext, error: Error) {
print("ERROR: \(error)")
context.channel.close(promise: nil)
}
}
let server = try ServerBootstrap(group: group)
// Allow us to reuse the port after the process quits.
.serverChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1)
// We should allow half-closure because we want to write back after having received an EOF on the input
.childChannelOption(ChannelOptions.allowRemoteHalfClosure, value: true)
// Our program consists of two parts:
.childChannelInitializer { channel in
channel.pipeline.addHandlers([
// 1: The accumulate everything until EOF handler
ByteToMessageHandler(AccumulateUntilEOF(),
// We want 1 MB of buffering max. If you remove this parameter, it'll also
// buffer indefinitely.
maximumBufferSize: 1024 * 1024),
// 2: Our "business logic"
SendBackLengthOfFirstInput()
])
}
// Let's bind port 9999
.bind(to: SocketAddress(ipAddress: "127.0.0.1", port: 9999))
.wait()
// This will never return.
try server.closeFuture.wait()
Demo:
$ echo -n "hello world" | nc localhost 9999
11
I would like to use some C code that uses a file descriptor.
Background is that I would like to read some data from cgraph library.
public extension UnsafeMutablePointer where Pointee == Agraph_t {
func saveTo(fileName: String) {
let f = fopen(cString(fileName), cString("w"))
agwrite(self,f)
fsync(fileno(f))
fclose(f)
}
}
I would like to have the file output, but without writing to a temp file. Hence, I would like to do something like this:
public extension UnsafeMutablePointer where Pointee == Agraph_t {
var asString: String {
let pipe = Pipe()
let fileDescriptor = UnsafeMutablePointer<Int32>.allocate(capacity: 1)
fileDescriptor.pointee = pipe.fileHandleForWriting.fileDescriptor
agwrite(self, fileDescriptor)
let data = pipe.fileHandleForReading.readDataToEndOfFile()
if let output = String(data: data, encoding: .utf8) {
return output
}
return ""
}
}
But it doesn't work, resulting in a EXC_BAD_ACCESS within agwrite(,). What do I need to do instead?
Many thanks in advance!
File descriptors and file pointers are not the same thing. It's confusing, and made even more frustrating by the fact that FILE * is really hard to Google because of the symbol.
You need to fdopen the file descriptor (pipe.fileHandleForWriting.fileDescriptor), to receive a FILE * (UnsafeMutablePointer<FILE> in Swift). This is what you then pass to agwrite.
It's important to fclose the file pointer when you're done writing to it, otherwise .readDataToEndOfFile() will never terminate. I made a helper function to ensure the fclose can't be forgetten. It's possible that agwrite closes the file pointer itself, internally. If that's the case, you should delete this code and just give it the result of fdopen, plain and simple.
import Foundation
public typealias Agraph_t = Int // Dummy value
public struct AGWriteWrongEncoding: Error { }
func agwrite(_: UnsafeMutablePointer<Agraph_t>, _ filePointer: UnsafeMutablePointer<FILE>) {
let message = "This is a stub."
_ = message.withCString { cString in
fputs(cString, stderr)
}
}
#discardableResult
func use<R>(
fileDescriptor: Int32,
mode: UnsafePointer<Int8>!,
closure: (UnsafeMutablePointer<FILE>) throws -> R
) rethrows -> R {
// Should prob remove this `!`, but IDK what a sensible recovery mechanism would be.
let filePointer = fdopen(fileDescriptor, mode)!
defer { fclose(filePointer) }
return try closure(filePointer)
}
public extension UnsafeMutablePointer where Pointee == Agraph_t {
func asString() throws -> String {
let pipe = Pipe()
use(fileDescriptor: pipe.fileHandleForWriting.fileDescriptor, mode: "w") { filePointer in
agwrite(self, filePointer)
}
let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard let output = String(data: data, encoding: .utf8) else {
throw AGWriteWrongEncoding()
}
return output
}
}
let ptr = UnsafeMutablePointer<Agraph_t>.allocate(capacity: 1) // Dummy value
print(try ptr.asString())
Several other things:
Throwing an error is probably a better choice than returning "". Empty strings aren't a good error handling mechanism. Returning an optional would also work, but it's likely to always be force unwrapped, anyway.
readDataToEndOfFile is a blocking call, which can lead to a bad use experience. It's probably best that this code be run on a background thread, or use a FileHandle.readabilityHandler to asynchronously consume the data as it comes in.
I'm trying to read data from a telnet session in golang. I wrote the following functions in an attempt to accomplish this.
Initially I was having an issue where I was reading from a socket with no data so it would lock and never return. BufferSocketData is my attempt to work around this issue as I can't know if there is data to read. The idea is it will wait 1 second before determining there is not data in the socket and return an empty string.
GetData seems to work the first time there is new data in the buffer, but beyond that it gets no new data. I'm sure this has something to do with my use of goroutines and channels, I'm new to go and I'm sure I'm not using them correctly.
Any ideas as to why my subsequent reads return no data?
/*
ReadDataFromSocket - Attempts to read any data in the socket.
*/
func ReadDataFromSocket(sock io.Reader, c chan string) {
var recvData = make([]byte, 1024)
var numBytes, _ = sock.Read(recvData)
c <- string(recvData[:numBytes])
}
/*
BufferSocketData - Read information from the socket and store it in the buffer.
*/
func (tn *TelnetLib) BufferSocketData(inp chan string, out chan string) {
var data string
var timeout int64 = 1000 // 1 second timeout.
var start = utils.GetTimestamp()
for utils.GetTimestamp()-start < timeout {
select {
case data = <-inp:
default:
}
if data != "" {
break
}
}
out <- data
}
/*
GetData - Start goroutines to get and buffer data.
*/
func (tn *TelnetLib) GetData() {
var sockCh = make(chan string)
var buffCh = make(chan string)
go ReadDataFromSocket(tn.Conn, sockCh)
go tn.BufferSocketData(sockCh, buffCh)
var data = <-buffCh
if data != "" {
tn.Buffer += data
}
}
Please let me know if you need any additional information.
Use SetReadDeadline to read data with a time limit:
func (tn *TelnetLib) GetData() {
tn.Conn.SetReadDeadline(time.Second)
recvData := make([]byte, 1024)
n, err := tn.Conn.Read(recvData)
if n > 0 {
// do something with recvData[:n]
}
if e, ok := err.(interface{ Timeout() bool }); ok && e.Timeout() {
// handle timeout
} else if err != nil {
// handle error
}
}
Note that a single call Read may not read all data sent by the peer. You may want to accumulate data by calling Read in a loop or call io.ReadFull.