正文 – 全部的都要求前戏
winds 的 select 和 linux 的 select 是五个精光两样的东西.
然则凡人喜好把它们揉在一起.
全局的测试宗旨main 函数有个别之类
前言 – 1个简易开场白
本文是个源码软文, 专注解决客户端的跨平台的connect难题. 服务器的connect
要比客户端多着想壹丁点.
正文是个源码软文, 专注化解客户端的跨平台的connect难点. 服务器的connect
要比客户端多着想壹丁点.
intsocket_connecto(socket_t s, const sockaddr_t * addr, int ms) {int n, r;struct timeval to;fd_set rset, wset, eset;// 还是阻塞的connectif (ms < 0) return socket_connect;// 非阻塞登录, 先设置非阻塞模式r = socket_set_nonblock;if {fprintf(stderr, "socket_set_nonblock error!\n");return r;}// 尝试连接一下, 非阻塞connect 返回 -1 并且 errno == EINPROGRESS 表示正在建立链接r = socket_connect;if (r >= 0) goto __return;// 链接不再进行中直接返回, linux是 EINPROGRESS,winds是 WASEWOULDBLOCKif (errno != ECONNECTED) {fprintf(stderr, "socket_connect error r = %d!\n", r);goto __return;}// 超时 timeout, 直接返回结果 ErrBase = -1 错误r = -1;if goto __return;FD_ZERO(&rset); FD_SET(s, &rset);FD_ZERO(&wset); FD_SET(s, &wset);FD_ZERO(&eset); FD_SET(s, &eset);to.tv_sec = ms / 1000;to.tv_usec = (ms % 1000) * 1000;n = selects + 1, &rset, &wset, &eset, &to);// 超时直接滚 or linux '异常'直接返回 0if (n <= 0) goto __return;// 当连接成功时候,描述符会变成可写if (n == 1 && FD_ISSET(s, &wset)) {r = 0;goto __return;}// 当连接建立遇到错误时候, winds 抛出异常, linux 描述符变为即可读又可写if (FD_ISSET(s, &eset) || n == 2) {socklen_t len = sizeof n;// 只要最后没有 error那就 链接成功if (!getsockopt(s, SOL_SOCKET, SO_ERROR, &n, &len) && !n)r = 0;}__return:socket_set_block;return r;}socket_tsocket_connectos(const char * host, uint16_t port, int ms) {int r;sockaddr_t addr;socket_t s = socket_stream();if (s == INVALID_SOCKET) {fprintf(stderr, "socket_stream is error!\n");return INVALID_SOCKET;}// 构建ip地址r = socket_addr(host, port, &addr);if return r;r = socket_connecto(s, &addr, ms);if {socket_close;fprintf(stderr, "socket_connecto host port ms = %s, %u, %d!\n", host, port, ms);return INVALID_SOCKET;}return s;}
每3回突破都来之不易. 假诺须要在工程中落到实处一份 nonblocking select
connect. 能够一直用地点思路.
extern int socket_addr(const char * ip, uint16_t port, sockaddr_t * addr);
extern int socket_connecto(socket_t s, const sockaddr_t * addr, int ms);
extern socket_t socket_connectos(const char * host, uint16_t port, int ms);
//
// gcc -g -O2 -Wall -o main.exe main.c
//
int main(int argc, char * argv[]) {
socket_start();
socket_t s = socket_connectos("127.0.0.1", 80, 10000);
if (s == INVALID_SOCKET) {
fprintf(stderr, "socket_connectos is error!!\n");
exit(EXIT_FAILURE);
}
puts("socket_connectos is success!");
return EXIT_SUCCESS;
}
int
socket_addr(const char * ip, uint16_t port, sockaddr_t * addr) {
if (!ip || !*ip || !addr) {
fprintf(stderr, "check empty ip = %s, port = %hu, addr = %p.\n", ip, port, addr);
return -1;
}
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
addr->sin_addr.s_addr = inet_addr(ip);
if (addr->sin_addr.s_addr == INADDR_NONE) {
struct hostent * host = gethostbyname(ip);
if (!host || !host->h_addr) {
fprintf(stderr, "check ip is error = %s.\n", ip);
return -1;
}
// 尝试一种, 默认ipv4
memcpy(&addr->sin_addr, host->h_addr, host->h_length);
}
memset(addr->sin_zero, 0, sizeof addr->sin_zero);
return 0;
}
正文 – 全数的都急需前戏
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <signal.h>
//
// IGNORE_SIGPIPE - 管道破裂,忽略SIGPIPE信号
//
#define IGNORE_SIGNAL(sig) signal(sig, SIG_IGN)
#ifdef __GNUC__
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <sys/select.h>
#include <sys/resource.h>
/*
* This is used instead of -1, since the
* SOCKET type is unsigned.
*/
#define INVALID_SOCKET (~0)
#define SOCKET_ERROR (-1)
#define IGNORE_SIGPIPE() IGNORE_SIGNAL(SIGPIPE)
// connect链接还在进行中, linux显示 EINPROGRESS,winds是 WSAEWOULDBLOCK
#define ECONNECTED EINPROGRESS
typedef int socket_t;
#elif _MSC_VER
#undef FD_SETSIZE
#define FD_SETSIZE (1024)
#include <ws2tcpip.h>
#undef errno
#define errno WSAGetLastError()
#define IGNORE_SIGPIPE()
// connect链接还在进行中, linux显示 EINPROGRESS,winds是 WSAEWOULDBLOCK
#define ECONNECTED WSAEWOULDBLOCK
typedef int socklen_t;
typedef SOCKET socket_t;
static inline void _socket_start(void) {
WSACleanup();
}
#endif
// 目前通用的tcp udp v4地址
typedef struct sockaddr_in sockaddr_t;
//
// socket_start - 单例启动socket库的初始化方法
// socket_addr - 通过ip, port 得到 ipv4 地址信息
//
inline void socket_start(void) {
#ifdef _MSC_VER
# pragma comment(lib, "ws2_32.lib")
WSADATA wsad;
WSAStartup(WINSOCK_VERSION, &wsad);
atexit(_socket_start);
#endif
IGNORE_SIGPIPE();
}
#include <stdio.h>#include <errno.h>#include <stdint.h>#include <stddef.h>#include <stdlib.h>#include <signal.h>//// IGNORE_SIGPIPE - 管道破裂,忽略SIGPIPE信号//#define IGNORE_SIGNAL signal(sig, SIG_IGN)#ifdef __GNUC__#include <fcntl.h>#include <netdb.h>#include <unistd.h>#include <arpa/inet.h>#include <netinet/tcp.h>#include <sys/un.h>#include <sys/uio.h>#include <sys/select.h>#include <sys/resource.h>/** This is used instead of -1, since the* SOCKET type is unsigned.*/#define INVALID_SOCKET #define SOCKET_ERROR #define IGNORE_SIGPIPE() IGNORE_SIGNAL// connect链接还在进行中, linux显示 EINPROGRESS,winds是 WSAEWOULDBLOCK#define ECONNECTED EINPROGRESStypedef int socket_t;#elif _MSC_VER#undef FD_SETSIZE#define FD_SETSIZE #include <ws2tcpip.h>#undef errno#define errno WSAGetLastError()#define IGNORE_SIGPIPE()// connect链接还在进行中, linux显示 EINPROGRESS,winds是 WSAEWOULDBLOCK#define ECONNECTED WSAEWOULDBLOCKtypedef int socklen_t;typedef SOCKET socket_t;static inline void _socket_start(void) { WSACleanup();}#endif// 目前通用的tcp udp v4地址typedef struct sockaddr_in sockaddr_t;//// socket_start - 单例启动socket库的初始化方法// socket_addr - 通过ip, port 得到 ipv4 地址信息// inline void socket_start(void) {#ifdef _MSC_VER# pragma comment(lib, "ws2_32.lib") WSADATA wsad; WSAStartup(WINSOCK_VERSION, &wsad); atexit(_socket_start);#endif IGNORE_SIGPIPE();}
那初叶吧 . 一切从丑陋的跨平台宏起先
序言 – 1个粗略开场白
非阻塞的connect业务是个自带超时机制的 connect.
完毕机制无外乎利用select(也有 epoll的).
那起来吧 . 壹切从丑陋的跨平台宏开端
有时机再扯. 对于 select 网上资料太多, 差不多都有点不痛不痒. 了然真相推荐
man and msdn !!!
每二遍突破都来之不易. 假设须求在工程中贯彻一份 nonblocking select
connect. 能够一向用地点思路.
inline socket_t socket_stream(void) { return socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);}inline int socket_close(socket_t s) {#ifdef _MSC_VER return closesocket;#else return close;#endif}inline int socket_set_block(socket_t s) {#ifdef _MSC_VER u_long mode = 0; return ioctlsocket(s, FIONBIO, &mode);#else int mode = fcntl(s, F_GETFL, 0); if (mode == SOCKET_ERROR) return SOCKET_ERROR; if (mode & O_NONBLOCK) return fcntl(s, F_SETFL, mode & ~O_NONBLOCK); return 0;#endif }inline int socket_set_nonblock(socket_t s) {#ifdef _MSC_VER u_long mode = 1; return ioctlsocket(s, FIONBIO, &mode);#else int mode = fcntl(s, F_GETFL, 0); if (mode == SOCKET_ERROR) return SOCKET_ERROR; if (mode & O_NONBLOCK) return 0; return fcntl(s, F_SETFL, mode | O_NONBLOCK);#endif }inline int socket_connect(socket_t s, const sockaddr_t * addr) { return connect(s, (const struct sockaddr *)addr, sizeof(*addr));}
后记 – 感悟
后记 – 感悟
winds 的 select 和 linux 的 select 是多个精光两样的东西.
不过凡人喜欢把它们揉在一起.
代码依然少点注释好, 那多少个老人说的代码即注释好像有点道理
有机会再扯. 对于 select 网上资料太多, 差不离都有点不痛不痒. 了然本质推荐
man and msdn !!!
那儿再封装壹些, 简化操作.
int
socket_connecto(socket_t s, const sockaddr_t * addr, int ms) {
int n, r;
struct timeval to;
fd_set rset, wset, eset;
// 还是阻塞的connect
if (ms < 0) return socket_connect(s, addr);
// 非阻塞登录, 先设置非阻塞模式
r = socket_set_nonblock(s);
if (r < 0) {
fprintf(stderr, "socket_set_nonblock error!\n");
return r;
}
// 尝试连接一下, 非阻塞connect 返回 -1 并且 errno == EINPROGRESS 表示正在建立链接
r = socket_connect(s, addr);
if (r >= 0) goto __return;
// 链接不再进行中直接返回, linux是 EINPROGRESS,winds是 WASEWOULDBLOCK
if (errno != ECONNECTED) {
fprintf(stderr, "socket_connect error r = %d!\n", r);
goto __return;
}
// 超时 timeout, 直接返回结果 ErrBase = -1 错误
r = -1;
if (ms == 0) goto __return;
FD_ZERO(&rset); FD_SET(s, &rset);
FD_ZERO(&wset); FD_SET(s, &wset);
FD_ZERO(&eset); FD_SET(s, &eset);
to.tv_sec = ms / 1000;
to.tv_usec = (ms % 1000) * 1000;
n = select((int)s + 1, &rset, &wset, &eset, &to);
// 超时直接滚 or linux '异常'直接返回 0
if (n <= 0) goto __return;
// 当连接成功时候,描述符会变成可写
if (n == 1 && FD_ISSET(s, &wset)) {
r = 0;
goto __return;
}
// 当连接建立遇到错误时候, winds 抛出异常, linux 描述符变为即可读又可写
if (FD_ISSET(s, &eset) || n == 2) {
socklen_t len = sizeof n;
// 只要最后没有 error那就 链接成功
if (!getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&n, &len) && !n)
r = 0;
}
__return:
socket_set_block(s);
return r;
}
socket_t
socket_connectos(const char * host, uint16_t port, int ms) {
int r;
sockaddr_t addr;
socket_t s = socket_stream();
if (s == INVALID_SOCKET) {
fprintf(stderr, "socket_stream is error!\n");
return INVALID_SOCKET;
}
// 构建ip地址
r = socket_addr(host, port, &addr);
if (r < 0)
return r;
r = socket_connecto(s, &addr, ms);
if (r < 0) {
socket_close(s);
fprintf(stderr, "socket_connecto host port ms = %s, %u, %d!\n", host, port, ms);
return INVALID_SOCKET;
}
return s;
}
全局的测试大旨main 函数有的之类
此刻再装进壹些, 简化操作.
那边才是您要的总体, 真正的跨平台的客户端非阻塞 connect.
非阻塞的connect业务是个自带超时机制的 connect.
完结机制无外乎利用select(也有 epoll的).
extern int socket_addr(const char * ip, uint16_t port, sockaddr_t * addr);extern int socket_connecto(socket_t s, const sockaddr_t * addr, int ms);extern socket_t socket_connectos(const char * host, uint16_t port, int ms);//// gcc -g -O2 -Wall -o main.exe main.c//int main(int argc, char * argv[]) {socket_start();socket_t s = socket_connectos("127.0.0.1", 80, 10000);if (s == INVALID_SOCKET) {fprintf(stderr, "socket_connectos is error!!\n");exit(EXIT_FAILURE);}puts("socket_connectos is success!");return EXIT_SUCCESS;}
int socket_addr(const char * ip, uint16_t port, sockaddr_t * addr) { if (!ip || !*ip || !addr) { fprintf(stderr, "check empty ip = %s, port = %hu, addr = %p.\n", ip, port, addr); return -1; } addr->sin_family = AF_INET; addr->sin_port = htons; addr->sin_addr.s_addr = inet_addr; if (addr->sin_addr.s_addr == INADDR_NONE) { struct hostent * host = gethostbyname; if (!host || !host->h_addr) { fprintf(stderr, "check ip is error = %s.\n", ip); return -1; } // 尝试一种, 默认ipv4 memcpy(&addr->sin_addr, host->h_addr, host->h_length); } memset(addr->sin_zero, 0, sizeof addr->sin_zero); return 0;}
骨干便是不一致平台的select api 的应用罢了. 你领悟了或然就少趟点坑,
多左顾右盼些~
代码仍然少点注释好, 那多少个老人说的代码即注释好像有点道理
基本就是分歧平台的select api 的应用罢了. 你领会了大概就少趟点坑,
多心急火燎些~
inline socket_t socket_stream(void) {
return socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
}
inline int socket_close(socket_t s) {
#ifdef _MSC_VER
return closesocket(s);
#else
return close(s);
#endif
}
inline int socket_set_block(socket_t s) {
#ifdef _MSC_VER
u_long mode = 0;
return ioctlsocket(s, FIONBIO, &mode);
#else
int mode = fcntl(s, F_GETFL, 0);
if (mode == SOCKET_ERROR)
return SOCKET_ERROR;
if (mode & O_NONBLOCK)
return fcntl(s, F_SETFL, mode & ~O_NONBLOCK);
return 0;
#endif
}
inline int socket_set_nonblock(socket_t s) {
#ifdef _MSC_VER
u_long mode = 1;
return ioctlsocket(s, FIONBIO, &mode);
#else
int mode = fcntl(s, F_GETFL, 0);
if (mode == SOCKET_ERROR)
return SOCKET_ERROR;
if (mode & O_NONBLOCK)
return 0;
return fcntl(s, F_SETFL, mode | O_NONBLOCK);
#endif
}
inline int socket_connect(socket_t s, const sockaddr_t * addr) {
return connect(s, (const struct sockaddr *)addr, sizeof(*addr));
}
此地才是您要的万事, 诚然的跨平台的客户端非阻塞 connect.