示例#1
0
  JNetReturn Sender::Connect( std::string IPAddress, short port )
  {
    SOCKET* newConnection = new SOCKET();
    sockaddr_in senderAddress;
    senderAddress.sin_family = AF_INET;
    senderAddress.sin_port = 0;
    senderAddress.sin_addr.S_un.S_addr = inet_addr( IPAddress_.c_str() );

    sockaddr_in remoteAddress;
    remoteAddress.sin_family = AF_INET;
    remoteAddress.sin_port = htons( port );
    remoteAddress.sin_addr.S_un.S_addr = inet_addr( IPAddress.c_str() );

    *newConnection = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);

    if( *newConnection == INVALID_SOCKET )
    {
      printf("Error, invalid socket, error code: %d\n", WSAGetLastError() );
      delete newConnection;
      newConnection = NULL;
      return JNetReturn::SocketError;
    }

    //socket was created fine, time to bind it.

    int result = bind( *newConnection, reinterpret_cast<sockaddr*>( &senderAddress), sizeof(senderAddress) );

    if( result == SOCKET_ERROR )
    {
      printf("Error binding socket to local endpoint. Error code: %d\n", WSAGetLastError() );
      FreeSocket( &newConnection );
      return JNetReturn::SocketError;
    }

    result = connect( *newConnection, reinterpret_cast<sockaddr*>( &remoteAddress), sizeof(remoteAddress) );

    if(result == SOCKET_ERROR )
    {
      result = WSAGetLastError();
      printf("Failed to connect. Error code: %d\n", result );
      FreeSocket( &newConnection );
      return JNetReturn::SocketError;
    }

    //last step create the connection
    try
    {
      connection_ = new Connection( newConnection );
    }
    catch( JNetException const & exception )
    {
      printf("Failed to create a connection object.\n");
      printf("Error code: %d\nError: %s\n", exception.Code(), exception.What() );
      FreeSocket( &newConnection );
    }

    return JNetReturn::Success;

  }
