Kitura / BlueSocket

Socket framework for Swift using the Swift Package Manager. Works on iOS, macOS, and Linux.
Apache License 2.0
1.41k stars 197 forks source link

No reaction after timeout period has passed #175

Closed Nathipha closed 5 years ago

Nathipha commented 5 years ago

First of all: Thanks for writing a library that works with an FTP server and is easy to use!

I created a new app in Xcode 10 with Swift 5 and the latest version of BlueSocket (installed via CocoaPod) to test it and I've come across a problem:

If I try to write a command like mysocket.write(from: "LIST\r\n"), mysocket.readString() never returns anything, even after the timeout period has passed.

My code (you can find a list of return codes here.):

private let url = "speedtest.tele2.net" //This is an open FTP server that can be used for testing
private let port:Int32 = 21
private let username:String = "anonymous"
private let password:String = ""

@IBAction func buttonClicked(_ sender: Any) {
    do {
        let s = try Socket.create()
        try s.connect(to: url, port: port, timeout: 10000, familyOnly: false) //10secs

        if s.isConnected {
            var answer = try s.readString()

            if answer!.hasPrefix("220") { //220 Enter username
                try s.write(from: "USER \(username)\r\n")
                answer = try s.readString()

                if answer!.hasPrefix("331") { //331 Enter password
                    try s.write(from: "PASS \(password)\r\n")
                    answer = try s.readString()

                    if answer!.hasPrefix("230") { //230 User logged in
                        //If I use "LIST" or "NLST" with the first socket, it tells me: "425 Use PORT or PASV first" ("PWD" works), that's why I'm opening a second socket, just for data:
                        try s.write(from: "PASV\r\n")
                        answer = try s.readString()

                        if answer!.hasPrefix("227") { //E.g. 227 Entering Passive Mode (90,130,70,73,100,187).
                            let new = getNewIP(answer!) //returns e.g. 90.130.70.73:25787
                            //Port according to answer = SecondToLastNumber * 256 + LastNumber
                            let s2 = try Socket.create() //Extra socket for data
                            try s2.connect(to: new.ip, port: new.port, timeout: 10000, familyOnly: false)

                            if s2.isConnected {
                                print("s2 connected")
                                try s2.write(from: "LIST\r\n")
                                answer = try s2.readString()
                                print("2nd socket: \(answer)")
                            } else {
                                print("s2 not connected")
                            }
                        } else {
                            print("Passive: Not 227!: \(answer!)")
                        }
                    } else {
                        print("PW: Not 230!: \(answer!)")
                    }
                } else {
                    print("User: Not 331!: \(answer!)")
                }
            } else {
                print("Connected: Not 220!: \(answer!)")
            }
        } else {
            print("not connected")
        }
    } catch let error {
        print("Error: \(error)\n\(error.localizedDescription)")
    }
}

"s2 connected" is printed but print("List: \(a5)") isn't, even long after the timeout period has passed. It doesn't seem to matter what I use for it, it's the same thing with 20000ms and it also happens with "NLST".

There's no error message, the app simply freezes and nothing happens.

Edit: After 15-20 minutes (timeout: 10000ms = 10s) I now got an error message:

Error: Error code: -9971(0x-26F3), Connection reset by peer The operation couldn’t be completed. (Socket.Socket.Error error 1.)

Closing the first socket before opening the second outputs the same error message instantly.

Nathipha commented 5 years ago

Sorry, my mistake! For e.g. "LIST" you have to write/send the code with the first socket but read the result with the second.