Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -1394,14 +1394,16 @@ AC_CHECK_HEADER(netinet/tcp.h, [ AC_DEFINE(OF_HAVE_NETINET_TCP_H, 1, [Whether we have netinet/tcp.h]) ]) AC_CHECK_HEADER(netinet/sctp.h, [ - AC_DEFINE(OF_HAVE_SCTP, 1, [Whether we have SCTP]) - AC_DEFINE(OF_HAVE_NETINET_SCTP_H, 1, - [Whether we have netinet/sctp.h]) - AC_SUBST(USE_SRCS_SCTP, '${SRCS_SCTP}') + AC_SEARCH_LIBS(sctp_recvv, sctp, [ + AC_DEFINE(OF_HAVE_SCTP, 1, [Whether we have SCTP]) + AC_DEFINE(OF_HAVE_NETINET_SCTP_H, 1, + [Whether we have netinet/sctp.h]) + AC_SUBST(USE_SRCS_SCTP, '${SRCS_SCTP}') + ]) ]) AC_CHECK_HEADERS([arpa/inet.h netdb.h sys/sockio.h]) AC_CHECK_HEADERS([net/if.h], [], [], [ #ifdef OF_HAVE_SYS_SOCKET_H # include Index: src/OFRunLoop+Private.h ================================================================== --- src/OFRunLoop+Private.h +++ src/OFRunLoop+Private.h @@ -21,10 +21,13 @@ #import "OFStream.h" #ifdef OF_HAVE_SOCKETS # import "OFDatagramSocket.h" # import "OFSequencedPacketSocket.h" # import "OFStreamSocket.h" +# ifdef OF_HAVE_SCTP +# import "OFSCTPSocket.h" +# endif #endif OF_ASSUME_NONNULL_BEGIN #ifdef OF_HAVE_SOCKETS @@ -96,11 +99,11 @@ length: (size_t)length mode: (OFRunLoopMode)mode # ifdef OF_HAVE_BLOCKS block: (nullable OFDatagramSocketAsyncReceiveBlock)block # endif - delegate: (nullable id ) delegate; + delegate: (nullable id )delegate; + (void)of_addAsyncSendForDatagramSocket: (OFDatagramSocket *)socket data: (OFData *)data receiver: (const OFSocketAddress *)receiver mode: (OFRunLoopMode)mode # ifdef OF_HAVE_BLOCKS @@ -113,20 +116,39 @@ length: (size_t)length mode: (OFRunLoopMode)mode # ifdef OF_HAVE_BLOCKS block: (nullable OFSequencedPacketSocketAsyncReceiveBlock)block # endif - delegate: (nullable id ) delegate; + delegate: (nullable id )delegate; + (void)of_addAsyncSendForSequencedPacketSocket: (OFSequencedPacketSocket *)socket data: (OFData *)data mode: (OFRunLoopMode)mode # ifdef OF_HAVE_BLOCKS block: (nullable OFSequencedPacketSocketAsyncSendDataBlock)block # endif delegate: (nullable id )delegate; +# ifdef OF_HAVE_SCTP ++ (void)of_addAsyncReceiveForSCTPSocket: (OFSCTPSocket *)socket + buffer: (void *)buffer + length: (size_t)length + mode: (OFRunLoopMode)mode +# ifdef OF_HAVE_BLOCKS + block: (nullable OFSCTPSocketAsyncReceiveBlock)block +# endif + delegate: (nullable id )delegate; ++ (void)of_addAsyncSendForSCTPSocket: (OFSCTPSocket *)socket + data: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + mode: (OFRunLoopMode)mode +# ifdef OF_HAVE_BLOCKS + block: (nullable OFSCTPSocketAsyncSendDataBlock)block +# endif + delegate: (nullable id )delegate; +# endif + (void)of_cancelAsyncRequestsForObject: (id)object mode: (OFRunLoopMode)mode; #endif - (void)of_removeTimer: (OFTimer *)timer forMode: (OFRunLoopMode)mode; @end OF_ASSUME_NONNULL_END Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -200,10 +200,35 @@ OFSequencedPacketSocketAsyncSendDataBlock _block; # endif OFData *_data; } @end + +# ifdef OF_HAVE_SCTP +@interface OFRunLoopSCTPReceiveQueueItem: OFRunLoopQueueItem +{ +@public +# ifdef OF_HAVE_BLOCKS + OFSCTPSocketAsyncReceiveBlock _block; +# endif + void *_buffer; + size_t _length; +} +@end + +@interface OFRunLoopSCTPSendQueueItem: OFRunLoopQueueItem +{ +@public +# ifdef OF_HAVE_BLOCKS + OFSCTPSocketAsyncSendDataBlock _block; +# endif + OFData *_data; + uint16_t _streamID; + uint32_t _PPID; +} +@end +# endif #endif @implementation OFRunLoopState - (instancetype)init { @@ -1006,10 +1031,123 @@ # endif [super dealloc]; } @end + +# ifdef OF_HAVE_SCTP +@implementation OFRunLoopSCTPReceiveQueueItem +- (bool)handleObject: (id)object +{ + size_t length; + uint16_t streamID; + uint32_t PPID; + id exception = nil; + + @try { + length = [object receiveIntoBuffer: _buffer + length: _length + streamID: &streamID + PPID: &PPID]; + } @catch (id e) { + length = 0; + exception = e; + } + +# ifdef OF_HAVE_BLOCKS + if (_block != NULL) + return _block(length, streamID, PPID, exception); + else { +# endif + if (![_delegate respondsToSelector: @selector(socket: + didReceiveIntoBuffer:length:streamID:PPID:exception:)]) + return false; + + return [_delegate socket: object + didReceiveIntoBuffer: _buffer + length: length + streamID: streamID + PPID: PPID + exception: exception]; +# ifdef OF_HAVE_BLOCKS + } +# endif +} + +# ifdef OF_HAVE_BLOCKS +- (void)dealloc +{ + [_block release]; + + [super dealloc]; +} +# endif +@end + +@implementation OFRunLoopSCTPSendQueueItem +- (bool)handleObject: (id)object +{ + id exception = nil; + OFData *newData, *oldData; + + @try { + [object sendBuffer: _data.items + length: _data.count * _data.itemSize + streamID: _streamID + PPID: _PPID]; + } @catch (id e) { + exception = e; + } + +# ifdef OF_HAVE_BLOCKS + if (_block != NULL) { + newData = _block(exception); + + if (newData == nil) + return false; + + oldData = _data; + _data = [newData copy]; + [oldData release]; + + return true; + } else { +# endif + if (![_delegate respondsToSelector: + @selector(socket:didSendData:streamID:PPID:exception:)]) + return false; + + newData = [_delegate socket: object + didSendData: _data + streamID: _streamID + PPID: _PPID + exception: exception]; + + if (newData == nil) + return false; + + oldData = _data; + _data = [newData copy]; + [oldData release]; + + return true; +# ifdef OF_HAVE_BLOCKS + } +# endif +} + +- (void)dealloc +{ + [_data release]; +# ifdef OF_HAVE_BLOCKS + [_block release]; +# endif + + [super dealloc]; +} +@end +# endif #endif @implementation OFRunLoop @synthesize currentMode = _currentMode; @@ -1314,10 +1452,56 @@ # endif queueItem->_data = [data copy]; QUEUE_ITEM } + +# ifdef OF_HAVE_SCTP ++ (void)of_addAsyncReceiveForSCTPSocket: (OFSCTPSocket *)sock + buffer: (void *)buffer + length: (size_t)length + mode: (OFRunLoopMode)mode +# ifdef OF_HAVE_BLOCKS + block: (OFSCTPSocketAsyncReceiveBlock)block +# endif + delegate: (id )delegate +{ + NEW_READ(OFRunLoopSCTPReceiveQueueItem, sock, mode) + + queueItem->_delegate = [delegate retain]; +# ifdef OF_HAVE_BLOCKS + queueItem->_block = [block copy]; +# endif + queueItem->_buffer = buffer; + queueItem->_length = length; + + QUEUE_ITEM +} + ++ (void)of_addAsyncSendForSCTPSocket: (OFSCTPSocket *)sock + data: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + mode: (OFRunLoopMode)mode +# ifdef OF_HAVE_BLOCKS + block: (OFSCTPSocketAsyncSendDataBlock)block +# endif + delegate: (id )delegate +{ + NEW_WRITE(OFRunLoopSCTPSendQueueItem, sock, mode) + + queueItem->_delegate = [delegate retain]; +# ifdef OF_HAVE_BLOCKS + queueItem->_block = [block copy]; +# endif + queueItem->_data = [data copy]; + queueItem->_streamID = streamID; + queueItem->_PPID = PPID; + + QUEUE_ITEM +} +# endif # undef NEW_READ # undef NEW_WRITE # undef QUEUE_ITEM + (void)of_cancelAsyncRequestsForObject: (id)object mode: (OFRunLoopMode)mode Index: src/OFSCTPSocket.h ================================================================== --- src/OFSCTPSocket.h +++ src/OFSCTPSocket.h @@ -33,10 +33,33 @@ * * @param exception An exception which occurred while connecting the socket or * `nil` on success */ typedef void (^OFSCTPSocketAsyncConnectBlock)(id _Nullable exception); + +/** + * @brief A block which is called when a packet has been received. + * + * @param length The length of the packet + * @param streamID The stream ID for the message + * @param PPID The Payload Protocol Identifier for the message + * @param exception An exception which occurred while receiving or `nil` on + * success + * @return A bool whether the same block should be used for the next receive + */ +typedef bool (^OFSCTPSocketAsyncReceiveBlock)(size_t length, uint16_t streamID, + uint32_t PPID, id _Nullable exception); + +/** + * @brief A block which is called when a packet has been sent. + * + * @param exception An exception which occurred while reading or `nil` on + * success + * @return The data to repeat the send with or nil if it should not repeat + */ +typedef OFData *_Nullable (^OFSCTPSocketAsyncSendDataBlock)( + id _Nullable exception); #endif /** * @protocol OFSCTPSocketDelegate OFSCTPSocket.h ObjFW/OFSCTPSocket.h * @@ -55,10 +78,45 @@ */ - (void)socket: (OFSCTPSocket *)socket didConnectToHost: (OFString *)host port: (uint16_t)port exception: (nullable id)exception; + +/** + * @brief This method is called when a packet has been received. + * + * @param socket The sequenced packet socket which received a packet + * @param buffer The buffer the packet has been written to + * @param length The length of the packet + * @param streamID The stream ID for the message + * @param PPID The Payload Protocol Identifier for the message + * @param exception An exception that occurred while receiving, or nil on + * success + * @return A bool whether the same block should be used for the next receive + */ +- (bool)socket: (OFSCTPSocket *)socket + didReceiveIntoBuffer: (void *)buffer + length: (size_t)length + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + exception: (nullable id)exception; + +/** + * @brief This method is called when a packet has been sent. + * + * @param socket The sequenced packet socket which sent a packet + * @param data The data which was sent + * @param streamID The stream ID for the message + * @param PPID The Payload Protocol Identifier for the message + * @param exception An exception that occurred while sending, or nil on success + * @return The data to repeat the send with or nil if it should not repeat + */ +- (nullable OFData *)socket: (OFSCTPSocket *)socket + didSendData: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + exception: (nullable id)exception; @end /** * @class OFSCTPSocket OFSCTPSocket.h ObjFW/OFSCTPSocket.h * @@ -156,8 +214,170 @@ * @return The address the socket was bound to * @throw OFBindIPSocketFailedException Binding failed * @throw OFAlreadyOpenException The socket is already connected or bound */ - (OFSocketAddress)bindToHost: (OFString *)host port: (uint16_t)port; + +/** + * @brief Receives a packet for the specified stream and stores it into the + * specified buffer. + * + * If the buffer is too small, the packet is truncated. + * + * @param buffer The buffer to write the packet to + * @param length The length of the buffer + * @param streamID The stream ID for the message + * @param PPID The Payload Protocol Identifier for the message + * @return The length of the received packet + * @throw OFReadFailedException Receiving failed + * @throw OFNotOpenException The socket is not open + */ +- (size_t)receiveIntoBuffer: (void *)buffer + length: (size_t)length + streamID: (nullable uint16_t *)streamID + PPID: (nullable uint32_t *)PPID; + +/** + * @brief Asynchronously receives a packet with stream ID and PPID and stores + * it into the specified buffer. + * + * If the buffer is too small, the packet is truncated. + * + * @param buffer The buffer to write the packet to + * @param length The length of the buffer + */ +- (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer + length: (size_t)length; + +/** + * @brief Asynchronously receives a packet with stream ID and PPID and stores + * it into the specified buffer. + * + * If the buffer is too small, the packet is truncated. + * + * @param buffer The buffer to write the packet to + * @param length The length of the buffer + * @param runLoopMode The run loop mode in which to perform the asynchronous + * receive + */ +- (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer + length: (size_t)length + runLoopMode: (OFRunLoopMode)runLoopMode; + +#ifdef OF_HAVE_BLOCKS +/** + * @brief Asynchronously receives a packet with stream ID and PPID and stores + * it into the specified buffer. + * + * If the buffer is too small, the packet is truncated. + * + * @param buffer The buffer to write the packet to + * @param length The length of the buffer + * @param block The block to call when the packet has been received. If the + * block returns true, it will be called again with the same + * buffer and maximum length when more packets have been received. + * If you want the next method in the queue to handle the packet + * received next, you need to return false from the method. + */ +- (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer + length: (size_t)length + block: (OFSCTPSocketAsyncReceiveBlock)block; + +/** + * @brief Asynchronously receives a packet with stream ID and PPID and stores + * it into the specified buffer. + * + * If the buffer is too small, the packet is truncated. + * + * @param buffer The buffer to write the packet to + * @param length The length of the buffer + * @param runLoopMode The run loop mode in which to perform the asynchronous + * receive + * @param block The block to call when the packet has been received. If the + * block returns true, it will be called again with the same + * buffer and maximum length when more packets have been received. + * If you want the next method in the queue to handle the packet + * received next, you need to return false from the method. + */ +- (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer + length: (size_t)length + runLoopMode: (OFRunLoopMode)runLoopMode + block: (OFSCTPSocketAsyncReceiveBlock)block; +#endif + +/** + * @brief Sends the specified packet on the specified stream. + * + * @param buffer The buffer to send as a packet + * @param length The length of the buffer + * @param streamID The stream ID for the message + * @param PPID The Payload Protocol Identifier for the message + * @throw OFWriteFailedException Sending failed + * @throw OFNotOpenException The socket is not open + */ +- (void)sendBuffer: (const void *)buffer + length: (size_t)length + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID; + +/** + * @brief Asynchronously sends the specified packet on the specified stream. + * + * @param data The data to send as a packet + * @param streamID The stream ID for the message + * @param PPID The Payload Protocol Identifier for the message + */ +- (void)asyncSendData: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID; + +/** + * @brief Asynchronously sends the specified packet on the specified stream. + * + * @param data The data to send as a packet + * @param streamID The stream ID for the message + * @param PPID The Payload Protocol Identifier for the message + * @param runLoopMode The run loop mode in which to perform the asynchronous + * send + */ +- (void)asyncSendData: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + runLoopMode: (OFRunLoopMode)runLoopMode; + +#ifdef OF_HAVE_BLOCKS +/** + * @brief Asynchronously sends the specified packet on the specified stream. + * + * @param data The data to send as a packet + * @param streamID The stream ID for the message + * @param PPID The Payload Protocol Identifier for the message + * @param block The block to call when the packet has been sent. It should + * return the data for the next send with the same callback or nil + * if it should not repeat. + */ +- (void)asyncSendData: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + block: (OFSCTPSocketAsyncSendDataBlock)block; + +/** + * @brief Asynchronously sends the specified packet on the specified stream. + * + * @param data The data to send as a packet + * @param streamID The stream ID for the message + * @param PPID The Payload Protocol Identifier for the message + * @param runLoopMode The run loop mode in which to perform the asynchronous + * send + * @param block The block to call when the packet has been sent. It should + * return the data for the next send with the same callback or nil + * if it should not repeat. + */ +- (void)asyncSendData: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + runLoopMode: (OFRunLoopMode)runLoopMode + block: (OFSCTPSocketAsyncSendDataBlock)block; +#endif @end OF_ASSUME_NONNULL_END Index: src/OFSCTPSocket.m ================================================================== --- src/OFSCTPSocket.m +++ src/OFSCTPSocket.m @@ -38,15 +38,19 @@ #import "OFSocket.h" #import "OFSocket+Private.h" #import "OFString.h" #import "OFThread.h" +#import "OFAcceptSocketFailedException.h" #import "OFAlreadyOpenException.h" #import "OFBindIPSocketFailedException.h" #import "OFGetOptionFailedException.h" #import "OFNotOpenException.h" +#import "OFOutOfRangeException.h" +#import "OFReadFailedException.h" #import "OFSetOptionFailedException.h" +#import "OFWriteFailedException.h" static const OFRunLoopMode connectRunLoopMode = @"OFSCTPSocketConnectRunLoopMode"; @interface OFSCTPSocket () @@ -85,10 +89,11 @@ errNo: (int *)errNo { #if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) int flags; #endif + int one = 1; if (_socket != OFInvalidSocketHandle) @throw [OFAlreadyOpenException exceptionWithObject: self]; if ((_socket = socket( @@ -101,10 +106,20 @@ #if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) fcntl(_socket, F_SETFD, flags | FD_CLOEXEC); #endif + + if (setsockopt(_socket, IPPROTO_SCTP, SCTP_RECVRCVINFO, &one, + sizeof(one)) != 0) { + *errNo = _OFSocketErrNo(); + + closesocket(_socket); + _socket = OFInvalidSocketHandle; + + return false; + } return true; } - (bool)of_connectSocketToAddress: (const OFSocketAddress *)address @@ -306,10 +321,220 @@ objc_autoreleasePoolPop(pool); return address; } + +- (instancetype)accept +{ + OFSCTPSocket *accepted = [super accept]; + int one = 1; + + if (setsockopt(accepted->_socket, IPPROTO_SCTP, SCTP_RECVRCVINFO, &one, + sizeof(one)) != 0) + @throw [OFAcceptSocketFailedException + exceptionWithSocket: self + errNo: _OFSocketErrNo()]; + + return accepted; +} + +- (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length +{ + return [self receiveIntoBuffer: buffer + length: length + streamID: NULL + PPID: NULL]; +} + +- (size_t)receiveIntoBuffer: (void *)buffer + length: (size_t)length + streamID: (uint16_t *)streamID + PPID: (uint32_t *)PPID +{ + ssize_t ret; + struct iovec iov = { + .iov_base = buffer, + .iov_len = length + }; + struct sctp_rcvinfo rcvinfo; + socklen_t rcvinfoSize = (socklen_t)sizeof(rcvinfo); + unsigned int infotype = SCTP_RECVV_RCVINFO; + + if (_socket == OFInvalidSocketHandle) + @throw [OFNotOpenException exceptionWithObject: self]; + + if ((ret = sctp_recvv(_socket, &iov, 1, NULL, 0, &rcvinfo, &rcvinfoSize, + &infotype, 0)) < 0) + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: length + errNo: _OFSocketErrNo()]; + + if (streamID != NULL) { + if (infotype == SCTP_RECVV_RCVINFO && + rcvinfoSize >= (socklen_t)sizeof(rcvinfo)) + *streamID = rcvinfo.rcv_sid; + else + *streamID = 0; + } + + if (PPID != NULL) { + if (infotype == SCTP_RECVV_RCVINFO && + rcvinfoSize >= (socklen_t)sizeof(rcvinfo)) + *PPID = rcvinfo.rcv_ppid; + else + *PPID = 0; + } + + return ret; +} + +- (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer + length: (size_t)length +{ + [self asyncReceiveWithInfoIntoBuffer: buffer + length: length + runLoopMode: OFDefaultRunLoopMode]; +} + +- (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer + length: (size_t)length + runLoopMode: (OFRunLoopMode)runLoopMode +{ + [OFRunLoop of_addAsyncReceiveForSCTPSocket: self + buffer: buffer + length: length + mode: runLoopMode +# ifdef OF_HAVE_BLOCKS + block: NULL +# endif + delegate: _delegate]; +} + +#ifdef OF_HAVE_BLOCKS +- (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer + length: (size_t)length + block: (OFSCTPSocketAsyncReceiveBlock)block +{ + [self asyncReceiveWithInfoIntoBuffer: buffer + length: length + runLoopMode: OFDefaultRunLoopMode + block: block]; +} + +- (void) + asyncReceiveWithInfoIntoBuffer: (void *)buffer + length: (size_t)length + runLoopMode: (OFRunLoopMode)runLoopMode + block: (OFSCTPSocketAsyncReceiveBlock)block +{ + [OFRunLoop of_addAsyncReceiveForSCTPSocket: self + buffer: buffer + length: length + mode: runLoopMode + block: block + delegate: nil]; +} +#endif + +- (void)sendBuffer: (const void *)buffer length: (size_t)length +{ + [self sendBuffer: buffer length: length streamID: 0 PPID: 0]; +} + +- (void)sendBuffer: (const void *)buffer + length: (size_t)length + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID +{ + ssize_t bytesWritten; + struct iovec iov = { + .iov_base = (void *)buffer, + .iov_len = length + }; + struct sctp_sndinfo sndinfo = { + .snd_sid = streamID, + .snd_ppid = PPID + }; + + if (_socket == OFInvalidSocketHandle) + @throw [OFNotOpenException exceptionWithObject: self]; + + if (length > SSIZE_MAX) + @throw [OFOutOfRangeException exception]; + + if ((bytesWritten = sctp_sendv(_socket, &iov, 1, NULL, 0, &sndinfo, + (socklen_t)sizeof(sndinfo), SCTP_SENDV_SNDINFO, 0)) < 0) + @throw [OFWriteFailedException + exceptionWithObject: self + requestedLength: length + bytesWritten: 0 + errNo: _OFSocketErrNo()]; + + if ((size_t)bytesWritten != length) + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length + bytesWritten: bytesWritten + errNo: 0]; +} + +- (void)asyncSendData: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID +{ + [self asyncSendData: data + streamID: streamID + PPID: PPID + runLoopMode: OFDefaultRunLoopMode]; +} + +- (void)asyncSendData: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + runLoopMode: (OFRunLoopMode)runLoopMode +{ + [OFRunLoop of_addAsyncSendForSCTPSocket: self + data: data + streamID: streamID + PPID: PPID + mode: runLoopMode +# ifdef OF_HAVE_BLOCKS + block: NULL +# endif + delegate: _delegate]; +} + +#ifdef OF_HAVE_BLOCKS +- (void)asyncSendData: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + block: (OFSequencedPacketSocketAsyncSendDataBlock)block +{ + [self asyncSendData: data + streamID: streamID + PPID: PPID + runLoopMode: OFDefaultRunLoopMode + block: block]; +} + +- (void)asyncSendData: (OFData *)data + streamID: (uint16_t)streamID + PPID: (uint32_t)PPID + runLoopMode: (OFRunLoopMode)runLoopMode + block: (OFSequencedPacketSocketAsyncSendDataBlock)block +{ + [OFRunLoop of_addAsyncSendForSCTPSocket: self + data: data + streamID: streamID + PPID: PPID + mode: runLoopMode + block: block + delegate: nil]; +} +#endif + - (void)setCanDelaySendingPackets: (bool)canDelaySendingPackets { int v = !canDelaySendingPackets; if (setsockopt(_socket, IPPROTO_SCTP, SCTP_NODELAY, Index: tests/OFSCTPSocketTests.m ================================================================== --- tests/OFSCTPSocketTests.m +++ tests/OFSCTPSocketTests.m @@ -32,10 +32,12 @@ - (void)testSCTPSocket { OFSCTPSocket *server, *client, *accepted; OFSocketAddress address; char buffer[6]; + uint16_t streamID; + uint32_t PPID; server = [OFSCTPSocket socket]; client = [OFSCTPSocket socket]; @try { @@ -56,11 +58,16 @@ accepted = [server accept]; OTAssertEqualObjects(OFSocketAddressString(accepted.remoteAddress), @"127.0.0.1"); - [client sendBuffer: "Hello!" length: 6]; + [client sendBuffer: "Hello!" length: 6 streamID: 1 PPID: 1234]; - [accepted receiveIntoBuffer: buffer length: 6]; + [accepted receiveIntoBuffer: buffer + length: 6 + streamID: &streamID + PPID: &PPID]; OTAssertEqual(memcmp(buffer, "Hello!", 6), 0); + OTAssertEqual(streamID, 1); + OTAssertEqual(PPID, 1234); } @end