commit 40fb8940cba19a5bcf72792b9f1e431723bd9036 Author: zzy <2450266535@qq.com> Date: Sun Oct 22 11:25:25 2023 +0800 初始提供tcp-ipv4的windows和linux的网络编程 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ce8dac8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +* +!.gitignore +!README.txt +!ssocket.h +!*/ +!test/client/client.c +!test/client/CMakeLists.txt +!test/server/server.c +!test/server/CMakeLists.txt \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..b61ab66 --- /dev/null +++ b/README.txt @@ -0,0 +1,117 @@ +提供ssocket.h库文件以供在windows和linux帮助tcp-ipv4的实现 +未来将提供udp-ipv4及相应ipv6实现 + +测试用例: + 在test文件夹下有client和server文件夹在其内部的build文件夹下有用于windows的vs工程文件 + 所有test用例使用cmake构建 + 在test文件夹下存在windows的可执行程序可用于测试 + (注意server文件忽略多线程资源竞争的问题,该问题将在未来解决) + +案例讲解: + 客户端伪代码解释: + 1.make_client_sock(); + 使用该函数创建面向服务器的套接字描述符 + 2.sock_connect(); + 判断make_client_sock返回值若表示连接服务器失败可以尝试用此函数重连 + 3.连接完成后可以使用recv和send互相通信(具体见后续讲解) + + 服务器伪代码解释: + 1.make_server_sock(); + 使用该函数创建服务器的套接字描述符 + 2.sock_accpet(); + 注意该函数为阻塞函数 + 当有客户端发起连接时该函数返回相应连接的客户端的套接字描述符 + 3.连接完成后可以使用recv和send互相通信(具体见后续讲解) + + recv和send函数讲解: + send函数的套接字描述符是向相应套接字发送内容 + recv同上,接收相应的内容 + 比如: + make_client_sock()返回的是连接到服务器的描述符,用于和服务器通信 + make_server_sock()返回的是服务器的描述符,用于监听自己是否被连接 + sock_accpet()返回的是连接进服务器的客户端的描述符,用于和连接的那个客户端通信 + sock_connect()用于改变连接到服务器的地址,同样用于和服务器通信 + + + +函数文档: + void sleeps(int s) + 输入相应秒数,使相应线程暂停 + void sleepms(int ms) + 输入相应毫秒数,使相应线程暂停 + int make_sock(SOCKET* sock) + 作用: + 提供sock变量的地址以此创建一个用于通信的socket套接字描述符 + 返回值: + + eg1. + SOCKET sock; + make_sock(&sock); + eg2. + SOCKET* psock = malloc(sizeof(SOCKET)); + if(psock == NULL) exit(-1); + make_sock(psock); + ... + free(psock); + + int make_server_sock(SOCKET* sock, const char* server_ip, unsigned short port) + 作用: + 提供sock变量的地址,服务器的IP(若为NULL则默认为所有地址),服务器监听的端口号(0-65535) + 以此创建一个服务器的套接字描述符 + 返回值: + 若返回0则成功,否则失败 + 使用案例同make_sock + + int make_client_sock(SOCKET* sock, const char* connect_ip, unsigned short port) + 作用: + 提供sock变量的地址,连接服务器的IP(若为NULL则默认为不启用初始化连接需要自己调用sock_connect),服务器监听的端口号(0-65535) + 以此创建一个客户端(面向服务器的)套接字描述符 + 返回值: + 若返回0则成功,否则失败 + 使用案例同make_sock + + int sock_connect(SOCKET sock, const char* connect_ip, unsigned short port) + 作用: + 使用make_sock等系列函数返回的套接字,使其连接到目标IP/端口 + 用于客户端初始化,或者客户端重新连接新的服务器 + 返回值: + 若返回0则成功,否则失败 + + int sock_bindlisten(SOCKET sock, const char* server_ip, unsigned short port) + 作用: + 使用make_sock函数返回的套接字,绑定并监听自身IP/端口 + 用于服务器套接字初始化 + 返回值: + 若返回0则成功,否则失败 + + int sock_accpet(SOCKET sock,SOCKET* client, char** accept_ip, unsigned short* port) + 作用: + 用于服务器接收客户端的连接(阻塞函数) + 返回值: + 若返回0则成功,否则失败 + + void close_sock(SOCKET sock) + 作用: + 提供套接字描述符以此删除相关数据 + + void out_sock_err(FILE* output, int errcode) + 作用:暂时未提供 + + void get_sock_err(char* buff_128, size_t buff_len, int errcode) + 作用:暂时未提供 + + void sock_thread(void(*thread_func)(void*), void* data) + 作用: + 创建一个线程并执行线程函数 + eg. + void thread_func(void* data) { + //do someting + if(data != NULL) { + free(data); + } + } + + int main() { + int* data = malloc(sizeof(int)); + sock_thread(thread_func, data); + } diff --git a/ssocket.h b/ssocket.h new file mode 100644 index 0000000..271520b --- /dev/null +++ b/ssocket.h @@ -0,0 +1,288 @@ +#ifndef _SSOCKET_H_ +#define _SSOCKET_H_ + +#include +#include + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) + #define _OS_WIN 1 + //define something for Windows (32-bit and 64-bit, this part is common) + #ifdef _WIN64 + #define _OS_WIN64 1 + //define something for Windows (64-bit only) + #else + #define _OS_WIN32 1 + //define something for Windows (32-bit only) + #endif +#elif __APPLE__ + #define _OS_APPLE 1 + #include + #if TARGET_IPHONE_SIMULATOR + #define _OS_APPLE_SIM 1 + // iOS, tvOS, or watchOS Simulator + #elif TARGET_OS_MACCATALYST + #define _OS_APPLE_CATA 1 + // Mac's Catalyst (ports iOS API into Mac, like UIKit). + #elif TARGET_OS_IPHONE + #define _OS_APPLE_PHO 1 + // iOS, tvOS, or watchOS device + #elif TARGET_OS_MAC + #define _OS_APPLE_MAC 1 + // Other kinds of Apple platforms + #else + # error "Unknown Apple platform" + #endif +#elif __linux__ +#define _OS_LINUX 1 + // linux +#elif __unix__ // all unices not caught above +#define _OS_UNIX 1 + // Unix +#elif defined(_POSIX_VERSION) +#define _OS_POSIX 1 + // POSIX +#else +# error "Unknown compiler" +#endif + +#if _OS_WIN +#define WIN_PART 1 +#elif _OS_LINUX +#define LINUX_PART 1 +#else +#error "Not Supported Operator System" +#endif + +#if WIN_PART +#include +#include +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define socklen_t int +#elif LINUX_PART +#include +#include +#include +#include + +#define SOCKET long long int +#endif + +#ifndef ZZY_SLEEP +#define ZZY_SLEEP +#if WIN_PART + #define sleeps(s) Sleep(s*1000) + #define sleepms(ms) Sleep(ms) +#elif LINUX_PART + #define sleeps(s) sleep(s) + #define sleepms(ms) usleep(ms*1000) +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + ERR_SOCK_SUCCESS, + ERR_SOCK_MALLOC, + ERR_SOCK_NULLPTR, + ERR_SOCK_WSAStartup, + ERR_SOCK_DETERMINE_AGREEMENT, + ERR_SOCK_CREATE_SOCKET, + ERR_SOCK_BIND, + ERR_SOCK_LISTEN, + ERR_SOCK_ACCEPT, + + ERR_SOCK_CONNECT, +}; + +static inline int make_sock(SOCKET* sock); +static inline int sock_connect(SOCKET sock, const char* connect_ip, unsigned short port); +static inline int sock_bindlisten(SOCKET sock, const char* server_ip, unsigned short port); +static inline int sock_accpet(SOCKET sock,SOCKET* client, char** accept_ip, unsigned short* port); +static inline int make_server_sock(SOCKET* sock, const char* server_ip, unsigned short port); +static inline int make_client_sock(SOCKET* sock, const char* connect_ip, unsigned short port); +static inline void close_sock(SOCKET sock); +static inline void out_sock_err(FILE* output, int errcode); +static inline void get_sock_err(char* buff_128, size_t buff_len, int errcode); +static inline void sock_thread(void(*thread_func)(void*), void* data); + +#define _SOCKET_TEST_IP "127.0.0.1" +#define _SOCKET_TEST_PORT 6789 + +//SOCK_STREAM +//SOCK_DGRAM +//static inline int make_sock_tcp(SOCKET* sock) { return _make_sock(sock, AF_INET, SOCK_STREAM); } +//static inline int make_sock_udp(SOCKET* sock) { return _make_sock(sock, AF_INET, SOCK_DGRAM); } + +static inline int make_sock(SOCKET* sock) { +#ifdef WIN_PART + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + return ERR_SOCK_WSAStartup; + } + + if (HIBYTE(wsaData.wVersion) != 2 || \ + LOBYTE(wsaData.wVersion) != 2) { + return ERR_SOCK_DETERMINE_AGREEMENT; + } +#endif + *sock = socket(AF_INET, SOCK_STREAM, 0); + if (*sock == -1) { + return ERR_SOCK_CREATE_SOCKET; + } + return ERR_SOCK_SUCCESS; +} + +static inline int sock_connect(SOCKET sock, const char* connect_ip, unsigned short port) { + struct sockaddr_in saddr; + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + if(connect_ip == NULL) { + saddr.sin_addr.s_addr = INADDR_ANY; + } + else { + saddr.sin_addr.s_addr = inet_addr(connect_ip); + } + + if(connect(sock, (struct sockaddr*)&saddr, sizeof(saddr))) { + return ERR_SOCK_CONNECT; + } + return ERR_SOCK_SUCCESS; +} + +static inline int sock_bindlisten(SOCKET sock, const char* server_ip, unsigned short port) { + struct sockaddr_in saddr; + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + if(server_ip == NULL) { + saddr.sin_addr.s_addr = INADDR_ANY; + } + else { + saddr.sin_addr.s_addr = inet_addr(server_ip); + } + + int res = bind(sock, (struct sockaddr*)&saddr, sizeof(saddr)); + if (res == -1) { + return ERR_SOCK_BIND; + } + + res = listen(sock, SOMAXCONN); + if (res == -1) { + return ERR_SOCK_LISTEN; + } + return ERR_SOCK_SUCCESS; +} + +static inline int sock_accpet(SOCKET sock,SOCKET* client, char** accept_ip, unsigned short* port) { + struct sockaddr_in caddr; + socklen_t addrLen = sizeof(struct sockaddr_in); + + if(client == NULL) { + return ERR_SOCK_NULLPTR; + } + *client = accept(sock,(struct sockaddr*) &caddr, &addrLen); + if (*client == -1) { + return ERR_SOCK_ACCEPT; + } + + char* ip = (char*)malloc(32); + if(ip == NULL) { + return ERR_SOCK_MALLOC; + } + + int buffLen = strlen( inet_ntoa(caddr.sin_addr) ); + if(accept_ip != NULL) { + strncpy(ip, inet_ntoa(caddr.sin_addr), buffLen); + ip[buffLen] = '\0'; + *accept_ip = ip; + } + if(port != NULL) { + *port = ntohs(caddr.sin_port); + } + + return ERR_SOCK_SUCCESS; +} + +static inline int make_server_sock(SOCKET* sock, const char* server_ip, unsigned short port) { + int res = make_sock(sock); + if(res != ERR_SOCK_SUCCESS) { + return res; + } + + res = sock_bindlisten(*sock, server_ip, port); + if(res != ERR_SOCK_SUCCESS) { + return res; + } + + return ERR_SOCK_SUCCESS; +} + +static inline int make_client_sock(SOCKET* sock, const char* connect_ip, unsigned short port) { + int res = make_sock(sock); + if(res != ERR_SOCK_SUCCESS) { + return res; + } + + if(connect_ip) { + res = sock_connect(*sock, connect_ip, port); + if(res != ERR_SOCK_SUCCESS) { + return res; + } + } + + return ERR_SOCK_SUCCESS; +} + +static inline void close_sock(SOCKET sock) { + if(sock == -1) { + return; + } +#if WIN_PART + closesocket(sock); +#elif LINUX_PART + close(sock); +#endif +} + +static inline void out_sock_err(FILE* output, int errcode) { + fprintf(output, "%d", errcode); +} + +static inline void get_sock_err(char* buff_128, size_t buff_len, int errcode) { + if(buff_len < 128) { + return; + } + // enum { + // ERR_SOCK_SUCCESS, + // ERR_SOCK_MALLOC, + // ERR_SOCK_NULLPTR, + // ERR_SOCK_WSAStartup, + // ERR_SOCK_DETERMINE_AGREEMENT, + // ERR_SOCK_CREATE_SOCKET, + // ERR_SOCK_BIND, + // ERR_SOCK_LISTEN, + // ERR_SOCK_ACCEPT, + + // ERR_SOCK_CONNECT, + // }; + + sprintf(buff_128, "%3d", errcode); +} + +static inline void sock_thread(void(*thread_func)(void*), void* data) { +#if WIN_PART + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, data, 0, NULL); +#elif LINUX_PART + pthread_t pid; + pthread_create(&pid, 0, (void*(*)(void*))thread_func, data); +#endif +} + +#ifdef __cplusplus +} +#endif + + +#endif \ No newline at end of file diff --git a/test/client/CMakeLists.txt b/test/client/CMakeLists.txt new file mode 100644 index 0000000..12dd31f --- /dev/null +++ b/test/client/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.22.1) + +project(client) + +set(EXECUTABLE_OUTPUT_PATH ../) +set(CMAKE_C_STANDARD 99) +set(SRC_FILE client.c) + +add_executable(${PROJECT_NAME} ${SRC_FILE}) + +if(WIN32) + target_link_libraries(${PROJECT_NAME} wsock32 ws2_32) +endif() +# cmake .. +# cmake --build . \ No newline at end of file diff --git a/test/client/client.c b/test/client/client.c new file mode 100644 index 0000000..1c7bceb --- /dev/null +++ b/test/client/client.c @@ -0,0 +1,59 @@ +#include +#include +#include "../../ssocket.h" + +SOCKET cfd; + +void receive_message(void* param) +{ + printf("recv\n"); + int res = 0; + char Buf[1024] = { 0 }; + while (1) { + res = recv(cfd, Buf, sizeof(Buf), 0); + if (res > 0 && res <= 1024) { + printf("[Recv]:%d,%s", res, Buf); + } + else { + break; + } + } + printf("server close connect, Close in three seconds\n"); + sleeps(3); + exit(-1); + // return NULL; +} + +int main(int argc, char** argv) +{ + int res; + char Buf[1024] = { 0 }; + printf("connect server...\n"); + res = make_client_sock(&cfd, _SOCKET_TEST_IP, _SOCKET_TEST_PORT); + if (res != 0) { + printf("error client sock\nerror code:%d\npress enter to continue\n", res); + getchar(); + exit(-1); + } + printf("conncet server success\n"); + + sock_thread(receive_message, NULL); + + while (1) { + fgets(Buf, sizeof(Buf), stdin); + if (strncasecmp(Buf, "exit", strlen("exit")) == 0) { + printf("press enter to continue\n"); + getchar(); + exit(-1); + } + res = send(cfd, Buf, strlen(Buf) + 1, 0); + if (res == -1) { + printf("send error %s", Buf); + printf("press enter to continue\n"); + getchar(); + exit(-1); + } + printf("[Buf]=%d,%s", res, Buf); + } + return 0; +} \ No newline at end of file diff --git a/test/server/CMakeLists.txt b/test/server/CMakeLists.txt new file mode 100644 index 0000000..b457301 --- /dev/null +++ b/test/server/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.22.1) + +project(server) + +set(EXECUTABLE_OUTPUT_PATH ../) +set(CMAKE_C_STANDARD 99) +set(SRC_FILE server.c) + +add_executable(${PROJECT_NAME} ${SRC_FILE}) + +if(WIN32) + target_link_libraries(${PROJECT_NAME} wsock32 ws2_32) +endif() +# cmake .. +# cmake --build . \ No newline at end of file diff --git a/test/server/server.c b/test/server/server.c new file mode 100644 index 0000000..8911227 --- /dev/null +++ b/test/server/server.c @@ -0,0 +1,103 @@ +#include +#include +#include "../../ssocket.h" + +#define CLIENT_SIZE 32 +SOCKET sfd; +SOCKET cfds[CLIENT_SIZE]; + +void send_the_message(int sock, const char *Buff, int szBuf) +{ + char Buf[2048] = {0}; + int tempsock; + sprintf(Buf, ":", sock); + strcat(Buf, Buff); + + for(int i = 0; i < CLIENT_SIZE; i++) { + if(cfds[i] != 0 && cfds[i] != sock) { + tempsock = cfds[i]; + send(tempsock, Buf, strlen(Buf)+1, 0); + } + } +} + +void receive_message(void* param) +{ + int res; + char Buf[1024] = {0}; + int sock = *((SOCKET*)param); + free(param); + printf("recv start %d\n", sock); + while(1) { + res = recv(sock, Buf, sizeof(Buf), 0); + if(res == -1 || res > sizeof(Buf) || res == 0) { +#if WIN_PART + printf("\n", GetLastError()); +#elif LINUX_PART + perror("recv error"); +#endif + break; + } + printf("recv buf:%d,%s",res, Buf); + send_the_message(sock, Buf, strlen(Buf)+1); + } + printf(" exit\n", sock); + for(int i = 0; i < CLIENT_SIZE; i++) { + if(cfds[i] == sock) { + cfds[i] = 0; + } + } +} + +void acceptfunc(void* param) { + char *ip; + unsigned short port; + SOCKET* psock; + SOCKET sock; + while(1) { + psock = (SOCKET*)malloc(sizeof(SOCKET)); + if(psock == NULL) { + exit(-1); + } + if (sock_accpet(sfd, &sock, &ip, &port) != 0) { + continue; + } + *psock = sock; + for(int i = 0; i < CLIENT_SIZE; i++) { + if(cfds[i] == 0) { + cfds[i] = sock; + printf("\n", ip, port, sock); + free(ip); + sock_thread(receive_message, (void*)psock); + goto CONTINUE; + } + } + printf("client full"); + exit(-2); + CONTINUE: + psock = NULL; + } +} + +int main() +{ + char Buf[128]; + int res = make_server_sock(&sfd, _SOCKET_TEST_IP, _SOCKET_TEST_PORT); + if(res != 0) { + printf("server start error\npress enter to continue"); + getchar(); + return 0; + } + printf("server start...\n"); + sock_thread(acceptfunc, NULL); + while(1) { + fgets(Buf, sizeof(Buf), stdin); + printf("%s", Buf); + if(strncasecmp(Buf, "exit", strlen("exit")) == 0) { + printf("exit success\npress enter to continue"); + getchar(); + exit(-1); + } + } + return 0; +} \ No newline at end of file