blob: e65133dac83c327b42c95fbfa37bfe51f6fd19a5 [file] [log] [blame]
Constantin Kaplinskyb0f89f82005-09-28 10:37:29 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00003 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19#include <stdio.h>
20#include <string.h>
21#ifdef _WIN32
22#include <winsock2.h>
23#define write(s,b,l) send(s,(const char*)b,l,0)
24#define EWOULDBLOCK WSAEWOULDBLOCK
25#undef errno
26#define errno WSAGetLastError()
27#undef EINTR
28#define EINTR WSAEINTR
29#else
30#include <sys/types.h>
31#include <errno.h>
32#include <unistd.h>
33#include <sys/time.h>
34#endif
35
36#include <rdr/FdOutStream.h>
37#include <rdr/Exception.h>
38
39
40using namespace rdr;
41
Constantin Kaplinsky384b18b2005-09-13 18:34:13 +000042enum { DEFAULT_BUF_SIZE = 16384 };
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000043
44FdOutStream::FdOutStream(int fd_, int timeoutms_, int bufSize_)
45 : fd(fd_), timeoutms(timeoutms_),
46 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
47{
48 ptr = start = new U8[bufSize];
49 end = start + bufSize;
50}
51
52FdOutStream::~FdOutStream()
53{
54 try {
55 flush();
56 } catch (Exception&) {
57 }
58 delete [] start;
59}
60
61void FdOutStream::setTimeout(int timeoutms_) {
62 timeoutms = timeoutms_;
63}
64
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000065int FdOutStream::length()
66{
67 return offset + ptr - start;
68}
69
70void FdOutStream::flush()
71{
72 U8* sentUpTo = start;
73 while (sentUpTo < ptr) {
74 int n = writeWithTimeout((const void*) sentUpTo, ptr - sentUpTo);
75 sentUpTo += n;
76 offset += n;
77 }
78
79 ptr = start;
80}
81
82
83int FdOutStream::overrun(int itemSize, int nItems)
84{
85 if (itemSize > bufSize)
86 throw Exception("FdOutStream overrun: max itemSize exceeded");
87
88 flush();
89
90 if (itemSize * nItems > end - ptr)
91 nItems = (end - ptr) / itemSize;
92
93 return nItems;
94}
95
96//
97// writeWithTimeout() writes up to the given length in bytes from the given
98// buffer to the file descriptor. If there is a timeout set and that timeout
99// expires, it throws a TimedOut exception. Otherwise it returns the number of
100// bytes written. It never attempts to write() unless select() indicates that
101// the fd is writable - this means it can be used on an fd which has been set
102// non-blocking. It also has to cope with the annoying possibility of both
103// select() and write() returning EINTR.
104//
105
106int FdOutStream::writeWithTimeout(const void* data, int length)
107{
108 int n;
109
110 do {
111
112 do {
113 fd_set fds;
114 struct timeval tv;
115 struct timeval* tvp = &tv;
116
117 if (timeoutms != -1) {
118 tv.tv_sec = timeoutms / 1000;
119 tv.tv_usec = (timeoutms % 1000) * 1000;
120 } else {
121 tvp = 0;
122 }
123
124 FD_ZERO(&fds);
125 FD_SET(fd, &fds);
126#ifdef _WIN32_WCE
127 // NB: This fixes a broken Winsock2 select() behaviour. select()
128 // never returns for non-blocking sockets, unless they're already
129 // ready to be written to...
130 u_long zero = 0; ioctlsocket(fd, FIONBIO, &zero);
131#endif
132 n = select(fd+1, 0, &fds, 0, tvp);
133#ifdef _WIN32_WCE
134 u_long one = 0; ioctlsocket(fd, FIONBIO, &one);
135#endif
136 } while (n < 0 && errno == EINTR);
137
138 if (n < 0) throw SystemException("select",errno);
139
140 if (n == 0) throw TimedOut();
141
142 do {
143 n = ::write(fd, data, length);
144 } while (n < 0 && (errno == EINTR));
145
146 // NB: This outer loop simply fixes a broken Winsock2 EWOULDBLOCK
147 // condition, found only under Win98 (first edition), with slow
148 // network connections. Should in fact never ever happen...
149 } while (n < 0 && (errno == EWOULDBLOCK));
150
151 if (n < 0) throw SystemException("write",errno);
152
153 return n;
154}