Hello,
I'm a problem to send binary file over socket. I would like send file but without close the connection, it's important. The problem is that server never close the file because it wait for ever a data.
I wrote an example:
Client:
let client: Socket = newSocket()
client.connect("127.0.0.1", Port(5555))
stdout.writeLine("Client: connected to server on address 127.0.0.1:5555")
while true:
stdout.write("> ")
let filename: string = stdin.readLine()
echo "Pull ..."
var data = newString(4000)
let file = open(filename, fmRead)
while true:
let len = file.readBuffer(addr(data[0]), 4000)
setLen(data, len)
if len == 0:
echo "Send end"
client.send("\c\L")
break
else:
client.send(data)
echo "End of file"
file.close()
client.close()
Server
import asyncnet, asyncdispatch
proc processClient*(client: AsyncSocket) {.async.} =
while not client.isClosed:
echo "Wait file"
var file = open( "receive.7z", fmWrite )
var dataFut = client.recv(4000)
while true:
yield dataFut
if dataFut.finished:
let data = dataFut.read
if data != "":
file.write(data)
dataFut = client.recv(4000)
else:
echo "No data"
break
else:
echo "Exit"
break
echo "End of file"
file.close
client.close()
proc serve() {.async.} =
var server = newAsyncSocket()
server.setSockOpt(OptReuseAddr, true)
server.bindAddr(Port(5555))
server.listen()
echo "Server run !"
while true:
let client = await server.accept()
echo "Connect client"
asyncCheck processClient(client)
asyncCheck serve()
runForever()
How I can send an EOF ? I tried "cL" or an empty string without success.
Thanks.
EOF or End of file in linux is usually when getChar returns -1. But you can't actually send -1 with a socket. All bytes must be 0-255.
End of file in DOS was ASCII character 'x04', also known as End of Transmission or ^D in linux. You could send that and check for that character.
But the problem is that you said binary. It's very easy for a binary file to have a 0x04 byte some where inside which will cut off your file. Its best to just send length of the file first. Read the length and then read the number of bytes you need for the rest of the file.
How I can send an EOF ? I tried "cL" or an empty string without success.
You send an EOF by closing the socket. There's really no such thing as an "EOF character" ... it's just something made up by a library to return when the end of the file/data is reached. It doesn't exist in the file or over the socket.
If you're trying to send a file over a socket but leave the socket open after the file is sent, then you need some other mechanism to tell the server when the file is done. The easiest way is to send the length (in bytes) of the file first, and then the data. The server reads the length, then reads that many bytes and writes them to the file, and then it knows the file is over.
You could make up some special byte like 0x00 to send at the end of the file ... but that byte could occur in the file itself, so then you need a way to escape it so the server doesn't think the file is over yet ... and then you need a way to escape the escape character. It's rather messy, and it slows down sending and receiving because you have to scan through all the bytes.
Thank you for the idea to send the file size :)
I modified the code to receive the file size et I add a counter of byte. But the server never get last data. I think that the server waits a buffer of size 4000.
The Client:
import net, streams, os
let client: Socket = newSocket()
client.connect("127.0.0.1", Port(5555))
stdout.writeLine("Client: connected to server on address 127.0.0.1:5555")
while true:
stdout.write("> ")
let filename: string = stdin.readLine()
var data = newString(4000)
let file = open(filename, fmRead)
let size = getFileSize(file)
echo "File size is :" & $size
echo "Send file size"
client.send($size & "\c\L")
echo "Pull ..."
sleep(100)
while true:
let len = file.readBuffer(addr(data[0]), 4000)
setLen(data, len)
if len == 0:
break
else:
echo "Send data size : " & $len
client.send(data)
echo "End of file"
file.close()
client.close()
The Server
import asyncnet, asyncdispatch, parseutils
proc processClient*(client: AsyncSocket) {.async.} =
var
fileSize: int
counter: int
while not client.isClosed:
echo "Wait file"
var file = open( "receive.7z", fmWrite )
let sizeFileString = await client.recvLine()
discard sizeFileString.parseInt(fileSize)
echo "File Size is " & $fileSize
counter = 0
var dataFut = client.recv(4000)
while true:
yield dataFut
echo "Counter : " & $counter
if counter >= fileSize:
break
else:
if dataFut.finished:
let data = dataFut.read
if data != "":
counter += data.len
file.write(data)
dataFut = client.recv(4000)
else:
echo "No data"
break
else:
echo "Exit"
break
echo "End of file"
file.close
client.close()
proc serve() {.async.} =
var server = newAsyncSocket()
server.setSockOpt(OptReuseAddr, true)
server.bindAddr(Port(5555))
server.listen()
echo "Server run !"
while true:
let client = await server.accept()
echo "Connect client"
asyncCheck processClient(client)
asyncCheck serve()
runForever()
Have you any idea ?
I found the problem. The server must not use the buffering mode otherwise it waits that buffer is full.
var server = newAsyncSocket(buffered=false)
Thanks