1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#ifndef SkNetIO_DEFINED
8#define SkNetIO_DEFINED
9
10#include <netinet/in.h>
11#include <sys/socket.h>
12#include "SkTypes.h"
13#include "SkStream.h"
14
15/* PACKET and HEADER Format */
16#define PACKET_SIZE 1024
17#define HEADER_SIZE 20
18#define CONTENT_SIZE 1004
19
20#define DEFAULT_PORT 15555
21#define MAX_WAITING_CLIENTS 3
22#define NONBLOCKING_SOCKETS
23
24class SkSocket {
25public:
26    SkSocket();
27    virtual ~SkSocket();
28
29    enum State {
30        kError_state,
31        kBegin_state,
32        kIncomplete_state,
33        kDone_state
34    };
35
36    enum DataType {
37        kPipeAppend_type,
38        kPipeReplace_type,
39        kString_type,
40        kInt_type
41    };
42
43    bool isConnected() { return fConnected; }
44    /**
45     * Write data to the socket. Data is a pointer to the beginning of the data
46     * to be sent and dataSize specifies the number of bytes to send. This
47     * method will spread the data across multiple packets if the data can't all
48     * fit in a single packet. The method will write all the data to each of the
49     * socket's open connections until all the bytes have been successfully sent
50     * and return total the number of bytes written to all clients, unless there
51     * was an error during the transfer, in which case the method returns -1.
52     * For blocking sockets, write will block indefinitely if the socket at the
53     * other end of the connection doesn't receive any data.
54     * NOTE: This method guarantees that all of the data will be sent unless
55     * there was an error, so it may block temporarily when the write buffer is
56     * full
57     */
58    int writePacket(void* data, size_t size, DataType type = kPipeAppend_type);
59
60    /**
61     * Read a logical packet from socket. The data read will be stored
62     * sequentially in the dataArray. This method will keep running until all
63     * the data in a logical chunk has been read (assembling multiple partial
64     * packets if necessary) and return the number of bytes successfully read,
65     * unless there was an error, in which case the method returns -1. \For
66     * nonblocking sockets, read will return 0 if there's nothing to read. For
67     * blocking sockets, read will block indefinitely if the socket doesn't
68     * receive any data.
69     * NOTE: This method guarantees that all the data in a logical packet will
70     * be read so it may block temporarily if it's waiting for parts of a
71     * packet
72     */
73    int readPacket(void (*onRead)(int cid, const void* data, size_t size,
74                                  DataType type, void*), void* context);
75
76    /**
77     * Suspend network transfers until resume() is called. Leaves all
78     * connections in tact.
79     */
80    void suspendAll() { fReadSuspended = fWriteSuspended = true; }
81    /**
82     * Resume all network transfers.
83     */
84    void resumeAll() { fReadSuspended = fWriteSuspended = false; }
85    /**
86     * Other helper functions
87     */
88    void suspendRead() { fReadSuspended = true; }
89    void resumeRead() { fReadSuspended = false; }
90    void suspendWrite()  { fWriteSuspended = true; }
91    void resumeWrite()  { fWriteSuspended = false; }
92
93protected:
94    struct header {
95        bool        done;
96        int         bytes;
97        DataType    type;
98    };
99
100    /**
101     * Create a socket and return its file descriptor. Returns -1 on failure
102     */
103    int createSocket();
104
105    /**
106     * Close the socket specified by the socket file descriptor argument. Will
107     * update fMaxfd and working set properly
108     */
109    void closeSocket(int sockfd);
110
111    /**
112     * Called when a broken or terminated connection has been detected. Closes
113     * the socket file descriptor and removes it from the master set by default.
114     * Override to handle broken connections differently
115     */
116    virtual void onFailedConnection(int sockfd);
117
118    /**
119     * Set the socket specified by the socket file descriptor as nonblocking
120     */
121    void setNonBlocking(int sockfd);
122
123    /**
124     * Add the socket specified by the socket file descriptor to the master
125     * file descriptor set, which is used to in the select() to detect new data
126     * or connections
127     */
128    void addToMasterSet(int sockfd);
129
130    bool    fConnected;
131    bool    fReady;
132    bool    fReadSuspended;
133    bool    fWriteSuspended;
134    int     fMaxfd;
135    int     fPort;
136    int     fSockfd;
137
138    /**
139     * fMasterSet contains all the file descriptors to be used for read/write.
140     * For clients, this only contains the client socket. For servers, this
141     * contains all the file descriptors associated with established connections
142     * to clients
143     */
144    fd_set  fMasterSet;
145};
146
147/*
148 * TCP server. Can accept simultaneous connections to multiple SkTCPClients and
149 * read/write data back and forth using read/writePacket calls. Port number can
150 * be specified, but make sure that client/server use the same port
151 */
152class SkTCPServer : public SkSocket {
153public:
154    SkTCPServer(int port = DEFAULT_PORT);
155    virtual ~SkTCPServer();
156
157    /**
158     * Accept any incoming connections to the server, will accept 1 connection
159     * at a time. Returns -1 on error. For blocking sockets, this method will
160     * block until a client calls connectToServer()
161     */
162    int acceptConnections();
163
164    /**
165     * Disconnect all connections to clients. Returns -1 on error
166     */
167    int disconnectAll();
168private:
169    typedef SkSocket INHERITED;
170};
171
172/*
173 * TCP client. Will connect to the server specified in the constructor. If a
174 * port number is specified, make sure that it's the same as the port number on
175 * the server
176 */
177class SkTCPClient : public SkSocket {
178public:
179    SkTCPClient(const char* hostname, int port = DEFAULT_PORT);
180
181    /**
182     * Connect to server. Returns -1 on error or failure. Call this to connect
183     * or reconnect to the server. For blocking sockets, this method will block
184     * until the connection is accepted by the server.
185     */
186    int connectToServer();
187protected:
188    /**
189     * Client needs to recreate the socket when a connection is broken because
190     * connect can only be called successfully once.
191     */
192    virtual void onFailedConnection(int sockfd);
193private:
194    sockaddr_in fServerAddr;
195    typedef SkSocket INHERITED;
196};
197
198#endif
199