使用 Socket 从浏览器读取请求后如何发送响应? (我正在使用 SwiftSocket)

Posted

技术标签:

【中文标题】使用 Socket 从浏览器读取请求后如何发送响应? (我正在使用 SwiftSocket)【英文标题】:How to send response after reading request from browser with Socket ? (I'm using SwiftSocket) 【发布时间】:2020-05-07 03:33:38 【问题描述】:

我是 ios 网络的新手,所以不太了解所有概念,我正在制作一个允许在设备上的浏览器上显示数据的应用程序, 为此,我正在创建一个套接字端口并使用 SwiftSocket

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()

我认为下面的函数selfserver()会初始化服务器并等待客户端连接

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)
    

当客户端尝试连接时,将调用以下函数senderclient(senderclient: TCPClient) 在响应中,我发送了index.html 文件,该文件保存在htmlfileURL 中,键入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)
    

我的问题是我得到了所有请求头,过滤后我还得到了请求头需要的 filenamecontent-type,但我不知道之后如何发送这些文件在响应中接收和读取请求头..

如上图所示 在控制台区域,您可以看到我收到了styles.afcc5f0641cf44266b1b.css 文件的请求...我只是不知道在请求时如何将该文件发送到浏览器(我有文件的完整路径)

谢谢

【问题讨论】:

【参考方案1】:

你创建的 TCPServer 实例,它实际上只存在于 selfserver 函数中。一旦 selfserver 函数返回,它就不再存在了

专注于这样一个事实,即在运行 startconnection() 时,没有运行 TCPServer 实例,这意味着它没有响应。第一步,从

返回它的实例
selfserver()->TCPsever  ... return server   

并将结果分配给 View 实例中的某些属性以延长其寿命

【讨论】:

那么我能做什么???....看到我在index.html中添加了5个参考文件所以当我发送index.htmlstartconnection()中的开关循环正在运行5次,但我不明白如何在阅读我在startconnection 的开关循环中得到的响应后发送相关文件......你能解释一下这个概念吗,我没有得到任何简短的教程..... 专注于这样一个事实,即在运行 startconnection() 时,没有运行 TCPServer 实例,这意味着它没有响应......作为第一步,从 selfserver() 返回它的实例- >TCPsever ... return server 并将结果分配给 View 实例中的某个属性以延长其寿命 您能否详细说明一下这个 TCP 客户端/服务器概念,因为我在这方面遇到了很多困难,因为我刚刚开始 iOS 开发,而且我是这个套接字区域的新手......跨度> 【参考方案2】:

注意:- 我仍在寻找对每个人都非常有用的正确答案,因为 Swift 中的 Socket 是一个很难实现的概念

//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!

实现上述代码后,我可以获取所有请求标头并发送响应 但是只剩下一个问题了:(见下面的截图)

现在,正如您在图片中看到的那样,我能够读取所有响应标头,并且我也在发送请求的文件 但是问题(因为循环或者我不知道是什么)是我发送的响应文件将进入下一个请求而不是当前请求标头

例如:

它在第一个请求的文件中显示空数据,而我为第一个 styles.afcc5f0641cf44266b1b.css 发送的数据/文件将进入 runtime.26209474bfa8dc87a77c.js 的下一个(第二个)请求标头中,由于这种不规则性,我的最后一个文件favicon.ico 甚至没有被发送

【讨论】:

以上是关于使用 Socket 从浏览器读取请求后如何发送响应? (我正在使用 SwiftSocket)的主要内容,如果未能解决你的问题,请参考以下文章

Java Socket编程之TCP

java socket-TCP

如何管理代理服务器的长短连接

使用一个 pyaudio 流进行数据读取和写入

socket 和serve socket

如何从 POST 请求中检索 cookie?