How to send response after reading request from browser with Socket ? (I'm using SwiftSocket)
Asked Answered
S

2

7

I am new to this networking in iOS so doesn't know all the concepts so well and I'm making an app which allows to show data on browser which is on device, For that I'm creating a socket-port and using SwiftSocket library

override func viewDidLoad() {
    super.viewDidLoad()
    let ip = String(getAddress(for: .wifi)!)
    self.host = ip
    print("Getting IP address of wifi\n\(self.host)\n")
    self.selfserver()
}

I think following function selfserver() initialises the server and wait for the client to connect

func selfserver()
{
    let server = TCPServer(address: self.host, port: Int32(port))
    switch server.listen() {
    case .success:
        while true {
            if let client2 = server.accept() {
                print("CLIENT ACCEPTED ....")
                 self.startconnection(senderclient: client2)
            } else {
                print("accept error")
            }
        }
    case .failure(let error):
        print(error)
    }
}

When client will try to connect following function senderclient(senderclient: TCPClient) will be called and in the response I'm sending index.html file which is saved in htmlfileURL key in Userdefaults

func startconnection(senderclient: TCPClient) {
    print("CLIENT ADD\n\(senderclient.address)\n")
    let filePath = UserDefaults.standard.url(forKey: "htmlfileURL")
    // Converting `index.html` in bytes to send
    var bytes = [UInt8]()
    if let data = NSData(contentsOfFile: filePath!.path) {
        var buffer = [UInt8](repeating: 0, count: data.length)
        data.getBytes(&buffer, length: data.length)
        bytes = buffer
        }

    senderclient.send(string: "HTTP/1.1 200 OK\n" +
        "Content-Length: \(bytes.count)\n" +
        "Connection: close\n" +
        "Content-Type: text/html\n" +
        "\n")
    switch senderclient.send(data: bytes) {
    case .success:
        let data = senderclient.read(1024*10)
        if let d = data {
            if let str = String(bytes: d, encoding: .utf8) {
                print("STR\n\(str)\n")
                // If I'm not wrong Here I should get the response 
            }
        }

    case .failure(let erro):
        print(erro)
    }
}

My problem is I am getting all request headers and after filtering I am also getting which filename and content-type the request header requires but I don't know how to send those file after receiving and reading the request header in reponse ..

enter image description here

As you can see in the above screenshot In console area , you can see I'm getting a request of styles.afcc5f0641cf44266b1b.css file...I just don't know how to send that file to the browser when it requests(I have full path of the file)

Thank You

Sonde answered 21/1, 2020 at 5:50 Comment(0)
S
4

NOTE:- I'm Still Searching for the Right answer which can be very useful to everyone as Socket in Swift is quite difficult concept to implement

//Iniiating Two variable which are initially empty to check if the loop is running first time
var loopablestring = String()
var loopableExtension = String()
//which was necessary here because otherwise only 'index.html' file will be sent in response to every request header    

//MARK:- Start connection with Client Socket Function
func startconnection(senderclient: TCPClient) {

    let filePath = Bundle.main.resourceURL
    var FilePath = String()
    var FileBytes = [Byte]()
    var ContType = String()
    
    //Initially this code will be called
    if self.loopablestring == "" {
        FilePath = filePath!.appendingPathComponent("index.html").path
        FileBytes = self.getBytesFromFile(fromPath: FilePath)
        self.loopableExtension = "html"
        ContType = self.getMimeType(file: self.loopableExtension)
    } else {
        FilePath = filePath!.appendingPathComponent(loopablestring).path
        FileBytes = self.getBytesFromFile(fromPath: FilePath)
        ContType = self.getMimeType(file: self.loopableExtension)
    }
    //sending requested file(if any or sending 'index.html')
    senderclient.send(string: "HTTP/1.1 200 OK\n" +
        "Content-Length: \(FileBytes.count)\n" +
        "Connection: close\n" +
        "Content-Type: \(ContType)\n" +
        "\n")

    print("Sending File Of Which Content-Type\t\(ContType)\n And Path\t\(FilePath)\nAnd Pure File Name\t\(loopablestring)\nWith Extension\t\(loopableExtension)\n")

    switch senderclient.send(data: FileBytes) {
    case .success:
        //print("SUCCESS")
        if let responseBytes = senderclient.read(1024*10) {
            let responseString = String(bytes: responseBytes, encoding: .utf8)!
            print("\nRespons String which contains Headers\n\(responseString)\n\n")

            let HeaderLines = responseString.components(separatedBy: .newlines)[0]
            let separator = HeaderLines.components(separatedBy: .whitespaces)[1]
            //print("Only first line of the Header\n\(HeaderLines)\n\n")

            if separator != "/" {
            //If we don't get Empty or nil value in request header then this code will run

            let purefilename = separator.components(separatedBy: "/")[1]
            //print("Pure File Name filtered from Header's First Line \n\(purefilename)\n")

                print("ALLOCATED FILE \n\(purefilename)\n")

                //giving that two variable values so in next loop's it won't go in `if self.loopablestring == ""` condition
                self.loopablestring = purefilename
                self.loopableExtension = self.getMimeType(file: separator)
                //giving that two variable values so in next loop's it won't go in `if self.loopablestring == ""` condition
            } else {
                print("CAME HERE to be closed")
            }
        }

    case .failure(let error):
        print("FAILED because of \(error)")
    }
    print(" \t\t AFTER THE LOOP \n\n\n")
    senderclient.close()
}

//To return Content Type to Browser (In response Header)
func getContentType(filename: String) -> String {
    do {
        if filename == "js" {
            return "application/javascript"
        } else if filename == "html" {
            return "text/html"
        } else if filename == "css" {
            return "text/css"
        } else if filename == "ico" || filename == "png" || filename == "PNG" || filename == "jpg"  || filename == "JPG" || filename == "jpeg" || filename == "JPEG" {
            return "image/png"
        } else {
            return "text/html"
        }
    } catch {
        print("Something is Wrong!!")
    }
}

//TO Get Extension without (.)DOT
func getMimeType(file: String) -> String {
    let ext = file.components(separatedBy: ".")
    //print("all extensions\n\(ext)\n")
    return ext.last!
}

After implementing above code I'm able to get all the Request Headers and send the Response But there is only one issue left :(See the below Screenshot)

enter image description here

Now, as you can see in the image that I'm able to read all the response header and I'm sending the requested files too But issue (because of loop or I don't know what) is the response file I send is going in the next request instead of the current request header

ex:

It shows null data in the first requested file and the data/file I'm sending for first styles.afcc5f0641cf44266b1b.css is going in the next(second) requested header of runtime.26209474bfa8dc87a77c.js and because of this irregularity, my last file favicon.ico is not even being sent

Sonde answered 29/1, 2020 at 12:9 Comment(0)
K
-2

your create instance of TCPServer, which in reality exists only inside selfserver function. Once the selfserver function return, it doesn't exist any more!

concentrate on the fact, that while running startconnection(), there is NO TCPServer instance running, that means no respond from it. As a first step, return instance of it from

selfserver()->TCPsever { ... return server  } 

and assign the result to some property in your View instance to extend its livespan

Karlie answered 27/1, 2020 at 14:23 Comment(3)
so what can I do ???....see I've added 5 reference file in index.html so when I'm sending index.html switch loop in startconnection() is running 5 times but I am not understanding how to send the related file after reading the response I'm getting in switch loop in startconnection....can you please explain the concept and all, I'm not getting any brief tutorials on this.....Sonde
concentrate on the fact, that while running startconnection(), there is NO TCPServer instance running, that means no respond from it ... As a first step, return instance of it from selfserver()->TCPsever { ... return server } and assign the result to some property in your View instance to extend its livespanKarlie
can you elaborate more about this TCP Client/Server concept as I'm having a lot of difficulties in that because I just started iOS development and I am new in this socket region....Sonde

© 2022 - 2024 — McMap. All rights reserved.