c++ socket (4) 选项设置
socket选项设置及获取,一般就会集合成方便类
#ifndef __SOCKET_H__
#define __SOCKET_H__
#include <memory>
#include <string>
#include <memory.h>
#ifdef _WIN32
#include <WinSock2.h>
#include <ws2tcpip.h>
#define socket_t SOCKET
#define invalid_socket INVALID_SOCKET
#define socklen_t int
#define errno GetLastError()
#pragma comment(lib, "ws2_32.lib")
#endif // _WIN32
#ifdef __linux__
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#define socket_t int
#define invalid_socket -1
#define closesocket close
#endif
class Socket {
static bool init_socket()
{
#ifdef _WIN32
WSADATA ws;
static bool _init = (0 == WSAStartup(MAKEWORD(2, 2), &ws));
return _init;
#endif
return true;
}
public:
//创建服务器(优先使用IPv6)
static socket_t CreateServer(int port, int type = SOCK_STREAM)
{
init_socket();
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // 任何地址家族
hints.ai_socktype = type; // 任何套接字类型
hints.ai_protocol = (type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;
int ret = 0;
struct addrinfo* res;
if ((ret = getaddrinfo(nullptr, std::to_string(port).c_str(), &hints, &res)) != 0) {
std::cout << "err getaddrinfo:" << gai_strerror(ret) << std::endl;
return invalid_socket;
}
int reuseAddr = 1;
socket_t skt = invalid_socket;
for (struct addrinfo* p = res; p != 0; p = p->ai_next) {
if ((p->ai_family != AF_INET && p->ai_family != AF_INET6)
|| (skt != invalid_socket && p->ai_family == AF_INET)) {
continue;
}
//关闭之前的IPv4
if (skt != invalid_socket) {
closesocket(skt);
skt = invalid_socket;
}
//创建socket 及 bind
if (invalid_socket == (skt = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
|| 0 != setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddr, sizeof(int))
|| 0 != bind(skt, (const struct sockaddr*)p->ai_addr, p->ai_addrlen)) {
if (skt != invalid_socket) {
closesocket(skt);
skt = invalid_socket;
}
continue;
}
//如果是IPv6,就不必再继续了
if (skt != invalid_socket && p->ai_family == AF_INET6) {
break;
}
}
freeaddrinfo(res);
return skt;
}
//连接服务端
static socket_t ConnectServer(const char* addr, int port, int timeout = 0)
{
init_socket();
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int ret = 0;
struct addrinfo* res;
if ((ret = getaddrinfo(addr, std::to_string(port).c_str(), &hints, &res)) != 0) {
std::cerr << "getaddrinfo:" << gai_strerror(ret) << std::endl;
return invalid_socket;
}
int reuseAddr = 1;
socket_t skt = invalid_socket;
for (struct addrinfo* p = res; p != 0 && skt == invalid_socket; p = p->ai_next) {
if ((p->ai_family != AF_INET && p->ai_family != AF_INET6)
|| invalid_socket == (skt = socket(p->ai_family, p->ai_socktype, p->ai_protocol))) {
continue;
}
int oldSec = 0;
if (timeout > 0 && GetSendTimeout(skt, oldSec)) {
SetSendTimeout(skt, timeout);
}
if (0 != connect(skt, (const struct sockaddr*)p->ai_addr, p->ai_addrlen)) {
std::cerr << "connect:" << strerror(errno)<<std::endl;
closesocket(skt);
skt = invalid_socket;
}
if (timeout > 0 && skt != invalid_socket) {
SetSendTimeout(skt, oldSec);
}
}
freeaddrinfo(res);
return skt;
}
//获取发送超时
static bool GetSendTimeout(socket_t skt, int& seconds)
{
struct timeval tv;
socklen_t len = sizeof(struct timeval);
if (0 != getsockopt(skt, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, &len)) {
return false;
}
seconds = tv.tv_sec;
return true;
}
//设置发送超时
static bool SetSendTimeout(socket_t skt, int seconds)
{
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = 0;
socklen_t len = sizeof(struct timeval);
return 0 == setsockopt(skt, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, len);
}
//获取接收超时
static bool GetRecvTimeout(socket_t skt, int& seconds)
{
struct timeval tv;
socklen_t len = sizeof(struct timeval);
if (0 != getsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, &len)) {
return false;
}
seconds = tv.tv_sec;
return true;
}
//设置接收超时
static bool SetRecvTimeout(socket_t skt, int seconds)
{
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = 0;
socklen_t len = sizeof(struct timeval);
return 0 == setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, len);
}
//获取发送缓冲区大小
static bool GetSendBufferSize(socket_t skt, int& size)
{
socklen_t len = sizeof(int);
if (0 != getsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&size, &len)) {
return false;
}
return true;
}
//设置发送缓冲区大小
static bool SetSendBufferSize(socket_t skt, int size)
{
socklen_t len = sizeof(int);
return 0 == setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&size, len);
}
//获取接收缓冲区大小
static bool GetRecvBufferSize(socket_t skt, int& size)
{
socklen_t len = sizeof(int);
if (0 != getsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&size, &len)) {
return false;
}
return true;
}
//设置接收缓冲区大小
static bool SetRecvBufferSize(socket_t skt, int size)
{
socklen_t len = sizeof(int);
return 0 == setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&size, len);
}
//设置地址复用
static bool SetReuseAddr(socket_t skt)
{
int reuse = 1;
socklen_t len = sizeof(int);
return 0 == setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, len);
}
//设置端口复用
static bool SetReusePort(socket_t skt)
{
#ifdef __linux__
int reuse = 1;
socklen_t len = sizeof(int);
return 0 == setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, (char*)&reuse, len);
#endif
return true;
}
//设置保持连接
static bool SetKeepAlive(socket_t skt)
{
int alive = 1;
socklen_t len = sizeof(int);
return 0 == setsockopt(skt, SOL_SOCKET, SO_KEEPALIVE, (char*)&alive, len);
}
//获取LINGER配置
static bool GetLinger(socket_t skt, int& onoff, int& seconds)
{
struct linger val;
socklen_t len = sizeof(struct linger);
if (0 != getsockopt(skt, SOL_SOCKET, SO_LINGER, (char*)&val, &len)) {
return false;
}
onoff = val.l_onoff;
seconds = seconds;
return true;
}
//设置LINGER配置
static bool SetLinger(socket_t skt, int onoff, int seconds)
{
struct linger val;
val.l_onoff = onoff;
val.l_linger = seconds;
socklen_t len = sizeof(struct linger);
return 0 == setsockopt(skt, SOL_SOCKET, SO_LINGER, (char*)&val, len);
}
//获取IO模式
static bool GetIOMode(socket_t skt, bool& blockMode)
{
#ifdef __linux__
int flags = 0;
if (-1 == (flags = fcntl(skt, F_GETFL, 0))) {
return false;
}
blockMode = 0 == (flags & O_NONBLOCK);
return true;
#else
int val = 0;
socklen_t len = sizeof(int);
if (0 != getsockopt(skt, SOL_SOCKET, SO_OOBINLINE, (char*)&val, &len)) {
return false;
}
blockMode = val == 1;
return true;
#endif
}
//设置IO模式
static bool SetIOMode(socket_t skt, bool blockMode)
{
#ifdef __linux__
int flags = 0;
if (-1 == (flags = fcntl(skt, F_GETFL, 0))) {
return false;
}
blockMode ? flags &= ~O_NONBLOCK : flags |= O_NONBLOCK;
return -1 != fcntl(skt, F_SETFL, flags);
#else
u_long mode = blockMode ? 1 : 0;
return ioctlsocket(skt, FIONBIO, &mode) == SOCKET_ERROR;
#endif
}
//填充sockaddr_in
static bool MakeAddress(struct sockaddr_in& addr, const char* ip, int port)
{
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
//addr.sin_addr.s_addr = inet_addr(ip);
return (ip == nullptr) ? true : 1 == inet_pton(AF_INET, ip, &addr.sin_addr.s_addr);
}
//填充sockaddr_in6
static bool MakeAddress(struct sockaddr_in6& addr, const char* ip6, int port)
{
memset(&addr, 0, sizeof(struct sockaddr_in6));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
return (ip6 == nullptr) ? true : 1 == inet_pton(AF_INET6, ip6, &addr.sin6_addr);
}
//解析sockaddr_in, 得到IP串及地址
static bool ParseAddress(const struct sockaddr_in* addr, std::string& ip, int& port)
{
char buf[32] = { 0 };
port = ntohs(addr->sin_port);
if (nullptr == inet_ntop(AF_INET, &addr->sin_addr.s_addr, buf, 32)) {
return false;
}
ip = buf;
return true;
}
//解析sockaddr_in, 得到IP串及地址
static bool ParseAddress(const struct sockaddr_in6* addr, std::string& ip, int& port)
{
char buf[32] = { 0 };
port = ntohs(addr->sin6_port);
if (nullptr == inet_ntop(AF_INET, &addr->sin6_addr, buf, 32)) {
return false;
}
ip = buf;
return true;
}
};
#endif
选项说明(参考下面地址, 就不搬运了)
SOL_SOCKET 套接字选项 (Winsock2.h) - Win32 apps | Microsoft Learn