Onvif摄像头告警接收异常:Socket阻塞与数据堆积问题排查
Hey there! Let's dive into your ONVIF camera alert reception issues—you've got three implementations with very different behaviors, so we'll break each down to find the root causes and fixes.
First, a quick recap of your setup: You have an ONVIF camera sending XML motion alerts to your server's 8085 port. Your Go HTTP server works flawlessly, but your C++ raw socket and C# programs are hitting frustrating snags. Let's tackle each problem one by one:
accept(), Gets Old Packets After Restart Root Cause Guess
ONVIF event notifications typically use SOAP over HTTP (TCP-based), so your camera is likely establishing a TCP connection to send alerts. Here's what's probably going wrong:
- If your C++ program is single-threaded, after calling
accept()to grab a client connection, it might get stuck blocking onread()(waiting for more data that never arrives, or failing to handle partial reads correctly). This means the program never loops back toaccept()to handle new connections from the camera. - The camera probably caches unsent alerts when its send buffer fills up (since your program isn't pulling data from the connection). When you restart the program, the old TCP connection drops, the camera re-establishes a new connection, and dumps all cached alerts at once.
- Alternatively, you might not be setting the
SO_REUSEADDRsocket option, so after a restart, the old connection is stuck inTIME_WAITstate and blocks your new listener from binding to 8085 immediately. - Another angle: You're not closing client sockets properly after the camera disconnects, leading to file descriptor leaks that eventually prevent new
accept()calls.
Fixes
- Use multi-threading or IO multiplexing: If you're using a single thread, switch to spawning a new thread for each accepted connection, or use
select(),poll(), orepoll()(on Linux) to handle multiple connections without blocking on a singleread(). - Set read timeouts: Use
setsockopt()withSO_RCVTIMEOto avoid infinite blocking onread(). If no data comes in after a reasonable timeout, close the client socket and return toaccept(). - Properly close client sockets: Whenever the camera disconnects (detected via
read()returning 0 bytes), callclose()on the client socket to free up resources. - Enable
SO_REUSEADDR: When creating your listening socket, set this option to avoidTIME_WAITissues after restarts. Example snippet:int opt = 1; if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("setsockopt"); exit(EXIT_FAILURE); }
Receive() Doesn't Clear Buffer, Data Piles Up, Port Gets Clogged, AcceptSocket() Blocks Root Cause Guess
C#'s Socket.Receive() only reads as much data as fits in your buffer—it doesn't guarantee to grab the entire HTTP/SOAP message in one call. If you're not looping to read all available data, leftover bytes stay in the TCP receive buffer. Over time:
- The camera's send buffer fills up, so it stops sending new alerts.
- If you're not closing connections properly, you end up with dozens (or hundreds) of stale connections in
TIME_WAITorESTABLISHEDstate, exhausting available ports and causingAcceptSocket()to block indefinitely because no new connections can be established.
Fixes
- Read until you get the full message: ONVIF alerts are HTTP POST requests, so parse the
Content-Lengthheader first, then loopReceive()until you've read exactly that many bytes. Alternatively, read until you hit the HTTP message boundary (\r\n\r\nfollowed by the body). - Use
TcpListenerandNetworkStreaminstead of rawSocket: These higher-level abstractions handle stream reading more gracefully. Example:TcpListener listener = new TcpListener(IPAddress.Any, 8085); listener.Start(); while (true) { using TcpClient client = listener.AcceptTcpClient(); using NetworkStream stream = client.GetStream(); // Read the full HTTP request from the stream byte[] buffer = new byte[4096]; int bytesRead; StringBuilder requestBuilder = new StringBuilder(); while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) { requestBuilder.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)); // Check if we've reached the end of the HTTP message if (requestBuilder.ToString().Contains("\r\n\r\n")) { // Parse the request and extract UtcTime break; } } } - Close connections properly: Always wrap
TcpClient/Socketinusingstatements (or explicitly callClose()) to ensure connections are closed and resources are freed. - Adjust socket linger settings: If you're seeing too many
TIME_WAITconnections, setSocket.LingerStateto reduce the wait time, or enableSocketOptionName.ReuseAddresson your listener.
Go's standard HTTP server handles all the heavy lifting for you: it uses goroutines to handle each connection concurrently, automatically reads full HTTP requests, manages connection timeouts, and cleans up stale connections. You don't have to manually handle the low-level socket details that trip up C++ and C# implementations.
If you can share snippets of your C++ and C# code, we can pinpoint the exact issues even more accurately!
内容的提问来源于stack exchange,提问作者hamidi

