/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkNetIO_DEFINED
#define SkNetIO_DEFINED
#include <netinet/in.h>
#include <sys/socket.h>
#include "SkTypes.h"
#include "SkStream.h"
/* PACKET and HEADER Format */
#define PACKET_SIZE 1024
#define HEADER_SIZE 20
#define CONTENT_SIZE 1004
#define DEFAULT_PORT 15555
#define MAX_WAITING_CLIENTS 3
#define NONBLOCKING_SOCKETS
class SkSocket {
public:
SkSocket();
virtual ~SkSocket();
enum State {
kError_state,
kBegin_state,
kIncomplete_state,
kDone_state
};
enum DataType {
kPipeAppend_type,
kPipeReplace_type,
kString_type,
kInt_type
};
bool isConnected() { return fConnected; }
/**
* Write data to the socket. Data is a pointer to the beginning of the data
* to be sent and dataSize specifies the number of bytes to send. This
* method will spread the data across multiple packets if the data can't all
* fit in a single packet. The method will write all the data to each of the
* socket's open connections until all the bytes have been successfully sent
* and return total the number of bytes written to all clients, unless there
* was an error during the transfer, in which case the method returns -1.
* For blocking sockets, write will block indefinitely if the socket at the
* other end of the connection doesn't receive any data.
* NOTE: This method guarantees that all of the data will be sent unless
* there was an error, so it may block temporarily when the write buffer is
* full
*/
int writePacket(void* data, size_t size, DataType type = kPipeAppend_type);
/**
* Read a logical packet from socket. The data read will be stored
* sequentially in the dataArray. This method will keep running until all
* the data in a logical chunk has been read (assembling multiple partial
* packets if necessary) and return the number of bytes successfully read,
* unless there was an error, in which case the method returns -1. \For
* nonblocking sockets, read will return 0 if there's nothing to read. For
* blocking sockets, read will block indefinitely if the socket doesn't
* receive any data.
* NOTE: This method guarantees that all the data in a logical packet will
* be read so it may block temporarily if it's waiting for parts of a
* packet
*/
int readPacket(void (*onRead)(int cid, const void* data, size_t size,
DataType type, void*), void* context);
/**
* Suspend network transfers until resume() is called. Leaves all
* connections in tact.
*/
void suspendAll() { fReadSuspended = fWriteSuspended = true; }
/**
* Resume all network transfers.
*/
void resumeAll() { fReadSuspended = fWriteSuspended = false; }
/**
* Other helper functions
*/
void suspendRead() { fReadSuspended = true; }
void resumeRead() { fReadSuspended = false; }
void suspendWrite() { fWriteSuspended = true; }
void resumeWrite() { fWriteSuspended = false; }
protected:
struct header {
bool done;
int bytes;
DataType type;
};
/**
* Create a socket and return its file descriptor. Returns -1 on failure
*/
int createSocket();
/**
* Close the socket specified by the socket file descriptor argument. Will
* update fMaxfd and working set properly
*/
void closeSocket(int sockfd);
/**
* Called when a broken or terminated connection has been detected. Closes
* the socket file descriptor and removes it from the master set by default.
* Override to handle broken connections differently
*/
virtual void onFailedConnection(int sockfd);
/**
* Set the socket specified by the socket file descriptor as nonblocking
*/
void setNonBlocking(int sockfd);
/**
* Add the socket specified by the socket file descriptor to the master
* file descriptor set, which is used to in the select() to detect new data
* or connections
*/
void addToMasterSet(int sockfd);
bool fConnected;
bool fReady;
bool fReadSuspended;
bool fWriteSuspended;
int fMaxfd;
int fPort;
int fSockfd;
/**
* fMasterSet contains all the file descriptors to be used for read/write.
* For clients, this only contains the client socket. For servers, this
* contains all the file descriptors associated with established connections
* to clients
*/
fd_set fMasterSet;
};
/*
* TCP server. Can accept simultaneous connections to multiple SkTCPClients and
* read/write data back and forth using read/writePacket calls. Port number can
* be specified, but make sure that client/server use the same port
*/
class SkTCPServer : public SkSocket {
public:
SkTCPServer(int port = DEFAULT_PORT);
virtual ~SkTCPServer();
/**
* Accept any incoming connections to the server, will accept 1 connection
* at a time. Returns -1 on error. For blocking sockets, this method will
* block until a client calls connectToServer()
*/
int acceptConnections();
/**
* Disconnect all connections to clients. Returns -1 on error
*/
int disconnectAll();
private:
typedef SkSocket INHERITED;
};
/*
* TCP client. Will connect to the server specified in the constructor. If a
* port number is specified, make sure that it's the same as the port number on
* the server
*/
class SkTCPClient : public SkSocket {
public:
SkTCPClient(const char* hostname, int port = DEFAULT_PORT);
/**
* Connect to server. Returns -1 on error or failure. Call this to connect
* or reconnect to the server. For blocking sockets, this method will block
* until the connection is accepted by the server.
*/
int connectToServer();
protected:
/**
* Client needs to recreate the socket when a connection is broken because
* connect can only be called successfully once.
*/
virtual void onFailedConnection(int sockfd);
private:
sockaddr_in fServerAddr;
typedef SkSocket INHERITED;
};
#endif