IXWebSocket/ixwebsocket/IXSocket.cpp

240 lines
5.3 KiB
C++
Raw Normal View History

2018-09-27 23:56:48 +02:00
/*
* IXSocket.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
*/
#include "IXSocket.h"
2018-12-15 01:28:11 +01:00
#include "IXSocketConnect.h"
2019-01-06 05:38:43 +01:00
#include "IXNetSystem.h"
2018-10-09 06:42:45 +02:00
2018-09-27 23:56:48 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2018-10-09 06:42:45 +02:00
#include <assert.h>
2018-09-27 23:56:48 +02:00
#include <stdint.h>
#include <fcntl.h>
2018-10-09 06:42:45 +02:00
#include <sys/types.h>
#include <algorithm>
#include <iostream>
2018-09-27 23:56:48 +02:00
namespace ix
{
2018-12-30 06:53:33 +01:00
Socket::Socket(int fd) :
_sockfd(fd)
2018-09-27 23:56:48 +02:00
{
2018-09-27 23:56:48 +02:00
}
Socket::~Socket()
{
close();
}
void Socket::poll(const OnPollCallback& onPollCallback)
{
if (_sockfd == -1)
{
onPollCallback();
return;
}
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(_sockfd, &rfds);
2018-10-09 06:42:45 +02:00
#ifdef __linux__
FD_SET(_eventfd.getFd(), &rfds);
2018-09-27 23:56:48 +02:00
#endif
int sockfd = _sockfd;
int nfds = (std::max)(sockfd, _eventfd.getFd());
2018-09-27 23:56:48 +02:00
select(nfds + 1, &rfds, nullptr, nullptr, nullptr);
onPollCallback();
}
void Socket::wakeUpFromPoll()
{
// this will wake up the thread blocked on select, only needed on Linux
_eventfd.notify();
2018-09-27 23:56:48 +02:00
}
bool Socket::connect(const std::string& host,
int port,
std::string& errMsg,
2018-12-15 01:28:11 +01:00
const CancellationRequest& isCancellationRequested)
2018-09-27 23:56:48 +02:00
{
std::lock_guard<std::mutex> lock(_socketMutex);
if (!_eventfd.clear()) return false;
2018-09-27 23:56:48 +02:00
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
2018-09-27 23:56:48 +02:00
return _sockfd != -1;
}
void Socket::close()
{
std::lock_guard<std::mutex> lock(_socketMutex);
if (_sockfd == -1) return;
2018-10-09 06:42:45 +02:00
closeSocket(_sockfd);
2018-09-27 23:56:48 +02:00
_sockfd = -1;
}
ssize_t Socket::send(char* buffer, size_t length)
2018-09-27 23:56:48 +02:00
{
int flags = 0;
#ifdef MSG_NOSIGNAL
flags = MSG_NOSIGNAL;
#endif
return ::send(_sockfd, buffer, length, flags);
2018-09-27 23:56:48 +02:00
}
ssize_t Socket::send(const std::string& buffer)
2018-09-27 23:56:48 +02:00
{
return send((char*)&buffer[0], buffer.size());
}
ssize_t Socket::recv(void* buffer, size_t length)
2018-09-27 23:56:48 +02:00
{
int flags = 0;
#ifdef MSG_NOSIGNAL
flags = MSG_NOSIGNAL;
#endif
return ::recv(_sockfd, (char*) buffer, length, flags);
2018-09-27 23:56:48 +02:00
}
2019-01-05 02:28:13 +01:00
int Socket::getErrno()
2018-10-09 06:42:45 +02:00
{
#ifdef _WIN32
return WSAGetLastError();
#else
return errno;
#endif
}
void Socket::closeSocket(int fd)
{
#ifdef _WIN32
closesocket(fd);
#else
::close(fd);
#endif
}
bool Socket::init()
{
#ifdef _WIN32
INT rc;
WSADATA wsaData;
rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
return rc != 0;
#else
return true;
#endif
}
void Socket::cleanup()
{
#ifdef _WIN32
WSACleanup();
#endif
}
bool Socket::readByte(void* buffer,
const CancellationRequest& isCancellationRequested)
{
while (true)
{
if (isCancellationRequested()) return false;
2019-01-06 06:10:08 +01:00
ssize_t ret;
ret = recv(buffer, 1);
// We read one byte, as needed, all good.
if (ret == 1)
{
return true;
}
// There is possibly something to be read, try again
else if (ret < 0 && (getErrno() == EWOULDBLOCK ||
getErrno() == EAGAIN))
{
2019-01-04 03:33:08 +01:00
// Wait with a timeout until something is written.
// This way we are not busy looping
fd_set rfds;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 1 * 1000; // 1ms
FD_ZERO(&rfds);
FD_SET(_sockfd, &rfds);
select(_sockfd + 1, &rfds, nullptr, nullptr, &timeout);
continue;
}
// There was an error during the read, abort
else
{
return false;
}
}
}
bool Socket::writeBytes(const std::string& str,
const CancellationRequest& isCancellationRequested)
{
while (true)
{
if (isCancellationRequested()) return false;
char* buffer = const_cast<char*>(str.c_str());
int len = (int) str.size();
2019-01-06 06:10:08 +01:00
ssize_t ret = send(buffer, len);
// We wrote some bytes, as needed, all good.
if (ret > 0)
{
return ret == len;
}
// There is possibly something to be write, try again
else if (ret < 0 && (getErrno() == EWOULDBLOCK ||
getErrno() == EAGAIN))
{
continue;
}
// There was an error during the write, abort
else
{
return false;
}
}
}
std::pair<bool, std::string> Socket::readLine(const CancellationRequest& isCancellationRequested)
{
char c;
std::string line;
line.reserve(64);
for (int i = 0; i < 2 || (line[i-2] != '\r' && line[i-1] != '\n'); ++i)
{
if (!readByte(&c, isCancellationRequested))
{
return std::make_pair(false, std::string());
}
line += c;
}
return std::make_pair(true, line);
}
2018-09-27 23:56:48 +02:00
}