blob: 6795fc891967ba7220f38aa9c7aee48b5538a565 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
2 *
3 * 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
42enum { DEFAULT_BUF_SIZE = 16384,
43 MIN_BULK_SIZE = 1024 };
44
45FdOutStream::FdOutStream(int fd_, int timeoutms_, int bufSize_)
46 : fd(fd_), timeoutms(timeoutms_),
47 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
48{
49 ptr = start = new U8[bufSize];
50 end = start + bufSize;
51}
52
53FdOutStream::~FdOutStream()
54{
55 try {
56 flush();
57 } catch (Exception&) {
58 }
59 delete [] start;
60}
61
62void FdOutStream::setTimeout(int timeoutms_) {
63 timeoutms = timeoutms_;
64}
65
66void FdOutStream::writeBytes(const void* data, int length)
67{
68 if (length < MIN_BULK_SIZE) {
69 OutStream::writeBytes(data, length);
70 return;
71 }
72
73 const U8* dataPtr = (const U8*)data;
74
75 flush();
76
77 while (length > 0) {
78 int n = writeWithTimeout(dataPtr, length);
79 length -= n;
80 dataPtr += n;
81 offset += n;
82 }
83}
84
85int FdOutStream::length()
86{
87 return offset + ptr - start;
88}
89
90void FdOutStream::flush()
91{
92 U8* sentUpTo = start;
93 while (sentUpTo < ptr) {
94 int n = writeWithTimeout((const void*) sentUpTo, ptr - sentUpTo);
95 sentUpTo += n;
96 offset += n;
97 }
98
99 ptr = start;
100}
101
102
103int FdOutStream::overrun(int itemSize, int nItems)
104{
105 if (itemSize > bufSize)
106 throw Exception("FdOutStream overrun: max itemSize exceeded");
107
108 flush();
109
110 if (itemSize * nItems > end - ptr)
111 nItems = (end - ptr) / itemSize;
112
113 return nItems;
114}
115
116//
117// writeWithTimeout() writes up to the given length in bytes from the given
118// buffer to the file descriptor. If there is a timeout set and that timeout
119// expires, it throws a TimedOut exception. Otherwise it returns the number of
120// bytes written. It never attempts to write() unless select() indicates that
121// the fd is writable - this means it can be used on an fd which has been set
122// non-blocking. It also has to cope with the annoying possibility of both
123// select() and write() returning EINTR.
124//
125
126int FdOutStream::writeWithTimeout(const void* data, int length)
127{
128 int n;
129
130 do {
131
132 do {
133 fd_set fds;
134 struct timeval tv;
135 struct timeval* tvp = &tv;
136
137 if (timeoutms != -1) {
138 tv.tv_sec = timeoutms / 1000;
139 tv.tv_usec = (timeoutms % 1000) * 1000;
140 } else {
141 tvp = 0;
142 }
143
144 FD_ZERO(&fds);
145 FD_SET(fd, &fds);
146#ifdef _WIN32_WCE
147 // NB: This fixes a broken Winsock2 select() behaviour. select()
148 // never returns for non-blocking sockets, unless they're already
149 // ready to be written to...
150 u_long zero = 0; ioctlsocket(fd, FIONBIO, &zero);
151#endif
152 n = select(fd+1, 0, &fds, 0, tvp);
153#ifdef _WIN32_WCE
154 u_long one = 0; ioctlsocket(fd, FIONBIO, &one);
155#endif
156 } while (n < 0 && errno == EINTR);
157
158 if (n < 0) throw SystemException("select",errno);
159
160 if (n == 0) throw TimedOut();
161
162 do {
163 n = ::write(fd, data, length);
164 } while (n < 0 && (errno == EINTR));
165
166 // NB: This outer loop simply fixes a broken Winsock2 EWOULDBLOCK
167 // condition, found only under Win98 (first edition), with slow
168 // network connections. Should in fact never ever happen...
169 } while (n < 0 && (errno == EWOULDBLOCK));
170
171 if (n < 0) throw SystemException("write",errno);
172
173 return n;
174}