C 尽管轮询文件描述符,但http客户端不可靠
我正在尝试用OCaml编写一个简单的HTTP客户机。我知道使用cohttp等库会更容易。我这样做是为了我自己,所以不需要提出建议 这是我的密码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
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_大小。。。但我真的不知道还有什么办法可以解决这个问题。读取的内容(无论是否让我们从\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