Cross-Platform Socket Behavior Inconsistencies
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 35/100
- Issue 类型
- 缺陷
- 描述清晰度
- 需要澄清
- 活跃度
- 停滞
- 技术栈
- cpp
调研方向
该 issue 未提供仓库文件或测试入口点。首先在 Windows、Linux、FreeBSD 和 macOS 上复现 Haxe server/client POC,然后跟踪 hxcpp socket accept、waitForRead 和 select 的行为。完成的标准是:无需额外的触发连接即可一致地处理请求,并为受影响的平台提供回归测试覆盖。
由索引模型根据 Issue 内容生成。
描述
There’s an issue with the hxcpp/hashlink/neko socket code platform-specific socket requests are either not being processed at all, or the waitForRead / accept calls aren’t being triggered.
Note: If you are having cors problems, try again using the plugin below.
https://chromewebstore.google.com/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf
POC
Server Side
import sys.net.Host;
import sys.net.Socket;
import sys.thread.Thread;
import haxe.io.Bytes;
import haxe.io.Input;
using StringTools;
class Main {
#if cpp
private static var threadPool = new sys.thread.FixedThreadPool(32);
#end
public static function main() {
var listener_socket = new Socket();
// Binding 0.0.0.0 means, listen on "any / all IP addresses on this host"
listener_socket.bind(new Host('0.0.0.0'), 8000);
listener_socket.listen(9999); // big max connections
while (true) {
// Accepting socket
trace('waiting to accept...');
var peer_connection:Socket = listener_socket.accept();
if (peer_connection != null) {
trace('got connection from : ' + peer_connection.peer());
// Spawn a reader thread for this connection:
#if cpp threadPool.run #else Thread.create #end (() -> read(peer_connection));
}
}
}
static function read(peer_connection:Socket):Void {
try {
var rawRequest:String = parseRequestFromInputProtocol(peer_connection.input).toString();
var request = parseRequest(rawRequest);
var requestPath:String = request.path;
trace(requestPath);
if(requestPath.startsWith("/list"))
handleUploadList(peer_connection);
else if(requestPath.startsWith("/upload"))
handleUpload(peer_connection);
} catch(e) {
trace(e);
trace(e.stack);
}
peer_connection.close();
}
static function handleUploadList(peer_connection:Socket):Void {
var chunks:Array<Dynamic> = [];
for(i in 0...2) {
chunks.push({start: 0, end: 16777216, key: 'chunk_${Math.random()}', createdAt: 0});
}
peer_connection.output.writeString('HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n${haxe.Json.stringify({chunks: chunks})}');
}
static function handleUpload(peer_connection:Socket):Void {
trace(readUntilDelimiter(peer_connection.input));
try {
peer_connection.input.read(16777216);
} catch(e) {
}
peer_connection.output.writeString('HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{"status":"success","sessionKey":"aWHdCaOsLwnwc90kEjfenp7UvxnVfwAf"}');
}
static function parseRequest(rawRequest:String):Dynamic {
var lines = rawRequest.split("\r\n");
var requestLine = lines[0].split(" ");
#if debug
trace(requestLine);
#end
var method = requestLine[0];
var path = requestLine[1].urlDecode();
return {method: method, path: path};
}
static function readUntilDelimiter(input:haxe.io.Input):String {
var buffer = new StringBuf();
var lastFourChars = "";
try {
while (true) {
var char = String.fromCharCode(input.readByte());
buffer.add(char);
// Keep track of the last 4 characters
lastFourChars += char;
if (lastFourChars.length > 4) {
lastFourChars = lastFourChars.substr(lastFourChars.length - 4);
}
// Check if we've found the delimiter
if (lastFourChars == "\r\n\r\n") {
break;
}
}
} catch (e:Dynamic) {
trace("Error reading from input: " + e);
}
return buffer.toString();
}
static function parseRequestFromInputProtocol(input:Input):Bytes
{
var httpRequestEnd = [0x0D, 0x0A, 0x0D, 0x0A];
#if cpp
var buffer:Array<cpp.UInt8> = new Array<cpp.UInt8>();
#elseif java
var buffer:Array<java.lang.Byte> = new Array<java.lang.Byte>();
#else
var buffer:Array<Int> = new Array<Int>();
#end
var index:Int = 0;
while (true)
{
var found:Bool = false;
buffer[index] = input.readByte();
if (index >= 4)
{
found = true;
for(i in 0...4)
{
if(buffer[index - 3 + i] != httpRequestEnd[i])
{
found = false;
break;
}
}
}
index++;
if(found)
break;
}
buffer.resize(buffer.length - httpRequestEnd.length);
#if cpp
return Bytes.ofData(buffer);
#else
var bytes = Bytes.alloc(buffer.length);
for(i in 0...buffer.length)
{
bytes.set(i, cast buffer[i]);
}
return bytes;
#end
}
}
Client Side
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>List → Upload Loop (Real-Time Log + Continue)</title>
<style>
body{font-family:monospace;background:#0e1117;color:#ddd;margin:0;padding:10px}
#controls{margin-bottom:10px}
button{background:#1b4bff;border:none;color:#fff;padding:6px 12px;
border-radius:6px;font-weight:bold;cursor:pointer}
#log{font-size:13px;white-space:pre-wrap}
.ok{color:#7CFC00}
.err{color:#FF6347}
.list{color:#00BFFF}
.upload{color:#FFD700}
.info{color:#aaa}
</style>
</head>
<body>
<div id="controls">
<button id="continueBtn">Trigger Socket Select</button>
</div>
<h3>Real-Time List / Upload Log</h3>
<div id="log"></div>
<script>
const API_URL = "http://127.0.0.1:8000";
const CHUNK_SIZE = 16777216;
let id = 0;
let entries = [];
function render() {
const now = performance.now();
const html = entries.slice(-100).reverse().map(e=>{
const elapsed = (now - e.start).toFixed(0);
const dur = e.end ? `${e.duration} ms` : `${elapsed} ms`;
const cls = e.type==="list"?"list":(e.ok===false?"err":(e.type==="continue"?"info":"upload"));
const status = e.end ? (e.ok?"OK":"ERROR") : "•••";
return `<span class="${cls}">[${e.type.toUpperCase()}] ${status} ${dur} ${e.msg}</span>`;
}).join("\n");
document.getElementById("log").innerHTML = html;
requestAnimationFrame(render);
}
function addEntry(type,msg){
const e={type,msg,start:performance.now(),end:0,ok:null,duration:0};
entries.push(e);
if(entries.length>200) entries.shift();
return e;
}
function finishEntry(e,ok){
e.ok=ok;
e.end=performance.now();
e.duration=(e.end-e.start).toFixed(0);
}
async function getList(){
const ent=addEntry("list","→ /list?id="+id);
try{
const res=await fetch(`${API_URL}/list?id=${id++}`,{method:"POST"});
if(!res.ok) throw new Error(res.status);
const data=await res.json();
finishEntry(ent,true);
if(data.chunks?.length){
await Promise.all(data.chunks.map(c=>uploadChunk(c.key)));
}
getList();
}catch(e){
finishEntry(ent,false);
getList();
}
}
async function uploadChunk(key){
const ent=addEntry("upload","→ "+key);
const blob=new Blob([new Uint8Array(CHUNK_SIZE)],{type:"application/octet-stream"});
const formData=new FormData();
formData.append("file",blob);
try{
const res=await fetch(`${API_URL}/upload?chunkKey=${key}`,{method:"POST",body:formData});
if(!res.ok) throw new Error(res.status);
finishEntry(ent,true);
}catch(e){
finishEntry(ent,false);
}
}
// --- Continue button handler ---
document.getElementById("continueBtn").addEventListener("click", async ()=>{
const ent = addEntry("continue","→ /");
try {
const res = await fetch(`${API_URL || ''}/`, {method:"GET"});
if (!res.ok) throw new Error(res.status);
finishEntry(ent,true);
} catch (e) {
finishEntry(ent,false);
}
});
// start rendering and looping
render();
getList();
</script>
</body>
</html>
Backend Behavior
Windows
HTTP requests usually fail.
Linux & FreeBSD
HTTP requests usually don’t fail, but sometimes a connection isn’t processed until another connection triggers it. I think the socket select call gets stuck at some point, and the connection isn’t handled until a new one arrives.
When a request freezes, pressing the “Trigger Socket Select” button will cause the frozen request to be processed.
I think the problem is related to Chrome’s TCP pooling feature.
MacOS
Working without any issue
- 主要语言
- C++
- 星标
- 330
- 派生
- 233
- 平均合并
- 3 天 5 小时
- 30 天内合并 PR
- 3
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
HaxeFoundation/hxcpp 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 68/100
HaxeFoundation/hxcpp#1217 ·
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 35/100
HaxeFoundation/hxcpp#1432 ·
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 35/100
HaxeFoundation/hxcpp#1431 ·
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 42/100
HaxeFoundation/hxcpp#1430 ·
维护者通常 1 天内回复
-
Null<UInt> compared with null gives wrong results (null is cast to 0 by UInt's == / != overloads)未关闭
难度 4/5 1-2 天 新手友好度 57/100
HaxeFoundation/hxcpp#1427 · 1 条评论 ·
维护者通常 1 天内回复
查看 HaxeFoundation/hxcpp 的全部 Issue
相似的 Issue
-
audit(c2): add_project_arguments('/experimental:c11atomics', language: ['c', 'cpp']) for MS (C2-13)未关闭hygiene type:refactor
难度 2/5 1-3 小时 新手友好度 66/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 64/100
sandialabs/Fenix#172 ·
-
难度 2/5 1-3 小时 新手友好度 75/100
rocketride-org/rocketride-server#2886 · 1 条评论 ·
维护者通常 2 天内回复
-
难度 2/5 1-3 小时 新手友好度 72/100
scp-fs2open/fs2open.github.com#7953 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 72/100
SteveMacenski/slam_toolbox#907 ·