Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 尽管轮询文件描述符,但http客户端不可靠_C_Sockets_Ocaml_Unix Socket - Fatal编程技术网

C 尽管轮询文件描述符,但http客户端不可靠

C 尽管轮询文件描述符,但http客户端不可靠,c,sockets,ocaml,unix-socket,C,Sockets,Ocaml,Unix Socket,我正在尝试用OCaml编写一个简单的HTTP客户机。我知道使用cohttp等库会更容易。我这样做是为了我自己,所以不需要提出建议 这是我的密码 module Connection = struct let sock_fd = let s_fd = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in Unix.setsockopt s_fd Unix.TCP_NODELAY true; s_fd

我正在尝试用OCaml编写一个简单的HTTP客户机。我知道使用cohttp等库会更容易。我这样做是为了我自己,所以不需要提出建议

这是我的密码

module Connection = struct
    let sock_fd =
        let s_fd = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
        Unix.setsockopt s_fd Unix.TCP_NODELAY true;
        s_fd

    let read_timeout = 10.0

    let read_from_sock () =
        let buff_size = 4096 in
        let buff = Bytes.create buff_size in
        let rec read_all response =
            let (read_fds, _, _) = Unix.select [sock_fd] [] [] read_timeout in
            match read_fds with
            | [] -> response
            | (read_fd :: _) -> begin
                let _ = Unix.read read_fd buff 0 buff_size in
                let current_response = response ^ buff in
                read_all current_response
            end in
        read_all ""

    let write_to_sock str =
        let len = String.length str in
        let _ = Unix.write sock_fd str 0 len in ()

    let make_request request serv_addr =
        Unix.connect sock_fd serv_addr;
        write_to_sock request

    class connection address port =
        object
            val serv_addr = Unix.ADDR_INET (Unix.inet_addr_of_string address, port)

            method get_response (request: string) =
                make_request request serv_addr;
                let response = read_from_sock () in
                Printf.printf "%s\n" response;
                Unix.shutdown sock_fd Unix.SHUTDOWN_ALL;
                Unix.close sock_fd
        end

    let create address port = new connection address port
end

let connection = Connection.create "54.175.219.8" 80;;
connection#get_response "GET / HTTP/1.1\r\nHost: www.httpbin.org\r\n\r\n"
正如我之前发布的——如果你觉得有用的话——我可以想象(非常粗糙的)C等价物是这样的:

int sock_fd = socket(PF_INET, SOCK_STREAM);
setsockopt(sock_fd, TCP_NODELAY, 1);

serv_addr addr {"54.175.219.8", 80};
connect(sock_fd, &serv_addr);
write(sock_fd, "GET / HTTP/1.1\r\nHost: www.httpbin.org\r\n\r\n");

char buffer[512];

while (sock_fd = select(sock_fd, 10.0)) {
    if (!sock_fd) break;
    read(sock_fd, &buffer);
    printf("%s\n", buffer);
    flush(stdout);
}

shutdown(sock_fd, SHUTDOWN_ALL);
close(sock_fd);
当我执行此操作时,我会得到各种各样的结果。有一次,我确实得到了整个页面。但大多数情况下,它在整个页面的80%左右被切断。我试图增加超时时间,但没有用

我想,如果我轮询文件描述符,我将能够可靠地知道什么时候没有像这个博客这样的数据。这种方法似乎是对循环的一种改进,直到读取大小小于缓冲区大小,但我想不是吗?我错过了什么

更新

我编辑代码以检查读取大小是否小于缓冲区大小。然而,这似乎是多余的。如果还有更多内容要读取,select将返回文件描述符。如果没有更多的东西要读,它就不会了,我会把我读过的东西还给你。这是新代码:

let r = Unix.read read_fd buff 0 buff_size in
let current_response = response ^ buff in
if r < buff_size
then current_response
else read_all response
设r=Unix.read read\u fd buff 0 buff\u大小in
让当前_响应=响应^buff进入
如果r
但实际上这是错误的。这完全消除了轮询文件描述符的目的。也许问题仍然在于读取的数据小于buff_大小。。。但我真的不知道还有什么办法可以解决这个问题。读取的内容(无论是否 最终解决方案(感谢@ivg):

让我们从\u sock()读取\u=
让buff_大小=4096英寸
让buff=Bytes.create buff_size in
让rec读取所有响应=
让(read_fds,u,u)=Unix。选择[sock_fd][]读取中的超时
让rec读取\u所有\u帮助程序当前\u响应=
将read_fds与
|[]->当前_响应
|(阅读::)->开始
设r=Unix.read read\u fd buff 0 buff\u大小in
让当前_response=response^(String.sub buff 0 r)输入
如果r
是的,根据你之前的帖子,这就是我从你的代码中期待的那种问题。这是邪恶的根源:

let _ = Unix.read read_fd buff 0 buff_size in

您不能忽略读取的结果,因为不能保证
read
调用将准确读取
buff\u size
,它可以返回较少的数据(所谓的“短读取”)。
write
调用也存在同样的问题。因此,您需要仔细使用缓冲区,在短时间读取后重建数据。另一个问题是,电话可能会被信号打断,但我认为你现在不会打这个

谢谢你关注我的帖子。但我还是不明白这是怎么回事。如果一个简短的阅读不是由投票来处理的话?如果还有更多的内容要读,那么select应该再次返回我的文件描述符——作为还有更多内容要读的指示符。我已经更新了我的问题(我相信是你的解决方案),但它还没有解决问题。这不是民意调查的工作方式。民意调查实际上回答了一个问题:“是否有新的数据可用”。如果它告诉你,它有4000字节,但你只读512字节,这就是你的问题。波尔不在乎。另外,当您请求4096字节,但读取512,然后将4096字节长的字符串附加到累加器中时,实际上添加了
4096-512
字节的垃圾。太好了,我找不到轮询只关心新数据的地方。非常感谢你!
let _ = Unix.read read_fd buff 0 buff_size in