update husky for server

This commit is contained in:
yanyiwu 2015-01-31 10:14:16 +08:00
parent 660cd9d93e
commit 00f738a617
5 changed files with 412 additions and 505 deletions

View File

@ -6,129 +6,103 @@
#include "Limonp/Logger.hpp" #include "Limonp/Logger.hpp"
#include "Limonp/StringUtil.hpp" #include "Limonp/StringUtil.hpp"
namespace Husky namespace Husky {
{ using namespace Limonp;
using namespace Limonp; using namespace std;
using namespace std;
static const char* const KEY_METHOD = "METHOD"; static const char* const KEY_METHOD = "METHOD";
static const char* const KEY_PATH = "PATH"; static const char* const KEY_URI = "URI";
static const char* const KEY_PROTOCOL = "PROTOCOL"; static const char* const KEY_PROTOCOL = "PROTOCOL";
typedef unsigned char BYTE; typedef unsigned char BYTE;
inline BYTE toHex(BYTE x) inline BYTE toHex(BYTE x) {
{
return x > 9 ? x -10 + 'A': x + '0'; return x > 9 ? x -10 + 'A': x + '0';
} }
inline BYTE fromHex(BYTE x) inline BYTE fromHex(BYTE x) {
{
return isdigit(x) ? x-'0' : x-'A'+10; return isdigit(x) ? x-'0' : x-'A'+10;
} }
inline void URLEncode(const string &sIn, string& sOut) inline void URLEncode(const string &sIn, string& sOut) {
{ for( size_t ix = 0; ix < sIn.size(); ix++ ) {
for( size_t ix = 0; ix < sIn.size(); ix++ )
{
BYTE buf[4]; BYTE buf[4];
memset( buf, 0, 4 ); memset( buf, 0, 4 );
if( isalnum( (BYTE)sIn[ix] ) ) if( isalnum( (BYTE)sIn[ix] ) ) {
{
buf[0] = sIn[ix]; buf[0] = sIn[ix];
} } else {
else
{
buf[0] = '%'; buf[0] = '%';
buf[1] = toHex( (BYTE)sIn[ix] >> 4 ); buf[1] = toHex( (BYTE)sIn[ix] >> 4 );
buf[2] = toHex( (BYTE)sIn[ix] % 16); buf[2] = toHex( (BYTE)sIn[ix] % 16);
} }
sOut += (char *)buf; sOut += (char *)buf;
} }
}; };
inline void URLDecode(const string &sIn, string& sOut) inline void URLDecode(const string &sIn, string& sOut) {
{ for( size_t ix = 0; ix < sIn.size(); ix++ ) {
for( size_t ix = 0; ix < sIn.size(); ix++ )
{
BYTE ch = 0; BYTE ch = 0;
if(sIn[ix]=='%') if(sIn[ix]=='%') {
{
ch = (fromHex(sIn[ix+1])<<4); ch = (fromHex(sIn[ix+1])<<4);
ch |= fromHex(sIn[ix+2]); ch |= fromHex(sIn[ix+2]);
ix += 2; ix += 2;
} } else if(sIn[ix] == '+') {
else if(sIn[ix] == '+')
{
ch = ' '; ch = ' ';
} } else {
else
{
ch = sIn[ix]; ch = sIn[ix];
} }
sOut += (char)ch; sOut += (char)ch;
} }
} }
class HttpReqInfo class HttpReqInfo {
{
public: public:
HttpReqInfo() HttpReqInfo() {
{
_isHeaderFinished = false; _isHeaderFinished = false;
_isBodyFinished = false; _isBodyFinished = false;
_contentLength = 0; _contentLength = 0;
} }
public: public:
bool parseHeader(const char* buffer, size_t len) bool parseHeader(const string& buffer) {
{ return parseHeader(buffer.c_str(), buffer.size());
}
bool parseHeader(const char* buffer, size_t len) {
string headerStr(buffer, len); string headerStr(buffer, len);
size_t lpos = 0, rpos = 0; size_t lpos = 0, rpos = 0;
vector<string> buf; vector<string> buf;
rpos = headerStr.find("\n", lpos); rpos = headerStr.find("\n", lpos);
if(string::npos == rpos) if(string::npos == rpos) {
{
LogError("headerStr[%s] illegal.", headerStr.c_str()); LogError("headerStr[%s] illegal.", headerStr.c_str());
return false; return false;
} }
string firstline(headerStr, lpos, rpos - lpos); string firstline(headerStr, lpos, rpos - lpos);
trim(firstline); trim(firstline);
if(!split(firstline, buf, " ") || 3 != buf.size()) if(!split(firstline, buf, " ") || 3 != buf.size()) {
{
LogError("parse header firstline[%s] failed.", firstline.c_str()); LogError("parse header firstline[%s] failed.", firstline.c_str());
return false; return false;
} }
_headerMap[KEY_METHOD] = trim(buf[0]); _headerMap[KEY_METHOD] = trim(buf[0]);
_headerMap[KEY_PATH] = trim(buf[1]); _headerMap[KEY_URI] = trim(buf[1]);
_headerMap[KEY_PROTOCOL] = trim(buf[2]); _headerMap[KEY_PROTOCOL] = trim(buf[2]);
//first request line end _parseUri(_headerMap[KEY_URI], _path, _methodGetMap);
//parse path to _methodGetMap
if("GET" == _headerMap[KEY_METHOD])
{
_parseUrl(firstline, _methodGetMap);
}
lpos = rpos + 1; lpos = rpos + 1;
if(lpos >= headerStr.size()) if(lpos >= headerStr.size()) {
{
LogError("headerStr[%s] illegal.", headerStr.c_str()); LogError("headerStr[%s] illegal.", headerStr.c_str());
return false; return false;
} }
//message header begin //message header begin
while(lpos < headerStr.size() && string::npos != (rpos = headerStr.find('\n', lpos)) && rpos > lpos) while(lpos < headerStr.size() && string::npos != (rpos = headerStr.find('\n', lpos)) && rpos > lpos) {
{
string s(headerStr, lpos, rpos - lpos); string s(headerStr, lpos, rpos - lpos);
size_t p = s.find(':'); size_t p = s.find(':');
if(string::npos == p) if(string::npos == p) {
{
break;//encounter empty line break;//encounter empty line
} }
string k(s, 0, p); string k(s, 0, p);
string v(s, p+1); string v(s, p+1);
trim(k); trim(k);
trim(v); trim(v);
if(k.empty()||v.empty()) if(k.empty()||v.empty()) {
{
LogError("headerStr[%s] illegal.", headerStr.c_str()); LogError("headerStr[%s] illegal.", headerStr.c_str());
return false; return false;
} }
@ -139,155 +113,131 @@ namespace Husky
rpos ++; rpos ++;
_isHeaderFinished = true; _isHeaderFinished = true;
string content_length; string content_length;
if(!find("CONTENT-LENGTH", content_length)) if(!find("CONTENT-LENGTH", content_length)) {
{
_isBodyFinished = true; _isBodyFinished = true;
return true; return true;
} }
_contentLength = atoi(content_length.c_str()); _contentLength = atoi(content_length.c_str());
if(rpos < headerStr.size()) if(rpos < headerStr.size()) {
{
appendBody(headerStr.c_str() + rpos, headerStr.size() - rpos); appendBody(headerStr.c_str() + rpos, headerStr.size() - rpos);
} }
return true; return true;
//message header end //message header end
} }
void appendBody(const char* buffer, size_t len) void appendBody(const char* buffer, size_t len) {
{ if(_isBodyFinished) {
if(_isBodyFinished)
{
return; return;
} }
_body.append(buffer, len); _body.append(buffer, len);
if(_body.size() >= _contentLength) if(_body.size() >= _contentLength) {
{
_isBodyFinished = true; _isBodyFinished = true;
} } else {
else
{
_isBodyFinished = false; _isBodyFinished = false;
} }
} }
bool isHeaderFinished() const bool isHeaderFinished() const {
{
return _isHeaderFinished; return _isHeaderFinished;
} }
bool isBodyFinished() const bool isBodyFinished() const {
{
return _isBodyFinished; return _isBodyFinished;
} }
public: public:
const string& set(const string& key, const string& value) const string& set(const string& key, const string& value) {
{
return _headerMap[key] = value; return _headerMap[key] = value;
} }
bool find(const string& key, string& res)const bool find(const string& key, string& res)const {
{
return _find(_headerMap, key, res); return _find(_headerMap, key, res);
} }
bool GET(const string& argKey, string& res)const bool GET(const string& argKey, string& res)const {
{
return _find(_methodGetMap, argKey, res); return _find(_methodGetMap, argKey, res);
} }
//const string& getMethod() const //const string& getMethod() const
//{ //{
// return _headerMap.find(KEY_METHOD)->second; // return _headerMap.find(KEY_METHOD)->second;
//} //}
bool isGET() const bool isGET() const {
{
string str; string str;
if(!_find(_headerMap, KEY_METHOD, str)) if(!_find(_headerMap, KEY_METHOD, str)) {
{
return false; return false;
} }
return str == "GET"; return str == "GET";
} }
bool isPOST() const bool isPOST() const {
{
string str; string str;
if(!_find(_headerMap, KEY_METHOD, str)) if(!_find(_headerMap, KEY_METHOD, str)) {
{
return false; return false;
} }
return str == "POST"; return str == "POST";
} }
const unordered_map<string, string> & getMethodGetMap() const const unordered_map<string, string> & getMethodGetMap() const {
{
return _methodGetMap; return _methodGetMap;
} }
const unordered_map<string, string> & getHeaders() const const unordered_map<string, string> & getHeaders() const {
{
return _headerMap; return _headerMap;
} }
const string& getBody() const const string& getBody() const {
{
return _body; return _body;
} }
const string& getPath() const {
return _path;
}
private: private:
bool _isHeaderFinished; bool _isHeaderFinished;
bool _isBodyFinished; bool _isBodyFinished;
size_t _contentLength; size_t _contentLength;
unordered_map<string, string> _headerMap; unordered_map<string, string> _headerMap;
unordered_map<string, string> _methodGetMap; unordered_map<string, string> _methodGetMap;
string _path;
string _body; string _body;
friend ostream& operator<<(ostream& os, const HttpReqInfo& obj); friend ostream& operator<<(ostream& os, const HttpReqInfo& obj);
private: private:
bool _find(const std::unordered_map<string, string>& mp, const string& key, string& res)const bool _find(const std::unordered_map<string, string>& mp, const string& key, string& res)const {
{
std::unordered_map<string, string>::const_iterator it = mp.find(key); std::unordered_map<string, string>::const_iterator it = mp.find(key);
if(it == mp.end()) if(it == mp.end()) {
{
return false; return false;
} }
res = it->second; res = it->second;
return true; return true;
} }
private: private:
bool _parseUrl(const string& url, std::unordered_map<string, string>& mp) void _parseUri(const string& uri, string& path, std::unordered_map<string, string>& mp) {
{ if(uri.empty()) {
if(url.empty()) return;
{
return false;
} }
size_t pos = url.find('?'); size_t pos = uri.find('?');
if(string::npos == pos) path = uri.substr(0, pos);
{ if(string::npos == pos) {
return false; return ;
} }
size_t kleft = 0, kright = 0; size_t kleft = 0, kright = 0;
size_t vleft = 0, vright = 0; size_t vleft = 0, vright = 0;
for(size_t i = pos + 1; i < url.size();) for(size_t i = pos + 1; i < uri.size();) {
{
kleft = i; kleft = i;
while(i < url.size() && url[i] != '=') while(i < uri.size() && uri[i] != '=') {
{
i++; i++;
} }
if(i >= url.size()) if(i >= uri.size()) {
{
break; break;
} }
kright = i; kright = i;
i++; i++;
vleft = i; vleft = i;
while(i < url.size() && url[i] != '&' && url[i] != ' ') while(i < uri.size() && uri[i] != '&' && uri[i] != ' ') {
{
i++; i++;
} }
vright = i; vright = i;
mp[url.substr(kleft, kright - kleft)] = url.substr(vleft, vright - vleft); mp[uri.substr(kleft, kright - kleft)] = uri.substr(vleft, vright - vleft);
i++; i++;
} }
return true; return;
} }
}; };
inline std::ostream& operator << (std::ostream& os, const Husky::HttpReqInfo& obj) inline std::ostream& operator << (std::ostream& os, const Husky::HttpReqInfo& obj) {
{ return os << obj._headerMap << obj._methodGetMap/* << obj._methodPostMap*/ << obj._path << obj._body ;
return os << obj._headerMap << obj._methodGetMap/* << obj._methodPostMap*/ << obj._body; }
}
} }

View File

@ -3,16 +3,14 @@
#include "HttpReqInfo.hpp" #include "HttpReqInfo.hpp"
namespace Husky namespace Husky {
{ class IRequestHandler {
class IRequestHandler
{
public: public:
virtual ~IRequestHandler(){}; virtual ~IRequestHandler() {};
public: public:
virtual bool do_GET(const HttpReqInfo& httpReq, string& res) const = 0; virtual bool do_GET(const HttpReqInfo& httpReq, string& res) const = 0;
virtual bool do_POST(const HttpReqInfo& httpReq, string& res) const = 0; virtual bool do_POST(const HttpReqInfo& httpReq, string& res) const = 0;
}; };
} }
#endif #endif

53
server/Husky/NetUtils.hpp Normal file
View File

@ -0,0 +1,53 @@
#ifndef HUSKY_NET_UTILS_HPP
#define HUSKY_NET_UTILS_HPP
#include <stdio.h>
#include <string.h>
#include <cassert>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <vector>
#include "Limonp/StdExtension.hpp"
#include "Limonp/HandyMacro.hpp"
namespace Husky {
static const size_t LISTEN_QUEUE_LEN = 1024;
typedef int SocketFd;
SocketFd CreateAndListenSocket(int port) {
SocketFd sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
LIMONP_CHECK(sock != -1);
int optval = 1; // nozero
int ret;
ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
LIMONP_CHECK(-1 != ret);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
ret = ::bind(sock, (sockaddr*)&addr, sizeof(addr));
LIMONP_CHECK(-1 != ret);
ret = listen(sock, LISTEN_QUEUE_LEN);
LIMONP_CHECK(-1 != ret);
return sock;
}
const char* const HTTP_FORMAT = "HTTP/1.1 200 OK\r\nConnection: close\r\nServer: HuskyServer/1.0.0\r\nContent-Type: text/json; charset=%s\r\nContent-Length: %d\r\n\r\n%s";
const char* const CHARSET_UTF8 = "UTF-8";
} // namespace Husky
#endif

View File

@ -1,108 +1,39 @@
#ifndef HUSKY_EPOLLSERVER_H #ifndef HUSKY_THREADPOOLSERVER_H
#define HUSKY_EPOLLSERVER_H #define HUSKY_THREADPOOLSERVER_H
#include <stdio.h> #include "NetUtils.hpp"
#include <string.h>
#include <cassert>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <vector>
#include "WorkerThread.hpp" #include "WorkerThread.hpp"
namespace Husky namespace Husky {
{ using namespace Limonp;
using namespace Limonp;
class ThreadPoolServer
{
private:
static const size_t LISTEN_QUEUE_LEN = 1024;
class ThreadPoolServer {
private: private:
ThreadPool _pool; ThreadPool _pool;
const IRequestHandler & _reqHandler; const IRequestHandler & _reqHandler;
int _host_socket; int _host_socket;
public: public:
ThreadPoolServer(size_t thread_number, size_t queue_max_size, size_t port, const IRequestHandler & handler): ThreadPoolServer(size_t thread_number, size_t queue_max_size, size_t port, const IRequestHandler & handler):
_pool(thread_number, queue_max_size), _reqHandler(handler), _host_socket(-1) _pool(thread_number, queue_max_size), _reqHandler(handler), _host_socket(-1) {
{ _host_socket = CreateAndListenSocket(port);
_init_host_socket(port); }
}; ~ThreadPoolServer() {};
~ThreadPoolServer(){};
public: public:
bool start() bool start() {
{
_pool.start(); _pool.start();
sockaddr_in clientaddr; sockaddr_in clientaddr;
socklen_t nSize = sizeof(clientaddr); socklen_t nSize = sizeof(clientaddr);
int clientSock; int clientSock;
while(true) while(true) {
{ if(-1 == (clientSock = accept(_host_socket, (struct sockaddr*) &clientaddr, &nSize))) {
if(-1 == (clientSock = accept(_host_socket, (struct sockaddr*) &clientaddr, &nSize)))
{
LogError(strerror(errno)); LogError(strerror(errno));
break; break;
//continue;
} }
_pool.add(CreateTask<WorkerThread,int, const IRequestHandler&>(clientSock, _reqHandler)); _pool.add(CreateTask<WorkerThread,int, const IRequestHandler&>(clientSock, _reqHandler));
//_response(clientSock);
//_closesocket(clientSock);
} }
return true; return true;
} }
private: };
bool _init_host_socket(size_t port)
{
_host_socket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == _host_socket)
{
LogFatal(strerror(errno));
return false;
}
int nRet = 1;
if(-1 == setsockopt(_host_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&nRet, sizeof(nRet)))
{
LogFatal(strerror(errno));
return false;
}
struct sockaddr_in addrSock;
addrSock.sin_family = AF_INET;
addrSock.sin_port = htons(port);
addrSock.sin_addr.s_addr = htonl(INADDR_ANY);
if(-1 == ::bind(_host_socket, (sockaddr*)&addrSock, sizeof(sockaddr)))
{
LogFatal(strerror(errno));
_closesocket(_host_socket);
return false;
}
if(-1 == listen(_host_socket, LISTEN_QUEUE_LEN))
{
LogFatal(strerror(errno));
return false;
}
LogInfo("create socket listening port[%u] init ok", port);
return true;
}
void _closesocket(int sockfd)
{
if(-1 == close(sockfd))
{
LogError(strerror(errno));
return;
}
}
};
} }
#endif #endif

View File

@ -3,64 +3,51 @@
#include "Limonp/ThreadPool.hpp" #include "Limonp/ThreadPool.hpp"
#include "IRequestHandler.hpp" #include "IRequestHandler.hpp"
#include "NetUtils.hpp"
namespace Husky namespace Husky {
{ const char* const CLIENT_IP_K = "CLIENT_IP";
const char* const HTTP_FORMAT = "HTTP/1.1 200 OK\r\nConnection: close\r\nServer: HuskyServer/1.0.0\r\nContent-Type: text/json; charset=%s\r\nContent-Length: %d\r\n\r\n%s"; const size_t RECV_BUFFER_SIZE = 16 * 1024;
const char* const CHARSET_UTF8 = "UTF-8";
const char* const CLIENT_IP_K = "CLIENT_IP";
const size_t RECV_BUFFER_SIZE = 16 * 1024;
const struct linger LNG = {1, 1}; const struct linger LNG = {1, 1};
const struct timeval SOCKET_TIMEOUT = {16, 0}; const struct timeval SOCKET_TIMEOUT = {16, 0};
class WorkerThread: public ITask {
class WorkerThread: public ITask
{
public: public:
WorkerThread(int sockfs, const IRequestHandler& reqHandler): WorkerThread(int sockfs, const IRequestHandler& reqHandler):
_sockfd(sockfs), _reqHandler(reqHandler) _sockfd(sockfs), _reqHandler(reqHandler) {
{
} }
virtual ~WorkerThread() virtual ~WorkerThread() {
{
} }
private: private:
int _sockfd; int _sockfd;
const IRequestHandler& _reqHandler; const IRequestHandler& _reqHandler;
public: public:
void run() void run() {
{ do {
do if(!_setsockopt(_sockfd)) {
{ LogFatal("_getsockopt failed.");
if(!_setsockopt(_sockfd))
{
LogFatal("_setsockopt failed.");
break; break;
} }
string strSnd, strRetByHandler; string strSnd, strRetByHandler;
HttpReqInfo httpReq; HttpReqInfo httpReq;
if(!_receive(_sockfd, httpReq)) if(!_receive(_sockfd, httpReq)) {
{
LogFatal("_receive failed."); LogFatal("_receive failed.");
break; break;
} }
if(httpReq.isGET() && !_reqHandler.do_GET(httpReq, strRetByHandler)) if(httpReq.isGET() && !_reqHandler.do_GET(httpReq, strRetByHandler)) {
{
LogError("do_GET failed."); LogError("do_GET failed.");
break; break;
} }
if(httpReq.isPOST() && !_reqHandler.do_POST(httpReq, strRetByHandler)) if(httpReq.isPOST() && !_reqHandler.do_POST(httpReq, strRetByHandler)) {
{
LogError("do_POST failed."); LogError("do_POST failed.");
break; break;
} }
strSnd = string_format(HTTP_FORMAT, CHARSET_UTF8, strRetByHandler.length(), strRetByHandler.c_str()); strSnd = string_format(HTTP_FORMAT, CHARSET_UTF8, strRetByHandler.length(), strRetByHandler.c_str());
if(!_send(_sockfd, strSnd)) if(!_send(_sockfd, strSnd)) {
{
LogError("_send failed."); LogError("_send failed.");
break; break;
} }
@ -68,22 +55,17 @@ namespace Husky
} while(false); } while(false);
if(-1 == close(_sockfd)) if(-1 == close(_sockfd)) {
{
LogError(strerror(errno)); LogError(strerror(errno));
} }
} }
private: private:
bool _receive(int sockfd, HttpReqInfo& httpInfo) const bool _receive(int sockfd, HttpReqInfo& httpInfo) const {
{
char recvBuf[RECV_BUFFER_SIZE]; char recvBuf[RECV_BUFFER_SIZE];
int n = 0; int n = 0;
while(!httpInfo.isBodyFinished() && (n = recv(sockfd, recvBuf, RECV_BUFFER_SIZE, 0)) > 0) while(!httpInfo.isBodyFinished() && (n = recv(sockfd, recvBuf, RECV_BUFFER_SIZE, 0)) > 0) {
{ if(!httpInfo.isHeaderFinished()) {
if(!httpInfo.isHeaderFinished()) if(!httpInfo.parseHeader(recvBuf, n)) {
{
if(!httpInfo.parseHeader(recvBuf, n))
{
LogError("parseHeader failed. "); LogError("parseHeader failed. ");
return false; return false;
} }
@ -91,43 +73,36 @@ namespace Husky
} }
httpInfo.appendBody(recvBuf, n); httpInfo.appendBody(recvBuf, n);
} }
if(n < 0) if(n < 0) {
{
LogError(strerror(errno)); LogError(strerror(errno));
return false; return false;
} }
return true; return true;
} }
bool _send(int sockfd, const string& strSnd) const bool _send(int sockfd, const string& strSnd) const {
{ if(-1 == send(sockfd, strSnd.c_str(), strSnd.length(), 0)) {
if(-1 == send(sockfd, strSnd.c_str(), strSnd.length(), 0))
{
LogError(strerror(errno)); LogError(strerror(errno));
return false; return false;
} }
return true; return true;
} }
bool _setsockopt(int sockfd) const bool _setsockopt(int sockfd) const {
{ if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&LNG, sizeof(LNG))) {
if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&LNG, sizeof(LNG)))
{
LogError(strerror(errno)); LogError(strerror(errno));
return false; return false;
} }
if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&SOCKET_TIMEOUT, sizeof(SOCKET_TIMEOUT))) if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&SOCKET_TIMEOUT, sizeof(SOCKET_TIMEOUT))) {
{
LogError(strerror(errno)); LogError(strerror(errno));
return false; return false;
} }
if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&SOCKET_TIMEOUT, sizeof(SOCKET_TIMEOUT))) if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&SOCKET_TIMEOUT, sizeof(SOCKET_TIMEOUT))) {
{
LogError(strerror(errno)); LogError(strerror(errno));
return false; return false;
} }
return true; return true;
} }
}; };
} }
#endif #endif