More work-in-progress:
* Added some timer methods * Fixed removing the connection too early (and thus replying with a RST instead of nothing when the peer acknowledged our FIN). * BufferQueue::RemoveUntil() did not bump the buffer's sequence * Delayed acknowledge, and retransmitting should work now (the latter is *very* basic right now) * Completed TCPConnection::_ShouldSendSegment() * fReceiveNext was always updated on a received buffer, even if there was a buffer missing. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19396 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -193,6 +193,7 @@ BufferQueue::RemoveUntil(tcp_sequence sequence)
|
|||||||
size_t size = sequence - buffer->sequence;
|
size_t size = sequence - buffer->sequence;
|
||||||
gBufferModule->remove_header(buffer, size);
|
gBufferModule->remove_header(buffer, size);
|
||||||
|
|
||||||
|
buffer->sequence += size;
|
||||||
fNumBytes -= size;
|
fNumBytes -= size;
|
||||||
fContiguousBytes -= size;
|
fContiguousBytes -= size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ TCPConnection::TCPConnection(net_socket *socket)
|
|||||||
fDuplicateAcknowledgeCount(0),
|
fDuplicateAcknowledgeCount(0),
|
||||||
fRoute(NULL),
|
fRoute(NULL),
|
||||||
fReceiveNext(0),
|
fReceiveNext(0),
|
||||||
|
fReceiveMaxAdvertised(0),
|
||||||
fReceiveWindow(socket->receive.buffer_size),
|
fReceiveWindow(socket->receive.buffer_size),
|
||||||
fReceiveMaxSegmentSize(TCP_DEFAULT_MAX_SEGMENT_SIZE),
|
fReceiveMaxSegmentSize(TCP_DEFAULT_MAX_SEGMENT_SIZE),
|
||||||
fReceiveQueue(socket->receive.buffer_size),
|
fReceiveQueue(socket->receive.buffer_size),
|
||||||
@@ -77,7 +78,7 @@ TCPConnection::TCPConnection(net_socket *socket)
|
|||||||
fState(CLOSED),
|
fState(CLOSED),
|
||||||
fError(B_OK)
|
fError(B_OK)
|
||||||
{
|
{
|
||||||
gStackModule->init_timer(&fTimer, _TimeWait, this);
|
//gStackModule->init_timer(&fTimer, _TimeWait, this);
|
||||||
|
|
||||||
recursive_lock_init(&fLock, "tcp lock");
|
recursive_lock_init(&fLock, "tcp lock");
|
||||||
// TODO: to be replaced with a real locking strategy!
|
// TODO: to be replaced with a real locking strategy!
|
||||||
@@ -85,12 +86,17 @@ TCPConnection::TCPConnection(net_socket *socket)
|
|||||||
//benaphore_init(&fSendLock, "tcp send");
|
//benaphore_init(&fSendLock, "tcp send");
|
||||||
fSendLock = create_sem(0, "tcp send");
|
fSendLock = create_sem(0, "tcp send");
|
||||||
fReceiveLock = create_sem(0, "tcp receive");
|
fReceiveLock = create_sem(0, "tcp receive");
|
||||||
|
|
||||||
|
gStackModule->init_timer(&fPersistTimer, TCPConnection::_PersistTimer, this);
|
||||||
|
gStackModule->init_timer(&fRetransmitTimer, TCPConnection::_RetransmitTimer, this);
|
||||||
|
gStackModule->init_timer(&fDelayedAcknowledgeTimer,
|
||||||
|
TCPConnection::_DelayedAcknowledgeTimer, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TCPConnection::~TCPConnection()
|
TCPConnection::~TCPConnection()
|
||||||
{
|
{
|
||||||
gStackModule->set_timer(&fTimer, -1);
|
//gStackModule->set_timer(&fTimer, -1);
|
||||||
|
|
||||||
recursive_lock_destroy(&fLock);
|
recursive_lock_destroy(&fLock);
|
||||||
//benaphore_destroy(&fReceiveLock);
|
//benaphore_destroy(&fReceiveLock);
|
||||||
@@ -115,6 +121,9 @@ TCPConnection::InitCheck() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - protocol API
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
TCPConnection::Open()
|
TCPConnection::Open()
|
||||||
{
|
{
|
||||||
@@ -127,8 +136,9 @@ TCPConnection::Open()
|
|||||||
status_t
|
status_t
|
||||||
TCPConnection::Close()
|
TCPConnection::Close()
|
||||||
{
|
{
|
||||||
//BenaphoreLocker lock(&fSendLock);
|
|
||||||
TRACE(("TCP:%p.Close()\n", this));
|
TRACE(("TCP:%p.Close()\n", this));
|
||||||
|
RecursiveLocker lock(fLock);
|
||||||
|
|
||||||
if (fState == SYNCHRONIZE_SENT || fState == LISTEN) {
|
if (fState == SYNCHRONIZE_SENT || fState == LISTEN) {
|
||||||
fState = CLOSED;
|
fState = CLOSED;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -161,7 +171,6 @@ TCPConnection::Free()
|
|||||||
TRACE(("TCP:%p.Free()\n", this));
|
TRACE(("TCP:%p.Free()\n", this));
|
||||||
|
|
||||||
// TODO: if this connection is not in the hash, we don't have to call this one
|
// TODO: if this connection is not in the hash, we don't have to call this one
|
||||||
remove_connection(this);
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,18 +430,40 @@ TCPConnection::ReadAvailable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - misc
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
TCPConnection::DelayedAcknowledge()
|
TCPConnection::DelayedAcknowledge()
|
||||||
{
|
{
|
||||||
// TODO: use timer instead and/or piggyback on send
|
// if the timer is already running, and there is still more than
|
||||||
return _SendQueued();
|
// half of the receive window free, just wait for the timer to expire
|
||||||
|
if (gStackModule->is_timer_active(&fDelayedAcknowledgeTimer)
|
||||||
|
&& (fReceiveMaxAdvertised - fReceiveNext) > (socket->receive.buffer_size >> 1))
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
if (gStackModule->cancel_timer(&fDelayedAcknowledgeTimer)) {
|
||||||
|
// timer was active, send an ACK now (with the exception above,
|
||||||
|
// we send every other ACK)
|
||||||
|
return _SendQueued();
|
||||||
|
}
|
||||||
|
|
||||||
|
gStackModule->set_timer(&fDelayedAcknowledgeTimer, TCP_DELAYED_ACKNOWLEDGE_TIMEOUT);
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
TCPConnection::SendAcknowledge()
|
TCPConnection::SendAcknowledge()
|
||||||
{
|
{
|
||||||
return _SendQueued();
|
return _SendQueued(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TCPConnection::_StartPersistTimer()
|
||||||
|
{
|
||||||
|
gStackModule->set_timer(&fPersistTimer, 1000000LL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -443,6 +474,9 @@ TCPConnection::UpdateTimeWait()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - receive
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
TCPConnection::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
TCPConnection::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
{
|
{
|
||||||
@@ -582,7 +616,8 @@ TCPConnection::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
fSendQueue.RemoveUntil(segment.acknowledge);
|
fSendQueue.RemoveUntil(segment.acknowledge);
|
||||||
fSendUnacknowledged = segment.acknowledge;
|
fSendUnacknowledged = segment.acknowledge;
|
||||||
|
|
||||||
// TODO: stop retransmit timer
|
// stop retransmit timer
|
||||||
|
gStackModule->cancel_timer(&fRetransmitTimer);
|
||||||
|
|
||||||
// notify threads waiting on the socket to become writable again
|
// notify threads waiting on the socket to become writable again
|
||||||
release_sem_etc(fSendLock, 1, B_DO_NOT_RESCHEDULE);
|
release_sem_etc(fSendLock, 1, B_DO_NOT_RESCHEDULE);
|
||||||
@@ -590,7 +625,7 @@ TCPConnection::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
gSocketModule->notify(socket, B_SELECT_WRITE, fSendWindow);
|
gSocketModule->notify(socket, B_SELECT_WRITE, fSendWindow);
|
||||||
|
|
||||||
// if there is data left to be send, send it now
|
// if there is data left to be send, send it now
|
||||||
//return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, false);
|
_SendQueued();
|
||||||
return DROP;
|
return DROP;
|
||||||
}
|
}
|
||||||
} else if (segment.acknowledge == fSendUnacknowledged
|
} else if (segment.acknowledge == fSendUnacknowledged
|
||||||
@@ -621,7 +656,6 @@ TCPConnection::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
// TODO: close connection depending on state
|
// TODO: close connection depending on state
|
||||||
fError = ECONNREFUSED;
|
fError = ECONNREFUSED;
|
||||||
fState = CLOSED;
|
fState = CLOSED;
|
||||||
remove_connection(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DROP;
|
return DROP;
|
||||||
@@ -692,15 +726,20 @@ TCPConnection::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
if (fSendUnacknowledged >= segment.acknowledge) {
|
if (fSendUnacknowledged >= segment.acknowledge) {
|
||||||
// this is a duplicate acknowledge
|
// this is a duplicate acknowledge
|
||||||
// TODO: handle this!
|
// TODO: handle this!
|
||||||
|
fDuplicateAcknowledgeCount++;
|
||||||
} else {
|
} else {
|
||||||
// this segment acknowleges in flight data
|
// this segment acknowleges in flight data
|
||||||
fDuplicateAcknowledgeCount = 0;
|
fDuplicateAcknowledgeCount = 0;
|
||||||
|
|
||||||
if (fSendMax == segment.acknowledge) {
|
if (fSendMax == segment.acknowledge) {
|
||||||
// there is no outstanding data to be acknowledged
|
// there is no outstanding data to be acknowledged
|
||||||
// TODO: stop retransmit timer
|
// TODO: if the transmit timer function is already waiting
|
||||||
|
// to acquire this connection's lock, we should stop it anyway
|
||||||
|
gStackModule->cancel_timer(&fRetransmitTimer);
|
||||||
} else {
|
} else {
|
||||||
// TODO: set retransmit timer
|
// TODO: set retransmit timer correctly
|
||||||
|
if (!gStackModule->is_timer_active(&fRetransmitTimer))
|
||||||
|
gStackModule->set_timer(&fRetransmitTimer, 1000000LL);
|
||||||
}
|
}
|
||||||
|
|
||||||
fSendUnacknowledged = segment.acknowledge;
|
fSendUnacknowledged = segment.acknowledge;
|
||||||
@@ -774,7 +813,8 @@ TCPConnection::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
// put it in the receive buffer
|
// put it in the receive buffer
|
||||||
|
|
||||||
if (buffer->size > 0) {
|
if (buffer->size > 0) {
|
||||||
fReceiveNext += buffer->size;
|
if (fReceiveNext == segment.sequence)
|
||||||
|
fReceiveNext += buffer->size;
|
||||||
fReceiveQueue.Add(buffer, segment.sequence);
|
fReceiveQueue.Add(buffer, segment.sequence);
|
||||||
} else
|
} else
|
||||||
gBufferModule->free(buffer);
|
gBufferModule->free(buffer);
|
||||||
@@ -783,19 +823,7 @@ TCPConnection::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
// #pragma mark - send
|
||||||
Resends a sent segment (\a data) if the segment's ACK wasn't received
|
|
||||||
before the timeout (eg \a timer expired)
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
TCPConnection::ResendSegment(struct net_timer *timer, void *data)
|
|
||||||
{
|
|
||||||
TRACE(("TCP:ResendSegment(%p)\n", data));
|
|
||||||
if (data == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: implement me!
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -838,11 +866,12 @@ inline bool
|
|||||||
TCPConnection::_ShouldSendSegment(tcp_segment_header &segment, uint32 length,
|
TCPConnection::_ShouldSendSegment(tcp_segment_header &segment, uint32 length,
|
||||||
bool outstandingAcknowledge)
|
bool outstandingAcknowledge)
|
||||||
{
|
{
|
||||||
// Avoid the silly window syndrome - we only send a segment in case:
|
|
||||||
// - we have a full segment to send, or
|
|
||||||
// - we're at the end of our buffer queue (and we're
|
|
||||||
// - we're retransmitting data
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
|
// Avoid the silly window syndrome - we only send a segment in case:
|
||||||
|
// - we have a full segment to send, or
|
||||||
|
// - we're at the end of our buffer queue, or
|
||||||
|
// - the buffer is at least larger than half of the maximum send window, or
|
||||||
|
// - we're retransmitting data
|
||||||
if (length == fSendMaxSegmentSize
|
if (length == fSendMaxSegmentSize
|
||||||
|| ((!outstandingAcknowledge || (fOptions & TCP_NODELAY) != 0)
|
|| ((!outstandingAcknowledge || (fOptions & TCP_NODELAY) != 0)
|
||||||
&& tcp_sequence(fSendNext + length) == fSendQueue.LastSequence())
|
&& tcp_sequence(fSendNext + length) == fSendQueue.LastSequence())
|
||||||
@@ -851,11 +880,23 @@ TCPConnection::_ShouldSendSegment(tcp_segment_header &segment, uint32 length,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: incomplete!
|
// check if we need to send a window update to the peer
|
||||||
|
if (segment.advertised_window > 0) {
|
||||||
|
// correct the window to take into account what already has been advertised
|
||||||
|
uint32 window = (min_c(segment.advertised_window, TCP_MAX_WINDOW)
|
||||||
|
<< fReceiveWindowShift) - (fReceiveMaxAdvertised - fReceiveNext);
|
||||||
|
|
||||||
if ((segment.flags & (TCP_FLAG_SYNCHRONIZE | TCP_FLAG_FINISH | TCP_FLAG_RESET | TCP_FLAG_ACKNOWLEDGE)) != 0)
|
// if we can advertise a window larger than twice the maximum segment
|
||||||
|
// size, or half the maximum buffer size we send a window update
|
||||||
|
if (window >= (fReceiveMaxSegmentSize << 1)
|
||||||
|
|| window >= (socket->receive.buffer_size >> 1))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((segment.flags & (TCP_FLAG_SYNCHRONIZE | TCP_FLAG_FINISH | TCP_FLAG_RESET)) != 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// there is no reason to send a segment just now
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -912,8 +953,14 @@ dprintf("fSendWindow = %lu, available = %lu, fSendNext = %lu, fSendUnacknowledge
|
|||||||
while (true) {
|
while (true) {
|
||||||
dprintf("length = %ld, segmentLength = %lu\n", length, segmentLength);
|
dprintf("length = %ld, segmentLength = %lu\n", length, segmentLength);
|
||||||
// Determine if we should really send this segment
|
// Determine if we should really send this segment
|
||||||
if (!force && !_ShouldSendSegment(segment, segmentLength, outstandingAcknowledge))
|
if (!force && !_ShouldSendSegment(segment, segmentLength, outstandingAcknowledge)) {
|
||||||
|
if (fSendQueue.Available()
|
||||||
|
&& !gStackModule->is_timer_active(&fPersistTimer)
|
||||||
|
&& !gStackModule->is_timer_active(&fRetransmitTimer))
|
||||||
|
_StartPersistTimer();
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
net_buffer *buffer = gBufferModule->create(256);
|
net_buffer *buffer = gBufferModule->create(256);
|
||||||
if (buffer == NULL)
|
if (buffer == NULL)
|
||||||
@@ -973,6 +1020,9 @@ dprintf("length = %ld, segmentLength = %lu\n", length, segmentLength);
|
|||||||
fSendMax += size;
|
fSendMax += size;
|
||||||
fSendNext += size;
|
fSendNext += size;
|
||||||
|
|
||||||
|
fReceiveMaxAdvertised = fReceiveNext
|
||||||
|
+ ((uint32)segment.advertised_window << fReceiveWindowShift);
|
||||||
|
|
||||||
length -= segmentLength;
|
length -= segmentLength;
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
break;
|
break;
|
||||||
@@ -985,12 +1035,51 @@ dprintf("length = %ld, segmentLength = %lu\n", length, segmentLength);
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
// #pragma mark - timer
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
|
TCPConnection::_RetransmitTimer(net_timer *timer, void *data)
|
||||||
|
{
|
||||||
|
TCPConnection *connection = (TCPConnection *)data;
|
||||||
|
|
||||||
|
RecursiveLocker locker(connection->Lock());
|
||||||
|
|
||||||
|
connection->fSendNext = connection->fSendUnacknowledged;
|
||||||
|
connection->_SendQueued();
|
||||||
|
connection->fSendNext = connection->fSendMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
|
TCPConnection::_PersistTimer(net_timer *timer, void *data)
|
||||||
|
{
|
||||||
|
TCPConnection *connection = (TCPConnection *)data;
|
||||||
|
|
||||||
|
RecursiveLocker locker(connection->Lock());
|
||||||
|
connection->_SendQueued(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
|
TCPConnection::_DelayedAcknowledgeTimer(struct net_timer *timer, void *data)
|
||||||
|
{
|
||||||
|
TCPConnection *connection = (TCPConnection *)data;
|
||||||
|
|
||||||
|
RecursiveLocker locker(connection->Lock());
|
||||||
|
connection->_SendQueued(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
TCPConnection::_TimeWait(struct net_timer *timer, void *data)
|
TCPConnection::_TimeWait(struct net_timer *timer, void *data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - hash functions
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
TCPConnection::Compare(void *_connection, const void *_key)
|
TCPConnection::Compare(void *_connection, const void *_key)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,18 +53,21 @@ class TCPConnection : public net_protocol {
|
|||||||
net_buffer *buffer);
|
net_buffer *buffer);
|
||||||
int32 Receive(tcp_segment_header& segment, net_buffer *buffer);
|
int32 Receive(tcp_segment_header& segment, net_buffer *buffer);
|
||||||
|
|
||||||
static void ResendSegment(struct net_timer *timer, void *data);
|
|
||||||
static int Compare(void *_packet, const void *_key);
|
static int Compare(void *_packet, const void *_key);
|
||||||
static uint32 Hash(void *_packet, const void *_key, uint32 range);
|
static uint32 Hash(void *_packet, const void *_key, uint32 range);
|
||||||
static int32 HashOffset() { return offsetof(TCPConnection, fHashNext); }
|
static int32 HashOffset() { return offsetof(TCPConnection, fHashNext); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void _StartPersistTimer();
|
||||||
uint8 _CurrentFlags();
|
uint8 _CurrentFlags();
|
||||||
bool _ShouldSendSegment(tcp_segment_header &segment, uint32 length,
|
bool _ShouldSendSegment(tcp_segment_header &segment, uint32 length,
|
||||||
bool outstandingAcknowledge);
|
bool outstandingAcknowledge);
|
||||||
status_t _SendQueued(bool force = false);
|
status_t _SendQueued(bool force = false);
|
||||||
|
|
||||||
static void _TimeWait(struct net_timer *timer, void *data);
|
static void _TimeWait(net_timer *timer, void *data);
|
||||||
|
static void _RetransmitTimer(net_timer *timer, void *data);
|
||||||
|
static void _PersistTimer(net_timer *timer, void *data);
|
||||||
|
static void _DelayedAcknowledgeTimer(net_timer *timer, void *data);
|
||||||
|
|
||||||
TCPConnection *fHashNext;
|
TCPConnection *fHashNext;
|
||||||
|
|
||||||
@@ -91,6 +94,7 @@ class TCPConnection : public net_protocol {
|
|||||||
// TODO: don't use a net_route, but a net_route_info!!!
|
// TODO: don't use a net_route, but a net_route_info!!!
|
||||||
|
|
||||||
tcp_sequence fReceiveNext;
|
tcp_sequence fReceiveNext;
|
||||||
|
tcp_sequence fReceiveMaxAdvertised;
|
||||||
uint32 fReceiveWindow;
|
uint32 fReceiveWindow;
|
||||||
uint32 fReceiveMaxSegmentSize;
|
uint32 fReceiveMaxSegmentSize;
|
||||||
BufferQueue fReceiveQueue;
|
BufferQueue fReceiveQueue;
|
||||||
@@ -110,10 +114,11 @@ class TCPConnection : public net_protocol {
|
|||||||
|
|
||||||
tcp_state fState;
|
tcp_state fState;
|
||||||
status_t fError;
|
status_t fError;
|
||||||
vint32 fDelayedAcknowledge;
|
|
||||||
|
|
||||||
// timer
|
// timer
|
||||||
net_timer fTimer;
|
net_timer fRetransmitTimer;
|
||||||
|
net_timer fPersistTimer;
|
||||||
|
net_timer fDelayedAcknowledgeTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TCP_CONNECTION_H
|
#endif // TCP_CONNECTION_H
|
||||||
|
|||||||
@@ -101,7 +101,9 @@ class tcp_sequence {
|
|||||||
#define TCP_FLAG_CONGESTION_WINDOW_REDUCED 0x80
|
#define TCP_FLAG_CONGESTION_WINDOW_REDUCED 0x80
|
||||||
|
|
||||||
#define TCP_CONNECTION_TIMEOUT 75000000 // 75 secs
|
#define TCP_CONNECTION_TIMEOUT 75000000 // 75 secs
|
||||||
|
#define TCP_DELAYED_ACKNOWLEDGE_TIMEOUT 100000 // 100 msecs
|
||||||
#define TCP_DEFAULT_MAX_SEGMENT_SIZE 536
|
#define TCP_DEFAULT_MAX_SEGMENT_SIZE 536
|
||||||
|
#define TCP_MAX_WINDOW 65535
|
||||||
|
|
||||||
struct tcp_option {
|
struct tcp_option {
|
||||||
uint8 kind;
|
uint8 kind;
|
||||||
|
|||||||
Reference in New Issue
Block a user