mirror of
https://github.com/debauchee/barrier.git
synced 2024-12-26 20:53:22 +03:00
c6ecc79c0d
restart if the X server connection was lost; since synergy is likely to be started by xdm or the user's xsession, it's better for synergy to simply terminate when the connection is lost. synergy will still restart due to other errors. also fixed numerous other minor bugs and cleaned some stuff up (like app error codes are now consistent and enumerated in Version.h, for lack of a better place). and boosted version and protocol numbers.
322 lines
6.3 KiB
C++
322 lines
6.3 KiB
C++
#include "CTCPSocket.h"
|
|
#include "CBufferedInputStream.h"
|
|
#include "CBufferedOutputStream.h"
|
|
#include "CNetworkAddress.h"
|
|
#include "XIO.h"
|
|
#include "XSocket.h"
|
|
#include "CCondVar.h"
|
|
#include "CLock.h"
|
|
#include "CMutex.h"
|
|
#include "CThread.h"
|
|
#include "CStopwatch.h"
|
|
#include "TMethodJob.h"
|
|
|
|
//
|
|
// CTCPSocket
|
|
//
|
|
|
|
CTCPSocket::CTCPSocket()
|
|
{
|
|
m_fd = CNetwork::socket(PF_INET, SOCK_STREAM, 0);
|
|
if (m_fd == CNetwork::Null) {
|
|
throw XSocketCreate();
|
|
}
|
|
init();
|
|
}
|
|
|
|
CTCPSocket::CTCPSocket(CNetwork::Socket fd) :
|
|
m_fd(fd)
|
|
{
|
|
assert(m_fd != CNetwork::Null);
|
|
|
|
init();
|
|
|
|
// socket starts in connected state
|
|
m_connected = kReadWrite;
|
|
|
|
// start handling socket
|
|
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
|
this, &CTCPSocket::ioThread));
|
|
}
|
|
|
|
CTCPSocket::~CTCPSocket()
|
|
{
|
|
try {
|
|
close();
|
|
}
|
|
catch (...) {
|
|
// ignore failures
|
|
}
|
|
|
|
// clean up
|
|
delete m_input;
|
|
delete m_output;
|
|
delete m_mutex;
|
|
}
|
|
|
|
void
|
|
CTCPSocket::bind(const CNetworkAddress& addr)
|
|
{
|
|
if (CNetwork::bind(m_fd, addr.getAddress(),
|
|
addr.getAddressLength()) == CNetwork::Error) {
|
|
if (errno == CNetwork::kEADDRINUSE) {
|
|
throw XSocketAddressInUse();
|
|
}
|
|
throw XSocketBind();
|
|
}
|
|
}
|
|
|
|
void
|
|
CTCPSocket::close()
|
|
{
|
|
// see if buffers should be flushed
|
|
bool doFlush = false;
|
|
{
|
|
CLock lock(m_mutex);
|
|
doFlush = (m_thread != NULL && (m_connected & kWrite) != 0);
|
|
}
|
|
|
|
// flush buffers
|
|
if (doFlush) {
|
|
m_output->flush();
|
|
}
|
|
|
|
// cause ioThread to exit
|
|
{
|
|
CLock lock(m_mutex);
|
|
if (m_fd != CNetwork::Null) {
|
|
CNetwork::shutdown(m_fd, 2);
|
|
m_connected = kClosed;
|
|
}
|
|
}
|
|
|
|
// wait for thread
|
|
if (m_thread != NULL) {
|
|
m_thread->wait();
|
|
delete m_thread;
|
|
m_thread = NULL;
|
|
}
|
|
|
|
// close socket
|
|
if (m_fd != CNetwork::Null) {
|
|
if (CNetwork::close(m_fd) == CNetwork::Error) {
|
|
throw XIOClose();
|
|
}
|
|
m_fd = CNetwork::Null;
|
|
}
|
|
}
|
|
|
|
void
|
|
CTCPSocket::connect(const CNetworkAddress& addr)
|
|
{
|
|
// connect asynchronously so we can check for cancellation
|
|
CNetwork::setblocking(m_fd, false);
|
|
if (CNetwork::connect(m_fd, addr.getAddress(),
|
|
addr.getAddressLength()) == CNetwork::Error) {
|
|
// check for failure
|
|
if (CNetwork::getsockerror() != CNetwork::kECONNECTING) {
|
|
XSocketConnect e;
|
|
CNetwork::setblocking(m_fd, true);
|
|
throw e;
|
|
}
|
|
|
|
// wait for connection or failure
|
|
CNetwork::PollEntry pfds[1];
|
|
pfds[0].fd = m_fd;
|
|
pfds[0].events = CNetwork::kPOLLOUT;
|
|
for (;;) {
|
|
CThread::testCancel();
|
|
const int status = CNetwork::poll(pfds, 1, 10);
|
|
if (status > 0) {
|
|
if ((pfds[0].revents & (CNetwork::kPOLLERR |
|
|
CNetwork::kPOLLNVAL)) != 0) {
|
|
// connection failed
|
|
int error = 0;
|
|
CNetwork::AddressLength size = sizeof(error);
|
|
CNetwork::setblocking(m_fd, true);
|
|
CNetwork::getsockopt(m_fd, SOL_SOCKET, SO_ERROR,
|
|
reinterpret_cast<char*>(&error), &size);
|
|
throw XSocketConnect(error);
|
|
}
|
|
if ((pfds[0].revents & CNetwork::kPOLLOUT) != 0) {
|
|
int error;
|
|
CNetwork::AddressLength size = sizeof(error);
|
|
if (CNetwork::getsockopt(m_fd, SOL_SOCKET, SO_ERROR,
|
|
reinterpret_cast<char*>(&error),
|
|
&size) == CNetwork::Error ||
|
|
error != 0) {
|
|
// connection failed
|
|
CNetwork::setblocking(m_fd, true);
|
|
throw XSocketConnect(error);
|
|
}
|
|
|
|
// connected!
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// back to blocking
|
|
CNetwork::setblocking(m_fd, true);
|
|
|
|
// start servicing the socket
|
|
m_connected = kReadWrite;
|
|
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
|
this, &CTCPSocket::ioThread));
|
|
}
|
|
|
|
IInputStream*
|
|
CTCPSocket::getInputStream()
|
|
{
|
|
return m_input;
|
|
}
|
|
|
|
IOutputStream*
|
|
CTCPSocket::getOutputStream()
|
|
{
|
|
return m_output;
|
|
}
|
|
|
|
void
|
|
CTCPSocket::init()
|
|
{
|
|
m_mutex = new CMutex;
|
|
m_thread = NULL;
|
|
m_connected = kClosed;
|
|
m_input = new CBufferedInputStream(m_mutex,
|
|
new TMethodJob<CTCPSocket>(
|
|
this, &CTCPSocket::closeInput));
|
|
m_output = new CBufferedOutputStream(m_mutex,
|
|
new TMethodJob<CTCPSocket>(
|
|
this, &CTCPSocket::closeOutput));
|
|
|
|
// turn off Nagle algorithm. we send lots of very short messages
|
|
// that should be sent without (much) delay. for example, the
|
|
// mouse motion messages are much less useful if they're delayed.
|
|
CNetwork::TCPNoDelayType flag = 1;
|
|
CNetwork::setsockopt(m_fd, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
|
|
}
|
|
|
|
void
|
|
CTCPSocket::ioThread(void*)
|
|
{
|
|
try {
|
|
ioService();
|
|
ioCleanup();
|
|
}
|
|
catch (...) {
|
|
ioCleanup();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void
|
|
CTCPSocket::ioCleanup()
|
|
{
|
|
try {
|
|
m_input->close();
|
|
}
|
|
catch (...) {
|
|
// ignore
|
|
}
|
|
try {
|
|
m_output->close();
|
|
}
|
|
catch (...) {
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
void
|
|
CTCPSocket::ioService()
|
|
{
|
|
assert(m_fd != CNetwork::Null);
|
|
|
|
// now service the connection
|
|
CNetwork::PollEntry pfds[1];
|
|
pfds[0].fd = m_fd;
|
|
for (;;) {
|
|
{
|
|
// choose events to poll for
|
|
CLock lock(m_mutex);
|
|
pfds[0].events = 0;
|
|
if (m_connected == 0) {
|
|
return;
|
|
}
|
|
if ((m_connected & kRead) != 0) {
|
|
// still open for reading
|
|
pfds[0].events |= CNetwork::kPOLLIN;
|
|
}
|
|
if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) {
|
|
// data queued for writing
|
|
pfds[0].events |= CNetwork::kPOLLOUT;
|
|
}
|
|
}
|
|
|
|
// check for status
|
|
const int status = CNetwork::poll(pfds, 1, 10);
|
|
|
|
// transfer data and handle errors
|
|
if (status == 1) {
|
|
if ((pfds[0].revents & (CNetwork::kPOLLERR |
|
|
CNetwork::kPOLLNVAL)) != 0) {
|
|
// stream is no good anymore so bail
|
|
m_input->hangup();
|
|
return;
|
|
}
|
|
|
|
// read some data
|
|
if (pfds[0].revents & CNetwork::kPOLLIN) {
|
|
UInt8 buffer[4096];
|
|
ssize_t n = CNetwork::read(m_fd, buffer, sizeof(buffer));
|
|
if (n > 0) {
|
|
CLock lock(m_mutex);
|
|
m_input->write(buffer, n);
|
|
}
|
|
else if (n == 0) {
|
|
// stream hungup
|
|
m_input->hangup();
|
|
m_connected &= ~kRead;
|
|
}
|
|
}
|
|
|
|
// write some data
|
|
if (pfds[0].revents & CNetwork::kPOLLOUT) {
|
|
CLock lock(m_mutex);
|
|
|
|
// get amount of data to write
|
|
UInt32 n = m_output->getSize();
|
|
|
|
// write data
|
|
const void* buffer = m_output->peek(n);
|
|
n = (UInt32)CNetwork::write(m_fd, buffer, n);
|
|
|
|
// discard written data
|
|
if (n > 0) {
|
|
m_output->pop(n);
|
|
}
|
|
else if (n == (UInt32)-1 && CNetwork::getsockerror() == EPIPE) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CTCPSocket::closeInput(void*)
|
|
{
|
|
// note -- m_mutex should already be locked
|
|
CNetwork::shutdown(m_fd, 0);
|
|
m_connected &= ~kRead;
|
|
}
|
|
|
|
void
|
|
CTCPSocket::closeOutput(void*)
|
|
{
|
|
// note -- m_mutex should already be locked
|
|
CNetwork::shutdown(m_fd, 1);
|
|
m_connected &= ~kWrite;
|
|
}
|