initial commit
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package clipsync_net
|
||||
|
||||
import cnet "core:net"
|
||||
|
||||
// TCP framing for protocol v1:
|
||||
// [4-byte big-endian length][UTF-8 JSON payload]
|
||||
|
||||
MAX_FRAME_BYTES :: 1024 * 1024 // 1 MiB
|
||||
|
||||
Framing_Error :: enum {
|
||||
None,
|
||||
Frame_Too_Large,
|
||||
Connection_Closed,
|
||||
Socket_Recv_Failed,
|
||||
Socket_Send_Failed,
|
||||
}
|
||||
|
||||
u32_to_be_bytes :: proc(v: u32) -> [4]byte {
|
||||
return [4]byte{
|
||||
byte((v >> 24) & 0xff),
|
||||
byte((v >> 16) & 0xff),
|
||||
byte((v >> 8) & 0xff),
|
||||
byte(v & 0xff),
|
||||
}
|
||||
}
|
||||
|
||||
be_bytes_to_u32 :: proc(prefix: [4]byte) -> u32 {
|
||||
return (u32(prefix[0]) << 24) |
|
||||
(u32(prefix[1]) << 16) |
|
||||
(u32(prefix[2]) << 8) |
|
||||
u32(prefix[3])
|
||||
}
|
||||
|
||||
recv_exact :: proc(socket: cnet.TCP_Socket, out: []byte) -> Framing_Error {
|
||||
offset := 0
|
||||
for offset < len(out) {
|
||||
read, err := cnet.recv_tcp(socket, out[offset:])
|
||||
if err != nil {
|
||||
return .Socket_Recv_Failed
|
||||
}
|
||||
if read == 0 {
|
||||
return .Connection_Closed
|
||||
}
|
||||
offset += read
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
send_all :: proc(socket: cnet.TCP_Socket, data: []byte) -> Framing_Error {
|
||||
offset := 0
|
||||
for offset < len(data) {
|
||||
written, err := cnet.send_tcp(socket, data[offset:])
|
||||
if err != nil {
|
||||
return .Socket_Send_Failed
|
||||
}
|
||||
if written == 0 {
|
||||
return .Connection_Closed
|
||||
}
|
||||
offset += written
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
send_frame :: proc(socket: cnet.TCP_Socket, payload: []byte) -> Framing_Error {
|
||||
if len(payload) > MAX_FRAME_BYTES {
|
||||
return .Frame_Too_Large
|
||||
}
|
||||
|
||||
prefix := u32_to_be_bytes(u32(len(payload)))
|
||||
if err := send_all(socket, prefix[:]); err != .None {
|
||||
return err
|
||||
}
|
||||
return send_all(socket, payload)
|
||||
}
|
||||
|
||||
recv_frame :: proc(socket: cnet.TCP_Socket, allocator := context.allocator) -> ([]byte, Framing_Error) {
|
||||
len_prefix: [4]byte
|
||||
if err := recv_exact(socket, len_prefix[:]); err != .None {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
size := int(be_bytes_to_u32(len_prefix))
|
||||
if size > MAX_FRAME_BYTES {
|
||||
return nil, .Frame_Too_Large
|
||||
}
|
||||
|
||||
buf := make([]byte, size, allocator)
|
||||
if size == 0 {
|
||||
return buf, .None
|
||||
}
|
||||
|
||||
if err := recv_exact(socket, buf); err != .None {
|
||||
delete(buf)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf, .None
|
||||
}
|
||||
Reference in New Issue
Block a user