126 lines
4.3 KiB
C
126 lines
4.3 KiB
C
#include <c_SocketStream.h>
|
|||
|
|
|
||
|
|
#if defined(_WIN32) || defined(_WIN64)
|
||
|
|
#include <winsock2.h>
|
||
|
|
#else
|
||
|
|
#include <sys/socket.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* ================================================================================================================== */
|
||
|
|
/* [套接字输入流子类]: c_SocketInStream 内部具体化上下文 */
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
c_InStream_t base;
|
||
|
|
c_socket_t sock;
|
||
|
|
c_Allocator_t allocator;
|
||
|
|
} c_SocketInImpl_t;
|
||
|
|
|
||
|
|
static c_err_t _SocketIn_Read(c_InStream_t* self, void* buf, c_size_t len, c_size_t* bytes_read) {
|
||
|
|
c_SocketInImpl_t* impl = (c_SocketInImpl_t*)self;
|
||
|
|
if (impl->sock == C_INVALID_SOCKET || !buf || len == 0) return C_ERR_PARAM;
|
||
|
|
|
||
|
|
/* 执行底层网络字节流套接字拦截 */
|
||
|
|
#if defined(_WIN32) || defined(_WIN64)
|
||
|
|
int n = recv(impl->sock, (char*)buf, (int)len, 0);
|
||
|
|
#else
|
||
|
|
ssize_t n = recv(impl->sock, buf, (size_t)len, 0);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (n == 0) {
|
||
|
|
if (bytes_read) *bytes_read = 0;
|
||
|
|
return C_ERR_OUTOFBOUND; /* 对端优雅关闭连接 */
|
||
|
|
}
|
||
|
|
if (n == C_SOCKET_ERROR) {
|
||
|
|
if (bytes_read) *bytes_read = 0;
|
||
|
|
return C_ERR_FAIL; /* 网络链路异常断开 */
|
||
|
|
}
|
||
|
|
|
||
|
|
if (bytes_read) *bytes_read = (c_size_t)n;
|
||
|
|
return ( (c_size_t)n == len ) ? C_SUCCESS : C_ERR_OK; /* 部分读取或完全读取皆视为合规 */
|
||
|
|
}
|
||
|
|
|
||
|
|
static void _SocketIn_Destroy(c_InStream_t* self) {
|
||
|
|
c_SocketInImpl_t* impl = (c_SocketInImpl_t*)self;
|
||
|
|
c_Allocator_t alloc = impl->allocator;
|
||
|
|
c_Allocator_Free(&alloc, impl);
|
||
|
|
}
|
||
|
|
|
||
|
|
static const c_InStreamVtbl_t g_SocketInVtbl = { _SocketIn_Read, _SocketIn_Destroy };
|
||
|
|
|
||
|
|
c_err_t c_SocketInStream_Create(c_InStream_t** out_stream, c_socket_t sock, c_Allocator_t* allocator) {
|
||
|
|
if (!out_stream || sock == C_INVALID_SOCKET) return C_ERR_PARAM;
|
||
|
|
c_Allocator_t alloc = allocator ? *allocator : c_DefaultAllocator;
|
||
|
|
|
||
|
|
c_SocketInImpl_t* impl = (c_SocketInImpl_t*)c_Allocator_Calloc(&alloc, 1, sizeof(c_SocketInImpl_t));
|
||
|
|
if (!impl) return C_ERR_NOMEM;
|
||
|
|
|
||
|
|
impl->base.vtbl = &g_SocketInVtbl;
|
||
|
|
impl->sock = sock;
|
||
|
|
impl->allocator = alloc;
|
||
|
|
*out_stream = &impl->base;
|
||
|
|
return C_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ================================================================================================================== */
|
||
|
|
/* [套接字输出流子类]: c_SocketOutStream 内部具体化上下文 */
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
c_OutStream_t base;
|
||
|
|
c_socket_t sock;
|
||
|
|
c_Allocator_t allocator;
|
||
|
|
} c_SocketOutImpl_t;
|
||
|
|
|
||
|
|
static c_err_t _SocketOut_Write(c_OutStream_t* self, const void* buf, c_size_t len, c_size_t* bytes_written) {
|
||
|
|
c_SocketOutImpl_t* impl = (c_SocketOutImpl_t*)self;
|
||
|
|
if (impl->sock == C_INVALID_SOCKET || !buf || len == 0) return C_ERR_PARAM;
|
||
|
|
|
||
|
|
#if defined(_WIN32) || defined(_WIN64)
|
||
|
|
int n = send(impl->sock, (const char*)buf, (int)len, 0);
|
||
|
|
#else
|
||
|
|
/* MSG_NOSIGNAL 能够有效防止 Unix 系统在往断开的连接写入时触发致命的 SIGPIPE 信号崩溃进程 */
|
||
|
|
#ifdef MSG_NOSIGNAL
|
||
|
|
ssize_t n = send(impl->sock, buf, (size_t)len, MSG_NOSIGNAL);
|
||
|
|
#else
|
||
|
|
ssize_t n = send(impl->sock, buf, (size_t)len, 0);
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (n == C_SOCKET_ERROR) {
|
||
|
|
if (bytes_written) *bytes_written = 0;
|
||
|
|
return C_ERR_FAIL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (bytes_written) *bytes_written = (c_size_t)n;
|
||
|
|
return ( (c_size_t)n == len ) ? C_SUCCESS : C_ERR_FAIL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static c_err_t _SocketOut_Flush(c_OutStream_t* self) {
|
||
|
|
/* TCP 套接字在操作系统内核层通常是实时刷新或通过 Nagle 算法控制,上层虚函数表在此可直接视为合规 */
|
||
|
|
(void)self;
|
||
|
|
return C_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void _SocketOut_Destroy(c_OutStream_t* self) {
|
||
|
|
c_SocketOutImpl_t* impl = (c_SocketOutImpl_t*)self;
|
||
|
|
c_Allocator_t alloc = impl->allocator;
|
||
|
|
c_Allocator_Free(&alloc, impl);
|
||
|
|
}
|
||
|
|
|
||
|
|
static const c_OutStreamVtbl_t g_SocketOutVtbl = { _SocketOut_Write, _SocketOut_Flush, _SocketOut_Destroy };
|
||
|
|
|
||
|
|
c_err_t c_SocketOutStream_Create(c_OutStream_t** out_stream, c_socket_t sock, c_Allocator_t* allocator) {
|
||
|
|
if (!out_stream || sock == C_INVALID_SOCKET) return C_ERR_PARAM;
|
||
|
|
c_Allocator_t alloc = allocator ? *allocator : c_DefaultAllocator;
|
||
|
|
|
||
|
|
c_SocketOutImpl_t* impl = (c_SocketOutImpl_t*)c_Allocator_Calloc(&alloc, 1, sizeof(c_SocketOutImpl_t));
|
||
|
|
if (!impl) return C_ERR_NOMEM;
|
||
|
|
|
||
|
|
impl->base.vtbl = &g_SocketOutVtbl;
|
||
|
|
impl->sock = sock;
|
||
|
|
impl->allocator = alloc;
|
||
|
|
*out_stream = &impl->base;
|
||
|
|
return C_SUCCESS;
|
||
|
|
}
|
||
|
|
|