1 module hunt.io.channel.iocp.AbstractStream;
2 
3 // dfmt off
4 version (HAVE_IOCP) : 
5 // dfmt on
6 
7 import hunt.collection.ByteBuffer;
8 import hunt.collection.BufferUtils;
9 import hunt.event.selector.Selector;
10 import hunt.io.channel.AbstractSocketChannel;
11 import hunt.io.channel.Common;
12 import hunt.io.channel.iocp.Common;
13 import hunt.logging.ConsoleLogger;
14 import hunt.Functions;
15 import hunt.concurrency.thread.Helper;
16 
17 import core.atomic;
18 import core.sys.windows.windows;
19 import core.sys.windows.winsock2;
20 import core.sys.windows.mswsock;
21 
22 import std.format;
23 import std.socket;
24 import core.sync.mutex;
25     import core.stdc.string;
26 
27 /**
28 TCP Peer
29 */
30 abstract class AbstractStream : AbstractSocketChannel {
31 
32     // data event handlers
33     
34     /**
35     * Warning: The received data is stored a inner buffer. For a data safe, 
36     * you would make a copy of it. 
37     */
38     protected DataReceivedHandler dataReceivedHandler;
39     protected SimpleActionHandler dataWriteDoneHandler;
40 
41     protected AddressFamily _family;
42     protected ByteBuffer _bufferForRead;
43 
44     private Mutex _mutex;
45     private bool _Closed  = false;
46 
47     this(Selector loop, AddressFamily family = AddressFamily.INET, size_t bufferSize = 4096 * 2) {
48         super(loop, ChannelType.TCP);
49         // setFlag(ChannelFlag.Read, true);
50         // setFlag(ChannelFlag.Write, true);
51 
52         version (HUNT_IO_DEBUG)
53             trace("Buffer size for read: ", bufferSize);
54         // _readBuffer = new ubyte[bufferSize];
55         _family = family;
56         _bufferForRead = BufferUtils.allocate(bufferSize);
57         _bufferForRead.clear();
58         _readBuffer = cast(ubyte[])_bufferForRead.array();
59         _writeQueue = new WritingBufferQueue();
60         this.socket = new TcpSocket(family);
61 
62         loadWinsockExtension(this.handle);
63         _mutex = new Mutex;
64     }
65 
66     mixin CheckIocpError;
67 
68     override void onRead() {
69         version (HUNT_IO_DEBUG)
70             trace("ready to read");
71         super.onRead();
72     }
73 
74     /**
75      * Should be thread-safe.
76      */
77     override void onWrite() {  
78         version (HUNT_IO_DEBUG)
79             tracef("checking write status, isWritting: %s, writeBuffer: %s", _isWritting, writeBuffer is null);
80 
81         //if(!_isWritting){
82         //    version (HUNT_IO_DEBUG) infof("No data to write out. fd=%d", this.handle);
83         //    return;
84         //}
85 
86         if(isClosing() && isWriteCancelling) {
87             version (HUNT_IO_DEBUG) infof("Write cancelled, fd=%d", this.handle);
88             resetWriteStatus();
89             return;
90         }
91         tryNextBufferWrite();
92     }
93     
94     protected override void onClose() {
95         infof("onClose cancelled");
96         _isWritting = false;
97          _Closed = true;
98         resetWriteStatus();
99         if(this._socket is null) {
100             import core.sys.windows.winsock2;
101             .closesocket(this.handle);
102         } else {
103             // FIXME: Needing refactor or cleanup -@Administrator at 2019/8/9 1:20:27 pm
104             //
105             infof("socket closed: fd=%d ", this.handle);
106             while(!_isSingleWriteBusy)
107             {
108                 this._socket.shutdown(SocketShutdown.BOTH);
109                 this._socket.close();
110             }
111         }
112         super.onClose();
113     }
114 
115     public void stopAction()
116     {
117         _isSingleWriteBusy = true;
118         _Closed = true;
119         _endFristRead = false;
120     }
121 
122     public void beginRead() {
123         //synchronized (_mutex){
124 
125             // https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-wsarecv
126             //if (this._socket !is null)
127             //{
128             try
129             {
130                 // synchronized (_mutex)
131                 // {
132                 if (!_Closed )
133                 {
134                     _isSingleWriteBusy = true;
135 
136                     WSABUF _dataReadBuffer;
137                     _dataReadBuffer.len = cast(uint) _readBuffer.length;
138                     _dataReadBuffer.buf = cast(char*) _readBuffer.ptr;
139                     _iocpread.channel = this;
140                     _iocpread.operation = IocpOperation.read;
141                     DWORD dwReceived = 0;
142                     DWORD dwFlags = 0;
143                     memset(&_iocpread.overlapped , 0 ,_iocpread.overlapped.sizeof );
144                     version (HUNT_IO_DEBUG)
145                         tracef("start receiving [fd=%d] ", this.socket.handle);
146                    // _isSingleWriteBusy = true;
147                     int nRet = WSARecv(cast(SOCKET) this.socket.handle, &_dataReadBuffer, 1u, &dwReceived, &dwFlags,
148                     &_iocpread.overlapped, cast(LPWSAOVERLAPPED_COMPLETION_ROUTINE) null);
149                     if (nRet == SOCKET_ERROR)
150                     {
151                         _isSingleWriteBusy = false;
152                         tracef("WSARecv close");
153                         close();
154                     }
155                     //checkErro(nRet, SOCKET_ERROR);
156                 }
157                 // }
158             } catch (Exception e)
159             {
160 
161             }
162        // }
163 
164 
165         //}
166     }
167 
168     protected bool doConnect(Address addr) {
169         try {
170             Address binded = createAddress(this.socket.addressFamily);
171             this.socket.bind(binded);
172             _iocpread.channel = this;
173             _iocpread.operation = IocpOperation.connect;
174             int nRet = ConnectEx(cast(SOCKET) this.socket.handle(),
175             cast(SOCKADDR*) addr.name(), addr.nameLen(), null, 0, null,
176             &_iocpread.overlapped);
177             checkErro(nRet, SOCKET_ERROR);
178 
179         } catch (SocketOSException e)
180         {
181             return false;
182         }
183         return true;
184     }
185 
186     private uint doWrite(const(ubyte)[] data) {
187        // synchronized (_mutex){
188             DWORD dwSent = 0;//cast(DWORD)data.length;
189             DWORD dwFlags = 0;
190 
191             tracef("is busy ------- ----------------------------------------   %d",_isSingleWriteBusy);
192 
193             //scope(exit) {
194             //    _isSingleWriteBusy = false;
195             //}
196             try {
197                 // synchronized (_mutex){
198 
199 
200                 //if (this.socket.handle() == INVALID_SOCKET)
201                 //{
202                 //    tracef("nonononono");
203                 //}
204                 if ( !_Closed)
205                 {
206                     _iocpread.channel = this;
207                     _iocpread.operation = IocpOperation.write;
208                     memset(&_iocpread.overlapped , 0 ,_iocpread.overlapped.sizeof );
209                     // tracef("To write %d bytes, fd=%d", data.length, this.socket.handle());
210                     version (HUNT_IO_DEBUG) {
211                         size_t bufferLength = data.length;
212                         tracef("To write %d bytes", bufferLength);
213                         if (bufferLength > 32)
214                             tracef("%(%02X %) ...", data[0 .. 32]);
215                         else
216                             tracef("%s", data);
217                     }
218                     // size_t bufferLength = data.length;
219                     //     tracef("To write %d bytes", bufferLength);
220                     //     tracef("%s", data);
221                     WSABUF _dataWriteBuffer;
222 
223                     //char[] bf = new char[data.length];
224                     //memcpy(bf.ptr,data.ptr,data.length);
225                     //_dataWriteBuffer.buf =  bf.ptr;
226                     _dataWriteBuffer.buf = cast(char*) data.ptr;
227                     _dataWriteBuffer.len = cast(uint) data.length;
228                     tracef("To write %s bytes %d", _dataWriteBuffer.buf , _dataWriteBuffer.len);
229                     _isSingleWriteBusy = true;
230                     int nRet = WSASend( cast(SOCKET) this.socket.handle(), &_dataWriteBuffer, 1, &dwSent,
231                     dwFlags, &_iocpread.overlapped, cast(LPWSAOVERLAPPED_COMPLETION_ROUTINE) null);
232                     if (nRet != NO_ERROR)
233                     {
234                         _isSingleWriteBusy = false;
235                         tracef("WSASend close");
236                         close();
237                     }
238 
239                     checkErro( nRet, SOCKET_ERROR);
240                 }
241                 //}
242             } catch(Exception e)
243             {
244                 _isSingleWriteBusy = false;
245                 return 0;
246             }
247 
248 
249             // FIXME: Needing refactor or cleanup -@Administrator at 2019/8/9 12:18:20 pm
250             // Keep this to prevent the buffer corrupted. Why?
251             version (HUNT_IO_DEBUG) {
252                 tracef("sent: %d / %d bytes, fd=%d", dwSent, bufferLength, this.handle);
253             }
254 
255 
256 
257             if (this.isError) {
258                 errorf("Socket error on write: fd=%d, message=%s", this.handle, this.errorMessage);
259                 this.close();
260             }
261 
262             return dwSent;
263        // }
264 
265     }
266 
267     protected void doRead() {
268         //_isSingleWriteBusy = false;
269         this.clearError();
270         version (HUNT_IO_DEBUG)
271             tracef("start reading: %d nbytes", this.readLen);
272 
273         if (readLen > 0) {
274             // import std.stdio;
275             // writefln("length=%d, data: %(%02X %)", readLen, _readBuffer[0 .. readLen]);
276             version (HUNT_IO_DEBUG)
277                 tracef("reading done: %d nbytes", readLen);
278 
279             auto ss = this.socket;
280             if (dataReceivedHandler !is null){
281             //if (dataReceivedHandler !is null && !_Closed && this.socket.handle() != INVALID_SOCKET && this.socket.handle() != -1 && this.socket.handle() != 0) {
282                 _bufferForRead.limit(cast(int)readLen);
283                 _bufferForRead.position(0);
284                 dataReceivedHandler(_bufferForRead);
285             }
286 
287             this.beginRead();
288             //if (_endFristRead)
289             //{
290             //
291             //}
292             // continue reading
293 
294         } else if (readLen == 0) {
295             version (HUNT_IO_DEBUG) {
296                 if (_remoteAddress !is null)
297                     warningf("connection broken: %s", _remoteAddress.toString());
298             }
299             onDisconnected();
300             // if (_isClosed)
301             //     this.close();
302         } else {
303             version (HUNT_IO_DEBUG) {
304                 warningf("undefined behavior on thread %d", getTid());
305             } else {
306                 this._error = true;
307                 this._errorMessage = "undefined behavior on thread";
308             }
309         }
310     }
311 
312     // try to write a block of data directly
313     protected size_t tryWrite(const ubyte[] data) {        
314         version (HUNT_IO_DEBUG)
315             tracef("start to write, total=%d bytes, fd=%d", data.length, this.handle);
316         clearError();
317         size_t nBytes;
318         //scope(exit) {
319         //    _isSingleWriteBusy = false;
320         //}
321         if (!_isSingleWriteBusy)
322         {
323              nBytes = doWrite(data);
324         }
325 
326         return nBytes;
327     }
328 
329     // try to write a block of data from the write queue
330     private void tryNextBufferWrite() {
331         if(checkAllWriteDone()){
332             _isSingleWriteBusy = false;
333             return;
334         } 
335         
336         // keep thread-safe here
337         //if(!cas(&_isSingleWriteBusy, false, true)) {
338         //    version (HUNT_IO_DEBUG) warningf("busy writing. fd=%d", this.handle);
339         //    return;
340         //}
341 
342         //scope(exit) {
343         //    _isSingleWriteBusy = false;
344         //}
345 
346         clearError();
347 
348         bool haveBuffer = _writeQueue.tryDequeue(writeBuffer);
349         if(haveBuffer) {
350             writeBufferRemaining();
351         } else {
352             version (HUNT_IO_DEBUG)
353                 warning("No buffer in queue");
354         }
355     }
356 
357     private void writeBufferRemaining() {
358         if (writeBuffer is null ||_isSingleWriteBusy)
359         {
360             return;
361         }
362         const(ubyte)[] data = cast(const(ubyte)[])writeBuffer.getRemaining();
363 
364         size_t nBytes = doWrite(data);
365 
366         version (HUNT_IO_DEBUG)
367             tracef("written data: %d bytes, fd=%d", nBytes, this.handle);
368         if(nBytes == data.length) {
369             writeBuffer = null;
370         } else if (nBytes > 0) { 
371             writeBuffer.nextGetIndex(cast(int)nBytes);
372             version (HUNT_IO_DEBUG)
373                 warningf("remaining data: %d / %d, fd=%d", data.length - nBytes, data.length, this.handle);
374         } else { 
375             version (HUNT_IO_DEBUG)
376             warningf("I/O busy: writing. fd=%d", this.handle);
377         }   
378     }
379     
380     protected bool checkAllWriteDone() {
381         if(_writeQueue.isEmpty() && writeBuffer is null) {
382             resetWriteStatus();        
383             version (HUNT_IO_DEBUG)
384                 tracef("All data are written out. fd=%d", this.handle);
385             if(dataWriteDoneHandler !is null)
386                 dataWriteDoneHandler(this);
387             return true;
388         }
389 
390         return false;
391     }
392     
393     void resetWriteStatus() {
394         _writeQueue.clear();
395         _isWritting = false;
396         isWriteCancelling = false;
397         sendDataBuffer = null;
398         sendDataBackupBuffer = null;
399         writeBuffer = null;
400         _isSingleWriteBusy = true;
401         _endFristRead = false;
402     }
403 
404     /**
405      * Called by selector after data sent
406      * Note: It's only for IOCP selector: 
407     */
408     void onWriteDone(size_t nBytes) {
409         version (HUNT_IO_DEBUG) {
410             tracef("write done once: %d bytes, isWritting: %s, writeBuffer: %s, fd=%d",
411                  nBytes, _isWritting, writeBuffer is null, this.handle);
412         }
413         //if (isWriteCancelling) {
414         //    version (HUNT_IO_DEBUG) tracef("write cancelled.");
415         //    resetWriteStatus();
416         //    return;
417         //}
418 
419 
420         //while(_isSingleWriteBusy) {
421         //    version(HUNT_IO_DEBUG)
422         //    info("waiting for last writting get finished...");
423         //}
424 
425         version (HUNT_IO_DEBUG) {
426             tracef("write done once: %d bytes, isWritting: %s, writeBuffer: %s, fd=%d",
427                  nBytes, _isWritting, writeBuffer is null, this.handle);
428         }
429 
430         if (writeBuffer !is null && writeBuffer.hasRemaining()) {
431             version (HUNT_IO_DEBUG) tracef("try to write the remaining in buffer.");
432             writeBufferRemaining();
433         }  else {
434             version (HUNT_IO_DEBUG) tracef("try to write next buffer.");
435             tryNextBufferWrite();
436         }
437     }
438 
439     private void notifyDataWrittenDone() {
440         if(dataWriteDoneHandler !is null && _writeQueue.isEmpty()) {
441             dataWriteDoneHandler(this);
442         }
443     }
444 
445     void cancelWrite() {
446         isWriteCancelling = true;
447     }
448 
449     void setFristRead(bool read)
450     {
451         _endFristRead = read;
452     }
453 
454     bool getFristRead()
455     {
456         return _endFristRead;
457     }
458 
459     void setBusyWrite( bool write)
460     {
461         _isSingleWriteBusy = write;
462     }
463 
464     abstract bool isConnected() nothrow;
465     abstract protected void onDisconnected();
466 
467     // protected void initializeWriteQueue() {
468     //     if (_writeQueue is null) {
469     //         _writeQueue = new WritingBufferQueue();
470     //     }
471     // }
472 
473     SimpleEventHandler disconnectionHandler;
474     
475     protected WritingBufferQueue _writeQueue;
476     protected bool isWriteCancelling = false;
477     private  bool _isSingleWriteBusy = false; // keep a single I/O write operation atomic
478     private  bool _endFristRead = false;
479     private const(ubyte)[] _readBuffer;
480     private const(ubyte)[] sendDataBuffer;
481     private const(ubyte)[] sendDataBackupBuffer;
482     private ByteBuffer writeBuffer; 
483 
484     private IocpContext _iocpread;
485     //private IocpContext _iocpwrite;
486 }