示例#2
0
/*----------------------------------------------------------------------------*/
int 
mtcp_epoll_create(mctx_t mctx, int size)
{
	mtcp_manager_t mtcp = g_mtcp[mctx->cpu];
	struct mtcp_epoll *ep;
	socket_map_t epsocket;

	if (size <= 0) {
		errno = EINVAL;
		return -1;
	}

	epsocket = AllocateSocket(mctx, MTCP_SOCK_EPOLL, FALSE);
	if (!epsocket) {
		errno = ENFILE;
		return -1;
	}

	ep = (struct mtcp_epoll *)calloc(1, sizeof(struct mtcp_epoll));
	if (!ep) {
		FreeSocket(mctx, epsocket->id, FALSE);
		return -1;
	}

	/* create event queues */
	ep->usr_queue = CreateEventQueue(size);
	if (!ep->usr_queue)
		return -1;

	ep->usr_shadow_queue = CreateEventQueue(size);
	if (!ep->usr_shadow_queue) {
		DestroyEventQueue(ep->usr_queue);
		return -1;
	}

	ep->mtcp_queue = CreateEventQueue(size);
	if (!ep->mtcp_queue) {
		DestroyEventQueue(ep->usr_queue);
		DestroyEventQueue(ep->usr_shadow_queue);
		return -1;
	}

	TRACE_EPOLL("epoll structure of size %d created.\n", size);

	mtcp->ep = ep;
	epsocket->ep = ep;

	if (pthread_mutex_init(&ep->epoll_lock, NULL)) {
		return -1;
	}
	if (pthread_cond_init(&ep->epoll_cond, NULL)) {
		return -1;
	}

	return epsocket->id;
}
示例#3
0
文件: api.c 项目: Cppowboy/mtcp
/*----------------------------------------------------------------------------*/
int 
mtcp_close(mctx_t mctx, int sockid)
{
	mtcp_manager_t mtcp;
	int ret;

	mtcp = GetMTCPManager(mctx);
	if (!mtcp) {
		return -1;
	}

	if (sockid < 0 || sockid >= CONFIG.max_concurrency) {
		TRACE_API("Socket id %d out of range.\n", sockid);
		errno = EBADF;
		return -1;
	}

	if (mtcp->smap[sockid].socktype == MTCP_SOCK_UNUSED) {
		TRACE_API("Invalid socket id: %d\n", sockid);
		errno = EBADF;
		return -1;
	}

	TRACE_API("Socket %d: mtcp_close called.\n", sockid);

	switch (mtcp->smap[sockid].socktype) {
	case MTCP_SOCK_STREAM:
		ret = CloseStreamSocket(mctx, sockid);
		break;

	case MTCP_SOCK_LISTENER:
		ret = CloseListeningSocket(mctx, sockid);
		break;

	case MTCP_SOCK_EPOLL:
		ret = CloseEpollSocket(mctx, sockid);
		break;

	case MTCP_SOCK_PIPE:
		ret = PipeClose(mctx, sockid);
		break;

	default:
		errno = EINVAL;
		ret = -1;
		break;
	}
	
	FreeSocket(mctx, sockid, FALSE);

	return ret;
}
示例#4
0
static void tcpsock_close(Socket *sock)
{
	TCPSocket *tcpsock = (TCPSocket*) sock;
	if (tcpsock->thread != NULL)
	{

		// terminate the semSendFetch semaphore, causing the handler thread to send a FIN and
		// later terminate.
		semTerminate(&tcpsock->semSendFetch);
	}
	else
	{
		FreeSocket(sock);
	};
};
示例#5
0
文件: server.c 项目: ranauei/melabot
static void ReapConfServers() {
	
	struct servercstruct *slist, *tnext;
	
	slist=serverclist;
	
	while(slist!=NULL) {
		tnext=slist->next;
		
		if(slist->reap) {		
			UnlinkListItem(slist,serverclist);
			FreeSocket(slist->sinfo);
			free(slist->address);
			free(slist->password);
			free(slist);		
		}	
		slist=tnext;
	}

}
示例#6
0
bool CIocpServer::CheckClients( )
{
	m_ClientsLock.Enter( );
	int tmpcnt = 0, tmpcnt2 = 0;
	for( CIndexArray<CIocpSocket>::Iter i = m_Clients.First( ); i.isValid( ); i++ )
	{
		CIocpSocket* sock = *i;

		sock->m_Lock.Enter( );

		//* If client should be timed out, print info :)
		if( m_ClientTimeout != 0 && sock->m_LastRecv < GetTickCount( ) - m_ClientTimeout )
			printf( "[OBJ:%d:%s] %d(R:%d,S:%d,D:%d), %d\n", i.id( ), sock->m_Ip, sock->m_RefCount, sock->m_RRefCount, sock->m_SRefCount, sock->m_DRefCount, GetTickCount( ) - sock->m_LastRecv );
		
		//* If client timed out, disconnect
		if( m_ClientTimeout > 0 ) {
			if( sock->m_LastRecv < GetTickCount( ) - m_ClientTimeout )
				sock->CloseSocket( );
		}

		//* If the client has no references, delete it
		if( sock->m_RefCount == 0 ) {
			sock->Disconnected( );
			m_Clients.Remove( i );
			sock->m_Lock.Leave( );
			FreeSocket( sock );

			tmpcnt2++;
		} else {
			sock->m_Lock.Leave( );
		}
		tmpcnt++;
	}
	//printf( "Clients Count :: %d/%d/%d\n", tmpcnt2, tmpcnt, m_Clients.GetUsedCount( ) );
	bool hasobjects = m_Clients.GetUsedCount( ) > 0;
	m_ClientsLock.Leave( );
	return hasobjects;
};
示例#7
0
static __inline__ int raw_NET2_TCPConnectToIP(IPaddress *ip)
{
  int s = -1;

  s = AllocSocket(TCPClientSocket);
  if (-1 == s)
  {
    setError("NET2: out of memory", -1);
    return -1;
  }

  socketHeap[s]->s.tcpSocket = snTCPOpen(ip);
  if (NULL == socketHeap[s]->s.tcpSocket)
  {
    setError("NET2: can't open a socket", -1);
    FreeSocket(s);
    return -1;
  }

  socketHeap[s]->state = addState;

  return s;
}
示例#8
0
static __inline__ int raw_NET2_TCPAcceptOnIP(IPaddress *ip)
{
  int s = -1;

  s = AllocSocket(TCPServerSocket);
  if (-1 == s)
  {
    setError("NET2: out of memory", -1);
    return -1;
  }

  socketHeap[s]->s.tcpSocket = snTCPOpen(ip);
  if (NULL == socketHeap[s]->s.tcpSocket)
  {
    setError("NET2: can't open a socket", -1);
    FreeSocket(s);
    return -1;
  }

  socketHeap[s]->p.tcpPort = ip->port;
  socketHeap[s]->state = addState;

  return s;
}
示例#9
0
static __inline__ int raw_NET2_UDPAcceptOn(int port, int size)
{
  int s = -1;

  s = AllocSocket(UDPServerSocket);
  if (-1 == s)
  {
    setError("NET2: out of memory", s);
    return -1;
  }

  socketHeap[s]->s.udpSocket = snUDPOpen(port);
  if (NULL == socketHeap[s]->s.udpSocket)
  {
    setError("NET2: can't open a socket", s);
    FreeSocket(s);
    return -1;
  }

  socketHeap[s]->p.udpLen = size;
  socketHeap[s]->state = addState;

  return s;
}
示例#10
0
static int PumpNetworkEvents(void *nothing)
{
  int i = 0;

#define timeOut (10)

  while (!doneYet)
  {
    if (-1 == snCheckSockets(socketSet, timeOut))
    {
      setError("NET2: the CheckSockets call failed", -1);
    }

    lockData();
    while ((!doneYet) && waitForRead)
    {
      waitUntilRead();
    }

    for (i = 0; ((!doneYet) && (i < lastHeapSocket)); i++)
    {
      if (addState == socketHeap[i]->state)
      {
        switch(socketHeap[i]->type)
        {
        case unusedSocket:
          sendError("NET2: trying to add an unused socket", i);
          break;

        case TCPServerSocket:
        case TCPClientSocket:
          if (-1 != snTCPAddSocket(socketHeap[i]->s.tcpSocket))
          {
            socketHeap[i]->state = readyState;
          }
          else
          {
            socketHeap[i]->state = delState;
            sendError("NET2: can't add a TCP socket to the socket set", i);
          }
          break;

        case UDPServerSocket:
          if (-1 != snUDPAddSocket(socketHeap[i]->s.udpSocket))
          {
            socketHeap[i]->state = readyState;
          }
          else
          {
            socketHeap[i]->state = delState;
            sendError("NET2: can't add a UDP socket to the socket set", i);
          }
          break;

        default:
          sendError("NET2: invalid socket type, this should never happen", i);
          break;
        }
      }
      else if (delState == socketHeap[i]->state)
      {
        switch(socketHeap[i]->type)
        {
        case unusedSocket:
          sendError("NET2: trying to delete an unused socket", i);
          break;

        case TCPServerSocket:
        case TCPClientSocket:
          if (-1 == snTCPDelSocket(socketHeap[i]->s.tcpSocket))
          {
            sendError("NET2: can't delete a TCP socket from the socket set", i);
          }
          snTCPClose(socketHeap[i]->s.tcpSocket);
          FreeSocket(i);
          break;

        case UDPServerSocket:
          if (-1 == snUDPDelSocket(socketHeap[i]->s.udpSocket))
          {
            sendError("NET2: can't delete a UDP socket from the socket set", i);
          }
          snUDPClose(socketHeap[i]->s.udpSocket);
          FreeSocket(i);
          break;

        default:
          sendError("NET2: invalid socket type, this should never happen", i);
          break;
        }
      }
      else if ((TCPServerSocket == socketHeap[i]->type) &&
               (snSocketReady(socketHeap[i]->s.genSocket)))
      {
        TCPsocket socket = NULL;
        socket = snTCPAccept(socketHeap[i]->s.tcpSocket);

        if (NULL != socket)
        {
          int s = -1;

          s = AllocSocket(TCPClientSocket);
          if (-1 != s)
          {
            //printf("got a connection!\n");

            socketHeap[s]->s.tcpSocket = socket;
            socketHeap[s]->state = addState;
            sendEvent(NET2_TCPACCEPTEVENT,  s, socketHeap[i]->p.tcpPort);
          }
          else // can't handle the connection, so close it.
          {
            snTCPClose(socket);
            sendError("NET2: a TCP accept failed", i); // let the app know
          }
        }
      }
      else if ((TCPClientSocket == socketHeap[i]->type) && 
               (readyState == socketHeap[i]->state) &&
               (snSocketReady(socketHeap[i]->s.genSocket)))
      {
        int len;
        CharQue *tb = &socketHeap[i]->q.tb;

        if (tcpQueLen <= tb->len)
        {
          waitForRead = 1;
        }
        else
        {
          len = snTCPRead(socketHeap[i]->s.tcpSocket, 
                          &tb->buf[tb->len], 
                          tcpQueLen - tb->len);

          if (0 < len)
          {
            int oldlen = tb->len;
            tb->len += len;
            if (0 == oldlen)
            {
              sendEvent(NET2_TCPRECEIVEEVENT, i, -1);
            }
          }
          else // no byte, must be dead.
          {
            socketHeap[i]->state = dyingState;
            sendEvent(NET2_TCPCLOSEEVENT, i, -1);
          }
        }
      }
      else if ((UDPServerSocket == socketHeap[i]->type) && 
               (readyState == socketHeap[i]->state) &&
               (snSocketReady(socketHeap[i]->s.genSocket)))
      {
        int recv = 0;
        UDPpacket *p = NULL;
        PacketQue *ub = &socketHeap[i]->q.ub;

        if (PacketQueFull(ub))
        {
          waitForRead = 1;
        }
        else
        {
          while ((!PacketQueFull(ub)) &&
                 (NULL != (p = snUDPAllocPacket(socketHeap[i]->p.udpLen))) &&
                 (1 == (recv = snUDPRecv(socketHeap[i]->s.udpSocket, p))))
          {
            if (PacketQueEmpty(ub))
            {
              EnquePacket(ub, &p);
              sendEvent(NET2_UDPRECEIVEEVENT, i, -1);
            }
            else
            {
              EnquePacket(ub, &p);
            }
          }

          // unravel terminating conditions and free left over memory
          // if we need to
          if (!PacketQueFull(ub)) // if the packet que is full then everything is fine
          {
            if (NULL != p) // couldn't alloc a packet
            {
              if (0 >= recv) // ran out of packets
              {
                snUDPFreePacket(p);
              }
            }
            else
            {
              sendError("NET2: out of memory", i);
            }
          }
        }
      }
    }
    unlockData();
  }

  return 0;
}
示例#11
0
文件: api.c 项目: Cppowboy/mtcp
/*----------------------------------------------------------------------------*/
int 
mtcp_abort(mctx_t mctx, int sockid)
{
	mtcp_manager_t mtcp;
	tcp_stream *cur_stream;
	int ret;

	mtcp = GetMTCPManager(mctx);
	if (!mtcp) {
		return -1;
	}

	if (sockid < 0 || sockid >= CONFIG.max_concurrency) {
		TRACE_API("Socket id %d out of range.\n", sockid);
		errno = EBADF;
		return -1;
	}

	if (mtcp->smap[sockid].socktype == MTCP_SOCK_UNUSED) {
		TRACE_API("Invalid socket id: %d\n", sockid);
		errno = EBADF;
		return -1;
	}
	
	if (mtcp->smap[sockid].socktype != MTCP_SOCK_STREAM) {
		TRACE_API("Not an end socket. id: %d\n", sockid);
		errno = ENOTSOCK;
		return -1;
	}

	cur_stream = mtcp->smap[sockid].stream;
	if (!cur_stream) {
		TRACE_API("Stream %d: does not exist.\n", sockid);
		errno = ENOTCONN;
		return -1;
	}

	TRACE_API("Socket %d: mtcp_abort()\n", sockid);
	
	FreeSocket(mctx, sockid, FALSE);
	cur_stream->socket = NULL;

	if (cur_stream->state == TCP_ST_CLOSED) {
		TRACE_API("Stream %d: connection already reset.\n", sockid);
		return ERROR;

	} else if (cur_stream->state == TCP_ST_SYN_SENT) {
		/* TODO: this should notify event failure to all 
		   previous read() or write() calls */
		cur_stream->state = TCP_ST_CLOSED;
		cur_stream->close_reason = TCP_ACTIVE_CLOSE;
		SQ_LOCK(&mtcp->ctx->destroyq_lock);
		StreamEnqueue(mtcp->destroyq, cur_stream);
		SQ_UNLOCK(&mtcp->ctx->destroyq_lock);
		mtcp->wakeup_flag = TRUE;
		return 0;

	} else if (cur_stream->state == TCP_ST_CLOSING || 
			cur_stream->state == TCP_ST_LAST_ACK || 
			cur_stream->state == TCP_ST_TIME_WAIT) {
		cur_stream->state = TCP_ST_CLOSED;
		cur_stream->close_reason = TCP_ACTIVE_CLOSE;
		SQ_LOCK(&mtcp->ctx->destroyq_lock);
		StreamEnqueue(mtcp->destroyq, cur_stream);
		SQ_UNLOCK(&mtcp->ctx->destroyq_lock);
		mtcp->wakeup_flag = TRUE;
		return 0;
	}

	/* the stream structure will be destroyed after sending RST */
	if (cur_stream->sndvar->on_resetq) {
		TRACE_ERROR("Stream %d: calling mtcp_abort() "
				"when in reset queue.\n", sockid);
		errno = ECONNRESET;
		return -1;
	}
	SQ_LOCK(&mtcp->ctx->reset_lock);
	cur_stream->sndvar->on_resetq = TRUE;
	ret = StreamEnqueue(mtcp->resetq, cur_stream);
	SQ_UNLOCK(&mtcp->ctx->reset_lock);
	mtcp->wakeup_flag = TRUE;

	if (ret < 0) {
		TRACE_ERROR("(NEVER HAPPEN) Failed to enqueue the stream to close.\n");
		errno = EAGAIN;
		return -1;
	}

	return 0;
}
示例#12
0
static void tcpThread(void *context)
{
	Socket *sock = (Socket*) context;
	TCPSocket *tcpsock = (TCPSocket*) context;
	Semaphore *sems[4] = {&tcpsock->semConnected, &tcpsock->semStop, &tcpsock->semAck, &tcpsock->semAckOut};
	Semaphore *semsOut[3] = {&tcpsock->semStop, &tcpsock->semAckOut, &tcpsock->semSendFetch};

	detachMe();
	
	uint16_t srcport, dstport;
	if (sock->domain == AF_INET)
	{
		const struct sockaddr_in *inaddr = (const struct sockaddr_in*) &tcpsock->peername;
		struct sockaddr_in *inname = (struct sockaddr_in*) &tcpsock->sockname;
		srcport = inname->sin_port;
		dstport = inaddr->sin_port;
	}
	else
	{
		const struct sockaddr_in6 *inaddr = (const struct sockaddr_in6*) &tcpsock->peername;
		struct sockaddr_in6 *inname = (struct sockaddr_in6*) &tcpsock->sockname;
		inname->sin6_family = AF_INET6;
		srcport = inname->sin6_port;
		dstport = inaddr->sin6_port;
	};

	int connectDone = 0;
	if (tcpsock->state == TCP_ESTABLISHED)
	{
		sems[0] = NULL;
		connectDone = 1;
	};
	int wantExit = 0;
	while (1)
	{
		if (wantExit) break;
		
		uint64_t deadline = getNanotime() + sock->options[GSO_SNDTIMEO];
		if (sock->options[GSO_SNDTIMEO] == 0)
		{
			deadline = 0;
		};
		
		int sendOK = 0;
		int retransCount = 16;
		while (((getNanotime() < deadline) || (deadline == 0)) && (retransCount--))
		{
			tcpsock->currentOut->segment->ackno = htonl(tcpsock->nextAckNo);
			ChecksumOutbound(tcpsock->currentOut);
			
			int status = sendPacketEx(&tcpsock->sockname, &tcpsock->peername,
							tcpsock->currentOut->segment, tcpsock->currentOut->size,
							IPPROTO_TCP, sock->options, sock->ifname);

			if (status != 0)
			{
				tcpsock->sockErr = -status;

				if (tcpsock->state == TCP_CONNECTING)
				{
					semSignal(&tcpsock->semConnected);
				};
			
				tcpsock->state = TCP_TERMINATED;
				kfree(tcpsock->currentOut);
				wantExit = 1;
				break;
			};
			
			uint8_t bitmap = 0;
			if (semPoll(4, sems, &bitmap, 0, TCP_RETRANS_TIMEOUT) == 0)
			{
				continue;
			};
			
			if (bitmap & (1 << 1))
			{
				kfree(tcpsock->currentOut);
				tcpsock->state = TCP_TERMINATED;
				wantExit = 1;
				break;
			};
			
			if (bitmap & (1 << 2))
			{
				semWait(&tcpsock->semAck);
				sendOK = 1;
				break;
			};
			
			if (bitmap & (1 << 3))
			{
				semWaitGen(&tcpsock->semAckOut, -1, 0, 0);
			};
		};
		
		if (wantExit) break;
		
		int wasFin = tcpsock->currentOut->segment->flags & TCP_FIN;
		kfree(tcpsock->currentOut);
		
		if (!sendOK)
		{
			tcpsock->sockErr = ETIMEDOUT;
			
			if (tcpsock->state == TCP_CONNECTING)
			{
				semSignal(&tcpsock->semConnected);
			};
			
			tcpsock->state = TCP_TERMINATED;
			return;
		};
		
		if (!connectDone)
		{
			while ((getNanotime() < deadline) || (deadline == 0))
			{
				uint8_t bitmap = 0;
				semPoll(3, sems, &bitmap, 0, sock->options[GSO_SNDTIMEO]);
			
				if (bitmap & (1 << 1))
				{
					tcpsock->state = TCP_TERMINATED;
					return;
				};
				
				if (bitmap & (1 << 0))
				{
					connectDone = 1;
					break;
				};
			};

			if (!connectDone)
			{
				tcpsock->sockErr = ETIMEDOUT;
				semSignal(&tcpsock->semConnected);
				tcpsock->state = TCP_TERMINATED;
				return;
			};

			sems[0] = NULL;
			tcpsock->state = TCP_ESTABLISHED;
		};
		
		if (wasFin) break;
		
		while (1)
		{
			uint8_t bitmap = 0;
			semPoll(3, semsOut, &bitmap, 0, 0);
		
			if (bitmap & (1 << 2))
			{
				if (bitmap & (1 << 1))
				{
					semWaitGen(&tcpsock->semAckOut, -1, 0, 0);
				};
				
				int count = semWaitGen(&tcpsock->semSendFetch, 512, 0, 0);
				
				TCPOutbound *ob = CreateOutbound(&tcpsock->sockname, &tcpsock->peername, (size_t)count);
				ob->segment->srcport = srcport;
				ob->segment->dstport = dstport;
				ob->segment->seqno = htonl(tcpsock->nextSeqNo);
				// ackno filled in at the start of the loop iteration
				ob->segment->dataOffsetNS = 0x50;
				ob->segment->flags = TCP_PSH | TCP_ACK;
				// a count of zero means end of data, so send FIN.
				if (count == 0)
				{
					ob->segment->flags = TCP_FIN | TCP_ACK;
					tcpsock->nextSeqNo++;
				};
				ob->segment->winsz = htons(TCP_BUFFER_SIZE);
				uint8_t *put = (uint8_t*) &ob->segment[1];
				uint32_t size = (uint32_t)count;
				while (count--)
				{
					*put++ = tcpsock->bufSend[tcpsock->idxSendFetch];
					tcpsock->idxSendFetch = (tcpsock->idxSendFetch+1) % TCP_BUFFER_SIZE;
				};

				tcpsock->nextSeqNo += size;
				tcpsock->expectedAck = tcpsock->nextSeqNo;
				tcpsock->currentOut = ob;
				semSignal2(&tcpsock->semSendPut, (int)size);
				break;		// continues the outer loop
			};
			
			if (bitmap & (1 << 1))
			{
				semWaitGen(&tcpsock->semAckOut, -1, 0, 0);
			
				TCPOutbound *ob = CreateOutbound(&tcpsock->sockname, &tcpsock->peername, 0);
				TCPSegment *ack = ob->segment;
				
				ack->srcport = srcport;
				ack->dstport = dstport;
				ack->seqno = htonl(tcpsock->nextSeqNo);
				ack->ackno = htonl(tcpsock->nextAckNo);
				ack->dataOffsetNS = 0x50;
				ack->flags = TCP_ACK;
				ack->winsz = htons(TCP_BUFFER_SIZE);
				ChecksumOutbound(ob);
				
				sendPacketEx(&tcpsock->sockname, &tcpsock->peername,
						ob->segment, ob->size,
						IPPROTO_TCP, sock->options, sock->ifname);
				
				kfree(ob);
			};

			if (bitmap & (1 << 0))
			{
				tcpsock->state = TCP_TERMINATED;
				wantExit = 1;
				break;
			};
		};
	};

	// wait up to 4 minutes, acknowledging any packets if necessary (during a clean exit)
	uint64_t deadline = getNanotime() + 4UL * 60UL * 1000000000UL;
	uint64_t currentTime;
	Semaphore *semsClosing[2] = {&tcpsock->semStop, &tcpsock->semAckOut};
	while ((currentTime = getNanotime()) < deadline)
	{
		uint8_t bitmap = 0;
		semPoll(2, semsClosing, &bitmap, 0, deadline - currentTime);

		if (bitmap & (1 << 0))
		{
			wantExit = 1;
			semsClosing[0] = NULL;
		};
		
		if (bitmap & (1 << 1))
		{
			if (!wantExit)
			{
				semWaitGen(&tcpsock->semAckOut, -1, 0, 0);
	
				TCPOutbound *ob = CreateOutbound(&tcpsock->sockname, &tcpsock->peername, 0);
				TCPSegment *ack = ob->segment;
		
				ack->srcport = srcport;
				ack->dstport = dstport;
				ack->seqno = htonl(tcpsock->nextSeqNo);
				ack->ackno = htonl(tcpsock->nextAckNo);
				ack->dataOffsetNS = 0x50;
				ack->flags = TCP_FIN | TCP_ACK;
				ack->winsz = htons(TCP_BUFFER_SIZE);
				ChecksumOutbound(ob);
		
				sendPacketEx(&tcpsock->sockname, &tcpsock->peername,
						ob->segment, ob->size,
						IPPROTO_TCP, sock->options, sock->ifname);
		
				kfree(ob);
			};
		};
	};

	// wait for the socket to actually be closed by the application (in case it wasn't already)
	while (semWaitGen(&tcpsock->semSendFetch, 512, 0, 0) != 0);
	
	// free the port and socket
	FreePort(srcport);
	FreeSocket(sock);
};