AF_INET = 2
SOCK_STREAM = 1
RECEIVED_BUFFER_SIZE = 1048576  # todo discuss the buffer size, currently 1GMb

import os
import random
import threading
import time
from pprint import pprint
import IP
from Exceptions import *
from TCP import *

buffer_list = {
    'listening': {},  # when server is listening, the key of the dict inside is the port of server.
    'connected': {
        'objects': {}
    # objcts is a dict that stores every connected tcpSocket object as its value while their remote_address is the key
    },  # when sockey (client or user) is connected, the key of the dict inside is the address of remote address
    'connecting': {},  # when client is connecting, put self here, the remote address is key, list is value
}

port_list = []
local_ip = None


class TCPsocket():
    # status
    CLOSED = 0
    LISTEN = 1
    SYN_RCVD = 2
    ESTABLISHED = 3
    CLOSE_WAIT = 4
    LAST_ACK = 5
    SYN_SENT = 6
    FIN_WAIT_1 = 7
    FIN_WAIT_2 = 8
    TIME_WAIT = 9

    # const
    TIME_INTERVAL = 100

    def __init__(self, local_address, remote_address=None, server_isn=None, client_isn=None, sample_RTT = None):
        self.__sent_but_not_acked = []  # a list of tuple, in the order of sending order. Each tuple contain a wanting ack number and data(NOT TCP OBJECT)
        self.__sample_RTT_to_record = {}  # a dict, the key of which is the wanting ack number, the value of which is the start_time (or None, indicating this message won’t count to sample_RTT).
        self.__sample_RTT = sample_RTT  # double
        self.__estimated_RTT = sample_RTT  # double
        self.__dev_RTT = 0  # double
        self.__window_buffer = []  # a list of tuples, a tuple is in the format of (sequence_number, data)
        self.__received_buffer = b''  # bytes, used to store complete infomation, which is also in order.
        self.__ack_to_be_sent = []
        self.__window_size = 5000
        self.__segment_size = 500
        self.__local_address = local_address
        self.__is_time_out = False  # init is_timeout
        self.__send_buffer = b''
        self.__time_out = 0  # todo init timeout
        self.__timer = threading.Thread()
        self.__timer_pid = 0  # helper variable for timer
        self.__duplicate_ack = 0  # init duplicate ack number
        local_ip = local_address[0]

        if server_isn:  # if the socket is build by server
            # pass sequence number and last_acked_number
            self.__next_sequence_number = server_isn
            self.__last_ack_received = server_isn  # SendBase
            self.__last_acked_sent = client_isn

            # init remote address and set up ip layer
            self.__remote_address = remote_address
            self.__ip = IP.IP(IP.PROTOCOL_TCP, self.__local_address[0], self.__remote_address[0])

            # start sending process
            self.__sending_process = threading.Thread(target=self._sending_thread)
            self.__sending_process.start()

            self.__time_out = self._get_new_timeout()




        else:
            self.__next_sequence_number = random.randint(0, 2147483645)

    def listen(self, backlog=None):
        """
                        listen([backlog])

                        Enable a server to accept connections.  If backlog is specified, it must be
                        at least 0 (if it is lower, it is set to 0); it specifies the number of
                        unaccepted connections that the system will allow before refusing new
                        connections. If not specified, a default reasonable value is chosen.
                        """
        if not self.__local_address:
            raise AddressNotSpecified("Did you bind address for this socket?")
        if self.__local_address[1] in buffer_list.keys():
            raise PortAlreadyInUse("Port is already in use.")

        # print('start listening')
        # open buffer for listening
        buffer_list['listening'][self.__local_address[1]] = {'queue': []}
        # print('create buffer_list')
        # print(buffer_list)

        # set up ip layer
        self.__ip = IP.IP(IP.PROTOCOL_TCP, self.__local_address[0])

    def accept(self):
        """accept() -> address tuple, server_isn int

        Wait for an incoming connection.  Return a new socket
        representing the connection, and the address of the client.
        For IP sockets, the address info is a pair (hostaddr, port).
        """
        # fd, addr = self._accept()
        # If our type has the SOCK_NONBLOCK flag, we shouldn't pass it onto the
        # new socket. We do not currently allow passing SOCK_NONBLOCK to
        # accept4, so the returned socket is always blocking.
        # type = self.type & ~globals().get("SOCK_NONBLOCK", 0)
        # sock = socket(self.family, type, self.proto, fileno=fd)
        # Issue #7995: if no default timeout is set and the listening
        # socket had a (non-zero) timeout, force the new socket in blocking
        # mode to override platform-specific socket flags inheritance.
        # if getdefaulttimeout() is None and self.gettimeout():
        #     sock.setblocking(True)
        # return address, server_isn

        # if not self.__address:
        #     raise AddressNotSpecified("Did you bind address for this socket?")

        # wait until one connected
        while buffer_list['listening'][self.__local_address[1]]['queue'] == []:
            continue

        # retrive first handshake
        data, (remote_ip, remote_port) = buffer_list['listening'][self.__local_address[1]]['queue'].pop()
        tcp = TCP()
        tcp.from_bytes(data)
        if not (tcp.SYN == 1 and tcp.ACK == 0):
            # print("wrong tcp package received, it's not the first handshake")
            return
        client_isn = tcp.sequence_number
        print('first handshake received')

        # reformat remote_address
        address = (remote_ip, tcp.src_port)

        # generate a inital server_isn
        server_isn = random.randint(0, 2147483645)

        # build a tcp with server_isn and client_isn + 1
        tcp = TCP()
        tcp.build(type=tcp.SEND_SYNACK,
                  src_port=self.__local_address[1],
                  dst_port=address[1],
                  sequence_number=server_isn,
                  acknowledgement_number=client_isn + 1)

        # send the second handshake and register a place for handshake
        tmp_ip = IP.IP(IP.PROTOCOL_TCP, self.__local_address[0], remote_ip)
        tmp_ip.send(bytes(tcp))
        buffer_list['connecting'][address] = []
        print('second handshake sent, waiting for the third handshake')

        # record the first time of send package
        fisrt_pacakge_sent_time = time.time()

        # wait until third handshake appear
        send_send_start_time = time.time()
        flag_3 = True
        flag_6 = False
        flag_12 = False
        while buffer_list['connecting'][address] == []:
            if flag_3 and (time.time() - send_send_start_time >= 2):
                print('waiting second hand time out, wait another 6s')
                tmp_ip.send(bytes(tcp))
                send_send_start_time = time.time()
                flag_3 = False
                flag_6 = True
                send_send_start_time =time.time()
            elif flag_6 and (time.time() - send_send_start_time >= 2):
                print('waiting second hand time out, wait another 12s')
                tmp_ip.send(bytes(tcp))
                send_send_start_time = time.time()
                flag_6 = False
                flag_12 = True
                send_send_start_time =time.time()
            elif flag_12 and (time.time() - send_send_start_time >= 2):
                print('waiting second hand time out, wait another 24s')
                tmp_ip.send(bytes(tcp))
                flag_6 = False
                flag_12 = False
                send_send_start_time =time.time()
            elif (time.time() - send_send_start_time >= 4):
                print('waiting second hand time out, wait another 6s')
                print('break')
                return

            continue

        # record the time of receiving second package
        self.__sample_RTT = time.time() - fisrt_pacakge_sent_time
        self.__estimated_RTT = self.__sample_RTT
        print('first sample RTT generated, it\'s {}'.format(self.__sample_RTT))

        # retrive the third handshake
        data, address = buffer_list['connecting'][address].pop()
        tcp = TCP()
        tcp.from_bytes(data)
        print('third handshake retrived')

        # check third handshake
        if not (tcp.sequence_number == client_isn + 1 and tcp.acknowledgement_number == server_isn + 1):
            print(
                'SYN {}, ACK{}, tcp.sequencenumber({}) ?= client_isn({}), tcp.acknowledgement_number({}) ?= server_isn({})'.format(
                    tcp.SYN, tcp.ACK, tcp.sequence_number, client_isn + 1, tcp.acknowledgement_number, server_isn + 1))
            print("wrong tcp package received, it's not the correct third handshake")
            return

        # update server_isn
        server_isn += 1

        # open a place for the newly connected socket
        buffer_list['connected'][address] = []

        # delete the original space
        buffer_list['connecting'].pop(address)

        # add data to the buffer if any

        # buffer_list['connected'][address].append(tcp.data)

        # Build a new tcpSocket
        tcpSocket = TCPsocket(self.__local_address, address, server_isn, client_isn + 1, self.__sample_RTT)

        # put the conneceted object in buffer
        buffer_list['connected']['objects'][address] = tcpSocket

        # add data if any
        if tcp.data != b'':
            tcpSocket._add_data(tcp.data)
        print('done with accept, returned address and server_isn')

        return address, tcpSocket

    def recv(self, buffersize, flags=None):  # real signature unknown; restored from __doc__
        """
        recv(buffersize[, flags]) -> data

        Receive up to buffersize bytes from the socket.  For the optional flags
        argument, see the Unix manual.  When no data is available, block until
        at least one byte is available or until the remote end is closed.  When
        the remote end is closed and all data is read, return the empty string.
        """
        while not self.__received_buffer:
            continue

        return_data = self.__received_buffer[:buffersize]
        self.__received_buffer = self.__received_buffer[buffersize:]
        return return_data

    def send(self, data, flags=None):  # real signature unknown; restored from __doc__
        """
                send(data[, flags]) -> count

                Send a data string to the socket.  For the optional flags
                argument, see the Unix manual.  Return the number of bytes
                sent; this may be less than len(data) if the network is busy.
                """

        # if self.__type != SOCK_STREAM:
        #     raise TypeNotRightException("type is not correct, is the socket assigned TCP protocol?")
        # NextSeqNum: int = 0

        self.__send_buffer += data

    def _sending_thread(self):
        # build empty tcp object
        tcp = TCP()
        tcp.build(type=tcp.SEND_DATA, src_port=self.__local_address[1], dst_port=self.__remote_address[1])
        tcp.sequence_number = self.__next_sequence_number
        tcp.acknowledgement_number = self.__last_acked_sent
        print('built empty tcp object')

        while 1:
            time.sleep(0.2)
            # print('sending thread begin')
            # detect whether there is act_to_be_sent
            if self.__ack_to_be_sent != []:
                tcp.ACK = 1
                tcp.acknowledgement_number = self.__ack_to_be_sent.pop()
                self.__last_acked_sent = tcp.acknowledgement_number
                print(
                    'detect there is ack_to_be_sent({}), added to current tcp object, last_acked_number updated'.format(
                        tcp.acknowledgement_number))

            # check time_out
            if self.__is_time_out:
                print('detect time out')

                # get first send_but_not_acked data in tcp.data
                try:
                    tcp_bytes = self.__sent_but_not_acked[0]


                    tcp = TCP()
                    tcp.from_bytes(tcp_bytes)

                    # modify tcp.sequence_number
                    # tcp.sequence_number = waiting_ack_number - len(tcp.data)

                    # double timeout
                    self.__time_out *= 2

                    if self.__time_out <= 1:
                        self.__time_out = 1

                    # cancel SampleRTT recording for this object
                    self.__sample_RTT_to_record[tcp.sequence_number + len(tcp.data)] = None

                    # Done with here
                    self.__is_time_out = False

                    self.__timer = threading.Thread()
                    self.__timer_pid = None

                    print(self.__time_out)
                except:
                    self.__is_time_out = False

                    self.__timer = threading.Thread()
                    self.__timer_pid = None
            else:
                # print('no timeout detected')

                # calculate the spare room in sent_but_not_acked
                spare_room_in_window = self.__window_size - (self.__next_sequence_number - self.__last_ack_received)

                if self.__send_buffer != b'' and spare_room_in_window != 0:  # specify squence number andNextSeqNum
                    NextSeqNum = self.__next_sequence_number

                    # prepare data
                    data = self.__send_buffer[:self.__segment_size]

                    # delete that data from send_buffer
                    self.__send_buffer = self.__send_buffer[self.__segment_size:]

                    # update tcp object
                    tcp.data = data
                    tcp.sequence_number = NextSeqNum

            # check tcp modified
            if tcp.data != b'' or tcp.ACK == 1:
                # set sequence number
                # tcp.sequence_number = self.__next_sequence_number

                # if data included, update next_sequence_number
                if tcp.data != b'':
                    self.__next_sequence_number += len(data)





                # if the tcp contains data
                if tcp.data != b'':
                    # check the data is first sent or not

                    # add current value to sent_but_not_acked
                    if bytes(tcp) not in self.__sent_but_not_acked:
                        self.__sent_but_not_acked.append(bytes(tcp))

                    if (tcp.sequence_number + len(tcp.data)) not in self.__sample_RTT_to_record.keys():
                        # it's first sent.

                        # record send time
                        send_time = time.time()
                        self.__sample_RTT_to_record[tcp.sequence_number + len(tcp.data)] = send_time
                        # print('----------')
                        # print('record time')
                        # print(self.__sample_RTT_to_record)
                        # print('----------')

                        # check whether there is already a tiemr
                        print('check timer', self.__timer.is_alive())
                        if not self.__timer.is_alive():
                            # there is no timer

                            # calculate a new time_out
                            self.__time_out = self._get_new_timeout()

                            # start a new timer
                            self.__timer = threading.Thread(target=self.__check_time,
                                                            args=(time.time(),
                                                                  self.__time_out))
                            self.__timer.start()
                    else:
                        # it's not first sent

                        # double timeout
                        print('detect not first send message, the sequence_number is {}, the ack_number is {}, content:{}'.format(tcp.sequence_number, tcp.acknowledgement_number, str(tcp)))
                        print(self.__sample_RTT_to_record)
                        self.__time_out *= 2
                        self.__timer = threading.Thread(target=self.__check_time,
                                                        args=(time.time(),
                                                              self.__time_out))
                        self.__timer.start()

                # send tcp object
                self.__ip.send(bytes(tcp))
                print(
                    'send tcp object with \tsequence number {} and \tacknowledge number {}.'.format(tcp.sequence_number,
                                                                                                    tcp.acknowledgement_number))
                print('content', str(tcp))

                # build new tcp object
                tcp = TCP()
                tcp.build(type=tcp.SEND_DATA, src_port=self.__local_address[1], dst_port=self.__remote_address[1])
                tcp.sequence_number = self.__next_sequence_number
                tcp.acknowledgement_number = self.__last_acked_sent
                # print('built empty tcp object')

    def _add_data(self, data):
        tcp = TCP()
        tcp.from_bytes(data)
        # print('retrived data from IP layer')

        # if has ack info
        if tcp.ACK == 1:
            # print("detect ACK info, it's", tcp.acknowledgement_number)
            if tcp.acknowledgement_number == self.__last_ack_received:
                # it's duplicated ack
                self.__duplicate_ack += 1
                print('detect {} duplicated ACK'.format(self.__duplicate_ack))
                if self.__duplicate_ack >= 3:
                    # fast retransmission

                    # stop timer and make timeout to be true
                    self.__timer = threading.Thread()
                    self.__timer_pid = None
                    self.__is_time_out = True
                    print('timer stoped, set timeout to be true, preparing for retransmission')
                    self.__duplicate_ack = 0
            else:
                self.__duplicate_ack = 0
                # it's not duplicated ack
                if tcp.acknowledgement_number > self.__last_ack_received:

                    print('current SendBase {}, updated to {}'.format(self.__last_ack_received,
                                                                      tcp.acknowledgement_number))

                    # update SendBase
                    self.__last_ack_received = tcp.acknowledgement_number
                    self.__next_sequence_number = tcp.acknowledgement_number


                    # calculating a new SampleRTT
                    # print('----------')
                    # print('receive time')
                    # print(self.__sample_RTT_to_record)
                    # print('tcp info:')
                    # print('sequence_number:{}, acknowledgement_number:{}, content:{}'.format(tcp.sequence_number, tcp.acknowledgement_number, str(tcp)))
                    # print('----------')
                    try:
                        self.__sample_RTT = time.time() - self.__sample_RTT_to_record[tcp.acknowledgement_number]
                    except:
                        pass

                    # remove self.__send_but_not_acked objects according to the ack number
                    print('updating sent_but_not_acked list')
                    remove_list = []
                    for tcp_bytes in self.__sent_but_not_acked:
                        tcp_ = TCP()
                        tcp_.from_bytes(tcp_bytes)
                        if tcp_.sequence_number + len(tcp_.data)<= tcp.acknowledgement_number:
                            remove_list.append(tcp_bytes)
                            print('removed waiting_ack_number:{}'.format(tcp_.sequence_number + len(tcp_.data)))
                    for item in remove_list:
                        self.__sent_but_not_acked.remove(item)
                    # print('updated')

                    # check whether a timer is running
                    if self.__timer.is_alive():
                        print('detect a timer still running')

                        # check whether there are still sent_but_not_acked
                        if self.__sent_but_not_acked:
                            print('detect there is still sent_but_not_acked:')
                            print(self.__sent_but_not_acked)
                            print('restart timer')
                            self.__time_out = self._get_new_timeout()
                            # restart timer
                            self.__timer = threading.Thread(target=self.__check_time,
                                                            args=(time.time(), self.__time_out))
                            self.__timer.start()
                        else:
                            # stop timer
                            self.__timer = threading.Thread()
                            self.__timer_pid = None
                            self.__is_time_out = False
                            self.__time_out = self._get_new_timeout()
                            print('no data in sent_but_note_acked, stopped timer')

        # if has data info:
        if tcp.data != b'':
            # check whether it's duplicate data
            if tcp.sequence_number < self.__last_acked_sent:
                print('the sequence_number({}) < last_acked_sent({}), omit it.'.format(tcp.sequence_number,
                                                                                       self.__last_acked_sent))
                # it's duplicate data
                tcp = TCP()
                tcp.build(type = tcp.SEND_ACK, src_port=self.__local_address[1], dst_port=self.__remote_address[1], acknowledgement_number=self.__last_acked_sent)
                print('duplicate data, send ack')
            else:
                # it's not duplicate data

                # put it in self.__window_buffer and sort
                print(tcp.data, 'has added to window buffer')
                self.__window_buffer.append((tcp.sequence_number, tcp.data))
                self.__window_buffer.sort(key=lambda x: x[0])

                # check tmp_buffer in-order data, if any, put it to recv_buffer

                while self.__window_buffer[0][0] == self.__last_acked_sent:
                    # retrive from window_buffer(tmp_buffer)
                    sequence_number, data = self.__window_buffer.pop()

                    # calculate and update last_ack_sent
                    self.__last_acked_sent += len(data)


                    # put data into recv_buffer
                    self.__received_buffer += data
                    print(
                        'put data with sequence_number {} out of tmp_buffer into recv_buffer, updated last_ack_sent, waiting to be sent later'.format(tcp.sequence_number))
                    if len(self.__window_buffer) == 0:
                        break

                # put last_ack_sent to ack_to_be_sent
                self.__ack_to_be_sent.append(self.__last_acked_sent)
                print('not duplicate, send ack', self.__last_acked_sent)


    def connect(self, remote_address):  # real signature unknown; restored from __doc__
        """
        connect(address)

        Connect the socket to a remote address.  For IP sockets, the address
        is a pair (host, port).
        """

        # init remote_address
        self.__remote_address = remote_address

        # connect to IP layer
        print('connecting to IP layer')
        self.__ip = IP.IP(IP.PROTOCOL_TCP, self.__local_address[0], dst_ip=remote_address[0])
        print('connected to IP layer')

        # generate client_isn
        self.__isn = random.randint(0, 2147483645)
        client_isn = self.__isn
        print('generated isn', self.__isn)

        # build a tcp object with SYN
        tcp = TCP()
        tcp.build(type=tcp.SEND_SYN,
                  src_port=self.__local_address[1],
                  dst_port=remote_address[1],
                  sequence_number=self.__isn)

        # sign a space in buffer_list
        buffer_list['connecting'][remote_address] = []

        # sent tcp object
        self.__ip.send(bytes(tcp))
        print('sent tcp object')

        # record first_package_sent_time
        first_package_sent_time = time.time()

        # wait for sencond hanshake
        print('waiting for sencond hanshake')
        star_time = time.time()
        flag_3 = True
        flag_6 = False
        flag_12 = False

        while buffer_list['connecting'][remote_address] == []:
            if flag_3 and time.time() - star_time >= 2:
                print('3s timeout')
                self.__ip.send(bytes(tcp))
                flag_3 = False
                flag_6 = True
                star_time = time.time()
            elif flag_6 and time.time() - star_time >= 2:
                print('6s timeout')
                self.__ip.send(bytes(tcp))
                flag_6 = False
                flag_12 = True
                star_time = time.time()
            elif flag_12 and time.time() - star_time >= 2:
                print('12s timeout')
                self.__ip.send(bytes(tcp))
                flag_12 = False
                star_time = time.time()
            elif time.time() - star_time >= 4:
                print('break')
                return
            continue

        self.status = 'established'
        # record first_package_receive_time
        self.__sample_RTT = time.time() - first_package_sent_time
        self.__estimated_RTT = self.__sample_RTT
        self._get_new_timeout()
        print('fisrt sampleRTT inited, it\'s', self.__sample_RTT)

        # retrive data
        data = buffer_list['connecting'][remote_address].pop()
        print('retrived')

        # parse tcp object
        tcp = TCP()
        tcp.from_bytes(data)

        # check tcp object is right
        if not (tcp.SYN == 1 and tcp.ACK == 1 and tcp.acknowledgement_number == client_isn + 1):
            print('the tcp object is not right. Connect failed')
            return

        # if it's right, update server_isn, client_isn
        server_isn = tcp.sequence_number
        client_isn += 1
        self.__next_sequence_number = client_isn
        print('client_isn sent', client_isn)
        self.__last_ack_received = tcp.acknowledgement_number

        # remove from buffer_list['connecting'], added to buffer_list['connected']
        buffer_list['connecting'].pop(remote_address)
        buffer_list['connected']['objects'][remote_address] = self

        # generate last_ack_sent and update ack_to_be_sent list, last_ack_received
        self.__last_acked_sent = server_isn + 1
        self.__ack_to_be_sent.append(self.__last_acked_sent)

        # start sending thread
        self.__sending_process = threading.Thread(target=self._sending_thread)
        self.__sending_process.start()
        print('connected')



    @staticmethod
    def push(data, remote_ip):
        # print
        # print('[static method]received data from ip, it\'s from', remote_ip)
        tcp = TCP()
        tcp.from_bytes(data)
        # print('current buffer_list is')
        # print(buffer_list)

        # print('src_port is', tcp.src_port, end=' ')
        # print('dst_port is', tcp.dst_port, end=' ')
        # print('content is', str(tcp))

        # print()
        # print basic info
        # names = ['CWR','ECE','URG','ACK','PSH','RST','SYN','FIN']
        # byte = tcp.flag_bits
        # byte = bin(int.from_bytes(byte, 'little'))[2:]
        # print(bytes)
        # for i in range(8):
        #     print("{}:{}".format(names[i], byte[i]))
        #
        # if tcp.SYN == 1 and tcp.ACK != 1:
        #     b
        remote_address = (remote_ip, tcp.src_port)
        if tcp.RST:
            raise ConnectionResetError
        try:
            if tcp.SYN == 1 and tcp.ACK == 1:
                print('detect second handshake')
                try:
                    buffer_list['connecting'][remote_address].append(data)
                except:
                    server_isn = tcp.sequence_number
                    client_isn = tcp.acknowledgement_number
                    remote_port = tcp.src_port
                    local_port = tcp.dst_port
                    tcp = TCP()
                    tcp.sequence_number = client_isn
                    tcp.acknowledgement_number = server_isn
                    tcp.src_port = local_port
                    tcp.dst_port = remote_port
                    tmp_ip = IP.IP(protocol=IP.PROTOCOL_TCP, src_ip=local_ip, dst_ip=remote_ip)
                    print('retransmitted', tcp.sequence_number, tcp.acknowledge_number)
                    print("str", str(tcp))
                    tmp_ip.send(bytes(tcp))

            elif tcp.SYN == 1:
                # it's first handshake
                print('detect first handshake')
                buffer_list['listening'][tcp.dst_port]['queue'].append((data, remote_address))
            else:
                # it's not first handshake

                # self.__last_acked_number = tcp.acknowledgement_number # todo update last_acked_number

                # check whether it's a third handshake
                if remote_address in buffer_list['connected']['objects'].keys():
                    print('detect normal message')
                    # it's not a third handshake
                    # buffer_list['connected'][remote_address].append((data, remote_address))

                    # get tcp object
                    obj = buffer_list['connected']['objects'][remote_address]

                    # let obj add data
                    obj._add_data(data)
                else:
                    # it's a third handshake
                    print('detect third handshake')
                    buffer_list['connecting'][remote_address].append((data, remote_address))
        except:
            local_port = tcp.dst_port
            remote_port = tcp.src_port
            sequence_number = tcp.acknowledgement_number
            acknowledge_number = tcp.sequence_number
            tcp = TCP()
            tcp.RST = 1
            tcp.ACK = 0
            tcp.src_port = local_port
            tcp.dst_port = remote_port
            tcp.sequence_number = sequence_number
            tcp.acknowledgement_number = acknowledge_number
            tmp_ip = IP.IP(protocol=IP.PROTOCOL_TCP, src_ip= local_ip, dst_ip=remote_ip)
            tmp_ip.send(bytes(tcp))


        # print()


    def _get_new_timeout(self):  # todo calculate a new time out
        if not self.__estimated_RTT:
            self.__estimated_RTT = self.__sample_RTT
        if not self.__dev_RTT:
            self.__dev_RTT = 0
        self.__estimated_RTT = 0.85 * self.__estimated_RTT + 0.125 * self.__sample_RTT
        self.__dev_RTT = 0.75 * self.__dev_RTT + 0.25 * abs(self.__sample_RTT - self.__estimated_RTT)
        self.__time_out = self.__estimated_RTT + self.__dev_RTT
        if self.__time_out == 0:
            self.__time_out = 1
        return self.__time_out


    def __check_time(self, start_time, time_interval):
        print('timer start')
        self.__timer_pid = os.getpid()
        pid = os.getpid()
        while 1:
            # print('ticking', time.time(), start_time, time_interval)
            time.sleep(time_interval * 0.1)
            if self.__timer_pid != pid:
                return
            current_time = time.time()
            if current_time - start_time > time_interval:
                self.__is_time_out = True
                self.timer_thread = threading.Thread()
                return

    def close(self):
        try:
            buffer_list['listening'].pop(self.__local_address[1])
        except:
            pass
        try:
            buffer_list['connecting'].pop(self.__remote_address)
        except:
            pass
        try:
            buffer_list['connected']['objects'][self.__remote_address]
        except:
            pass


    def __del__(self):
        pass
AF_INET = 2
SOCK_STREAM = 1
RECEIVED_BUFFER_SIZE = 1048576  # todo discuss the buffer size, currently 1GMb

import os
import random
import threading
import time
from pprint import pprint
import IP
from Exceptions import *
from TCP import *

buffer_list = {
    'listening': {},  # when server is listening, the key of the dict inside is the port of server.
    'connected': {
        'objects': {}
    # objcts is a dict that stores every connected tcpSocket object as its value while their remote_address is the key
    },  # when sockey (client or user) is connected, the key of the dict inside is the address of remote address
    'connecting': {},  # when client is connecting, put self here, the remote address is key, list is value
}

port_list = []
local_ip = None


class TCPsocket():
    # status
    CLOSED = 0
    LISTEN = 1
    SYN_RCVD = 2
    ESTABLISHED = 3
    CLOSE_WAIT = 4
    LAST_ACK = 5
    SYN_SENT = 6
    FIN_WAIT_1 = 7
    FIN_WAIT_2 = 8
    TIME_WAIT = 9

    # const
    TIME_INTERVAL = 100

    def __init__(self, local_address, remote_address=None, server_isn=None, client_isn=None, sample_RTT = None):
        self.__sent_but_not_acked = []  # a list of tuple, in the order of sending order. Each tuple contain a wanting ack number and data(NOT TCP OBJECT)
        self.__sample_RTT_to_record = {}  # a dict, the key of which is the wanting ack number, the value of which is the start_time (or None, indicating this message won’t count to sample_RTT).
        self.__sample_RTT = sample_RTT  # double
        self.__estimated_RTT = sample_RTT  # double
        self.__dev_RTT = 0  # double
        self.__window_buffer = []  # a list of tuples, a tuple is in the format of (sequence_number, data)
        self.__received_buffer = b''  # bytes, used to store complete infomation, which is also in order.
        self.__ack_to_be_sent = []
        self.__window_size = 5000
        self.__segment_size = 500
        self.__local_address = local_address
        self.__is_time_out = False  # init is_timeout
        self.__send_buffer = b''
        self.__time_out = 0  # todo init timeout
        self.__timer = threading.Thread()
        self.__timer_pid = 0  # helper variable for timer
        self.__duplicate_ack = 0  # init duplicate ack number
        local_ip = local_address[0]

        if server_isn:  # if the socket is build by server
            # pass sequence number and last_acked_number
            self.__next_sequence_number = server_isn
            self.__last_ack_received = server_isn  # SendBase
            self.__last_acked_sent = client_isn

            # init remote address and set up ip layer
            self.__remote_address = remote_address
            self.__ip = IP.IP(IP.PROTOCOL_TCP, self.__local_address[0], self.__remote_address[0])

            # start sending process
            self.__sending_process = threading.Thread(target=self._sending_thread)
            self.__sending_process.start()

            self.__time_out = self._get_new_timeout()




        else:
            self.__next_sequence_number = random.randint(0, 2147483645)

    def listen(self, backlog=None):
        """
                        listen([backlog])

                        Enable a server to accept connections.  If backlog is specified, it must be
                        at least 0 (if it is lower, it is set to 0); it specifies the number of
                        unaccepted connections that the system will allow before refusing new
                        connections. If not specified, a default reasonable value is chosen.
                        """
        if not self.__local_address:
            raise AddressNotSpecified("Did you bind address for this socket?")
        if self.__local_address[1] in buffer_list.keys():
            raise PortAlreadyInUse("Port is already in use.")

        # print('start listening')
        # open buffer for listening
        buffer_list['listening'][self.__local_address[1]] = {'queue': []}
        # print('create buffer_list')
        # print(buffer_list)

        # set up ip layer
        self.__ip = IP.IP(IP.PROTOCOL_TCP, self.__local_address[0])

    def accept(self):
        """accept() -> address tuple, server_isn int

        Wait for an incoming connection.  Return a new socket
        representing the connection, and the address of the client.
        For IP sockets, the address info is a pair (hostaddr, port).
        """
        # fd, addr = self._accept()
        # If our type has the SOCK_NONBLOCK flag, we shouldn't pass it onto the
        # new socket. We do not currently allow passing SOCK_NONBLOCK to
        # accept4, so the returned socket is always blocking.
        # type = self.type & ~globals().get("SOCK_NONBLOCK", 0)
        # sock = socket(self.family, type, self.proto, fileno=fd)
        # Issue #7995: if no default timeout is set and the listening
        # socket had a (non-zero) timeout, force the new socket in blocking
        # mode to override platform-specific socket flags inheritance.
        # if getdefaulttimeout() is None and self.gettimeout():
        #     sock.setblocking(True)
        # return address, server_isn

        # if not self.__address:
        #     raise AddressNotSpecified("Did you bind address for this socket?")

        # wait until one connected
        while buffer_list['listening'][self.__local_address[1]]['queue'] == []:
            continue

        # retrive first handshake
        data, (remote_ip, remote_port) = buffer_list['listening'][self.__local_address[1]]['queue'].pop()
        tcp = TCP()
        tcp.from_bytes(data)
        if not (tcp.SYN == 1 and tcp.ACK == 0):
            # print("wrong tcp package received, it's not the first handshake")
            return
        client_isn = tcp.sequence_number
        print('first handshake received')

        # reformat remote_address
        address = (remote_ip, tcp.src_port)

        # generate a inital server_isn
        server_isn = random.randint(0, 2147483645)

        # build a tcp with server_isn and client_isn + 1
        tcp = TCP()
        tcp.build(type=tcp.SEND_SYNACK,
                  src_port=self.__local_address[1],
                  dst_port=address[1],
                  sequence_number=server_isn,
                  acknowledgement_number=client_isn + 1)

        # send the second handshake and register a place for handshake
        tmp_ip = IP.IP(IP.PROTOCOL_TCP, self.__local_address[0], remote_ip)
        tmp_ip.send(bytes(tcp))
        buffer_list['connecting'][address] = []
        print('second handshake sent, waiting for the third handshake')

        # record the first time of send package
        fisrt_pacakge_sent_time = time.time()

        # wait until third handshake appear
        send_send_start_time = time.time()
        flag_3 = True
        flag_6 = False
        flag_12 = False
        while buffer_list['connecting'][address] == []:
            if flag_3 and (time.time() - send_send_start_time >= 2):
                print('waiting second hand time out, wait another 6s')
                tmp_ip.send(bytes(tcp))
                send_send_start_time = time.time()
                flag_3 = False
                flag_6 = True
                send_send_start_time =time.time()
            elif flag_6 and (time.time() - send_send_start_time >= 2):
                print('waiting second hand time out, wait another 12s')
                tmp_ip.send(bytes(tcp))
                send_send_start_time = time.time()
                flag_6 = False
                flag_12 = True
                send_send_start_time =time.time()
            elif flag_12 and (time.time() - send_send_start_time >= 2):
                print('waiting second hand time out, wait another 24s')
                tmp_ip.send(bytes(tcp))
                flag_6 = False
                flag_12 = False
                send_send_start_time =time.time()
            elif (time.time() - send_send_start_time >= 4):
                print('waiting second hand time out, wait another 6s')
                print('break')
                return

            continue

        # record the time of receiving second package
        self.__sample_RTT = time.time() - fisrt_pacakge_sent_time
        self.__estimated_RTT = self.__sample_RTT
        print('first sample RTT generated, it\'s {}'.format(self.__sample_RTT))

        # retrive the third handshake
        data, address = buffer_list['connecting'][address].pop()
        tcp = TCP()
        tcp.from_bytes(data)
        print('third handshake retrived')

        # check third handshake
        if not (tcp.sequence_number == client_isn + 1 and tcp.acknowledgement_number == server_isn + 1):
            print(
                'SYN {}, ACK{}, tcp.sequencenumber({}) ?= client_isn({}), tcp.acknowledgement_number({}) ?= server_isn({})'.format(
                    tcp.SYN, tcp.ACK, tcp.sequence_number, client_isn + 1, tcp.acknowledgement_number, server_isn + 1))
            print("wrong tcp package received, it's not the correct third handshake")
            return

        # update server_isn
        server_isn += 1

        # open a place for the newly connected socket
        buffer_list['connected'][address] = []

        # delete the original space
        buffer_list['connecting'].pop(address)

        # add data to the buffer if any

        # buffer_list['connected'][address].append(tcp.data)

        # Build a new tcpSocket
        tcpSocket = TCPsocket(self.__local_address, address, server_isn, client_isn + 1, self.__sample_RTT)

        # put the conneceted object in buffer
        buffer_list['connected']['objects'][address] = tcpSocket

        # add data if any
        if tcp.data != b'':
            tcpSocket._add_data(tcp.data)
        print('done with accept, returned address and server_isn')

        return address, tcpSocket

    def recv(self, buffersize, flags=None):  # real signature unknown; restored from __doc__
        """
        recv(buffersize[, flags]) -> data

        Receive up to buffersize bytes from the socket.  For the optional flags
        argument, see the Unix manual.  When no data is available, block until
        at least one byte is available or until the remote end is closed.  When
        the remote end is closed and all data is read, return the empty string.
        """
        while not self.__received_buffer:
            continue

        return_data = self.__received_buffer[:buffersize]
        self.__received_buffer = self.__received_buffer[buffersize:]
        return return_data

    def send(self, data, flags=None):  # real signature unknown; restored from __doc__
        """
                send(data[, flags]) -> count

                Send a data string to the socket.  For the optional flags
                argument, see the Unix manual.  Return the number of bytes
                sent; this may be less than len(data) if the network is busy.
                """

        # if self.__type != SOCK_STREAM:
        #     raise TypeNotRightException("type is not correct, is the socket assigned TCP protocol?")
        # NextSeqNum: int = 0

        self.__send_buffer += data

    def _sending_thread(self):
        # build empty tcp object
        tcp = TCP()
        tcp.build(type=tcp.SEND_DATA, src_port=self.__local_address[1], dst_port=self.__remote_address[1])
        tcp.sequence_number = self.__next_sequence_number
        tcp.acknowledgement_number = self.__last_acked_sent
        print('built empty tcp object')

        while 1:
            time.sleep(0.2)
            # print('sending thread begin')
            # detect whether there is act_to_be_sent
            if self.__ack_to_be_sent != []:
                tcp.ACK = 1
                tcp.acknowledgement_number = self.__ack_to_be_sent.pop()
                self.__last_acked_sent = tcp.acknowledgement_number
                print(
                    'detect there is ack_to_be_sent({}), added to current tcp object, last_acked_number updated'.format(
                        tcp.acknowledgement_number))

            # check time_out
            if self.__is_time_out:
                print('detect time out')

                # get first send_but_not_acked data in tcp.data
                try:
                    tcp_bytes = self.__sent_but_not_acked[0]


                    tcp = TCP()
                    tcp.from_bytes(tcp_bytes)

                    # modify tcp.sequence_number
                    # tcp.sequence_number = waiting_ack_number - len(tcp.data)

                    # double timeout
                    self.__time_out *= 2

                    if self.__time_out <= 1:
                        self.__time_out = 1

                    # cancel SampleRTT recording for this object
                    self.__sample_RTT_to_record[tcp.sequence_number + len(tcp.data)] = None

                    # Done with here
                    self.__is_time_out = False

                    self.__timer = threading.Thread()
                    self.__timer_pid = None

                    print(self.__time_out)
                except:
                    self.__is_time_out = False

                    self.__timer = threading.Thread()
                    self.__timer_pid = None
            else:
                # print('no timeout detected')

                # calculate the spare room in sent_but_not_acked
                spare_room_in_window = self.__window_size - (self.__next_sequence_number - self.__last_ack_received)

                if self.__send_buffer != b'' and spare_room_in_window != 0:  # specify squence number andNextSeqNum
                    NextSeqNum = self.__next_sequence_number

                    # prepare data
                    data = self.__send_buffer[:self.__segment_size]

                    # delete that data from send_buffer
                    self.__send_buffer = self.__send_buffer[self.__segment_size:]

                    # update tcp object
                    tcp.data = data
                    tcp.sequence_number = NextSeqNum

            # check tcp modified
            if tcp.data != b'' or tcp.ACK == 1:
                # set sequence number
                # tcp.sequence_number = self.__next_sequence_number

                # if data included, update next_sequence_number
                if tcp.data != b'':
                    self.__next_sequence_number += len(data)





                # if the tcp contains data
                if tcp.data != b'':
                    # check the data is first sent or not

                    # add current value to sent_but_not_acked
                    if bytes(tcp) not in self.__sent_but_not_acked:
                        self.__sent_but_not_acked.append(bytes(tcp))

                    if (tcp.sequence_number + len(tcp.data)) not in self.__sample_RTT_to_record.keys():
                        # it's first sent.

                        # record send time
                        send_time = time.time()
                        self.__sample_RTT_to_record[tcp.sequence_number + len(tcp.data)] = send_time
                        # print('----------')
                        # print('record time')
                        # print(self.__sample_RTT_to_record)
                        # print('----------')

                        # check whether there is already a tiemr
                        print('check timer', self.__timer.is_alive())
                        if not self.__timer.is_alive():
                            # there is no timer

                            # calculate a new time_out
                            self.__time_out = self._get_new_timeout()

                            # start a new timer
                            self.__timer = threading.Thread(target=self.__check_time,
                                                            args=(time.time(),
                                                                  self.__time_out))
                            self.__timer.start()
                    else:
                        # it's not first sent

                        # double timeout
                        print('detect not first send message, the sequence_number is {}, the ack_number is {}, content:{}'.format(tcp.sequence_number, tcp.acknowledgement_number, str(tcp)))
                        print(self.__sample_RTT_to_record)
                        self.__time_out *= 2
                        self.__timer = threading.Thread(target=self.__check_time,
                                                        args=(time.time(),
                                                              self.__time_out))
                        self.__timer.start()

                # send tcp object
                self.__ip.send(bytes(tcp))
                print(
                    'send tcp object with \tsequence number {} and \tacknowledge number {}.'.format(tcp.sequence_number,
                                                                                                    tcp.acknowledgement_number))
                print('content', str(tcp))

                # build new tcp object
                tcp = TCP()
                tcp.build(type=tcp.SEND_DATA, src_port=self.__local_address[1], dst_port=self.__remote_address[1])
                tcp.sequence_number = self.__next_sequence_number
                tcp.acknowledgement_number = self.__last_acked_sent
                # print('built empty tcp object')

    def _add_data(self, data):
        tcp = TCP()
        tcp.from_bytes(data)
        # print('retrived data from IP layer')

        # if has ack info
        if tcp.ACK == 1:
            # print("detect ACK info, it's", tcp.acknowledgement_number)
            if tcp.acknowledgement_number == self.__last_ack_received:
                # it's duplicated ack
                self.__duplicate_ack += 1
                print('detect {} duplicated ACK'.format(self.__duplicate_ack))
                if self.__duplicate_ack >= 3:
                    # fast retransmission

                    # stop timer and make timeout to be true
                    self.__timer = threading.Thread()
                    self.__timer_pid = None
                    self.__is_time_out = True
                    print('timer stoped, set timeout to be true, preparing for retransmission')
                    self.__duplicate_ack = 0
            else:
                self.__duplicate_ack = 0
                # it's not duplicated ack
                if tcp.acknowledgement_number > self.__last_ack_received:

                    print('current SendBase {}, updated to {}'.format(self.__last_ack_received,
                                                                      tcp.acknowledgement_number))

                    # update SendBase
                    self.__last_ack_received = tcp.acknowledgement_number
                    self.__next_sequence_number = tcp.acknowledgement_number


                    # calculating a new SampleRTT
                    # print('----------')
                    # print('receive time')
                    # print(self.__sample_RTT_to_record)
                    # print('tcp info:')
                    # print('sequence_number:{}, acknowledgement_number:{}, content:{}'.format(tcp.sequence_number, tcp.acknowledgement_number, str(tcp)))
                    # print('----------')
                    try:
                        self.__sample_RTT = time.time() - self.__sample_RTT_to_record[tcp.acknowledgement_number]
                    except:
                        pass

                    # remove self.__send_but_not_acked objects according to the ack number
                    print('updating sent_but_not_acked list')
                    remove_list = []
                    for tcp_bytes in self.__sent_but_not_acked:
                        tcp_ = TCP()
                        tcp_.from_bytes(tcp_bytes)
                        if tcp_.sequence_number + len(tcp_.data)<= tcp.acknowledgement_number:
                            remove_list.append(tcp_bytes)
                            print('removed waiting_ack_number:{}'.format(tcp_.sequence_number + len(tcp_.data)))
                    for item in remove_list:
                        self.__sent_but_not_acked.remove(item)
                    # print('updated')

                    # check whether a timer is running
                    if self.__timer.is_alive():
                        print('detect a timer still running')

                        # check whether there are still sent_but_not_acked
                        if self.__sent_but_not_acked:
                            print('detect there is still sent_but_not_acked:')
                            print(self.__sent_but_not_acked)
                            print('restart timer')
                            self.__time_out = self._get_new_timeout()
                            # restart timer
                            self.__timer = threading.Thread(target=self.__check_time,
                                                            args=(time.time(), self.__time_out))
                            self.__timer.start()
                        else:
                            # stop timer
                            self.__timer = threading.Thread()
                            self.__timer_pid = None
                            self.__is_time_out = False
                            self.__time_out = self._get_new_timeout()
                            print('no data in sent_but_note_acked, stopped timer')

        # if has data info:
        if tcp.data != b'':
            # check whether it's duplicate data
            if tcp.sequence_number < self.__last_acked_sent:
                print('the sequence_number({}) < last_acked_sent({}), omit it.'.format(tcp.sequence_number,
                                                                                       self.__last_acked_sent))
                # it's duplicate data
                tcp = TCP()
                tcp.build(type = tcp.SEND_ACK, src_port=self.__local_address[1], dst_port=self.__remote_address[1], acknowledgement_number=self.__last_acked_sent)
                print('duplicate data, send ack')
            else:
                # it's not duplicate data

                # put it in self.__window_buffer and sort
                print(tcp.data, 'has added to window buffer')
                self.__window_buffer.append((tcp.sequence_number, tcp.data))
                self.__window_buffer.sort(key=lambda x: x[0])

                # check tmp_buffer in-order data, if any, put it to recv_buffer

                while self.__window_buffer[0][0] == self.__last_acked_sent:
                    # retrive from window_buffer(tmp_buffer)
                    sequence_number, data = self.__window_buffer.pop()

                    # calculate and update last_ack_sent
                    self.__last_acked_sent += len(data)


                    # put data into recv_buffer
                    self.__received_buffer += data
                    print(
                        'put data with sequence_number {} out of tmp_buffer into recv_buffer, updated last_ack_sent, waiting to be sent later'.format(tcp.sequence_number))
                    if len(self.__window_buffer) == 0:
                        break

                # put last_ack_sent to ack_to_be_sent
                self.__ack_to_be_sent.append(self.__last_acked_sent)
                print('not duplicate, send ack', self.__last_acked_sent)


    def connect(self, remote_address):  # real signature unknown; restored from __doc__
        """
        connect(address)

        Connect the socket to a remote address.  For IP sockets, the address
        is a pair (host, port).
        """

        # init remote_address
        self.__remote_address = remote_address

        # connect to IP layer
        print('connecting to IP layer')
        self.__ip = IP.IP(IP.PROTOCOL_TCP, self.__local_address[0], dst_ip=remote_address[0])
        print('connected to IP layer')

        # generate client_isn
        self.__isn = random.randint(0, 2147483645)
        client_isn = self.__isn
        print('generated isn', self.__isn)

        # build a tcp object with SYN
        tcp = TCP()
        tcp.build(type=tcp.SEND_SYN,
                  src_port=self.__local_address[1],
                  dst_port=remote_address[1],
                  sequence_number=self.__isn)

        # sign a space in buffer_list
        buffer_list['connecting'][remote_address] = []

        # sent tcp object
        self.__ip.send(bytes(tcp))
        print('sent tcp object')

        # record first_package_sent_time
        first_package_sent_time = time.time()

        # wait for sencond hanshake
        print('waiting for sencond hanshake')
        star_time = time.time()
        flag_3 = True
        flag_6 = False
        flag_12 = False

        while buffer_list['connecting'][remote_address] == []:
            if flag_3 and time.time() - star_time >= 2:
                print('3s timeout')
                self.__ip.send(bytes(tcp))
                flag_3 = False
                flag_6 = True
                star_time = time.time()
            elif flag_6 and time.time() - star_time >= 2:
                print('6s timeout')
                self.__ip.send(bytes(tcp))
                flag_6 = False
                flag_12 = True
                star_time = time.time()
            elif flag_12 and time.time() - star_time >= 2:
                print('12s timeout')
                self.__ip.send(bytes(tcp))
                flag_12 = False
                star_time = time.time()
            elif time.time() - star_time >= 4:
                print('break')
                return
            continue

        self.status = 'established'
        # record first_package_receive_time
        self.__sample_RTT = time.time() - first_package_sent_time
        self.__estimated_RTT = self.__sample_RTT
        self._get_new_timeout()
        print('fisrt sampleRTT inited, it\'s', self.__sample_RTT)

        # retrive data
        data = buffer_list['connecting'][remote_address].pop()
        print('retrived')

        # parse tcp object
        tcp = TCP()
        tcp.from_bytes(data)

        # check tcp object is right
        if not (tcp.SYN == 1 and tcp.ACK == 1 and tcp.acknowledgement_number == client_isn + 1):
            print('the tcp object is not right. Connect failed')
            return

        # if it's right, update server_isn, client_isn
        server_isn = tcp.sequence_number
        client_isn += 1
        self.__next_sequence_number = client_isn
        print('client_isn sent', client_isn)
        self.__last_ack_received = tcp.acknowledgement_number

        # remove from buffer_list['connecting'], added to buffer_list['connected']
        buffer_list['connecting'].pop(remote_address)
        buffer_list['connected']['objects'][remote_address] = self

        # generate last_ack_sent and update ack_to_be_sent list, last_ack_received
        self.__last_acked_sent = server_isn + 1
        self.__ack_to_be_sent.append(self.__last_acked_sent)

        # start sending thread
        self.__sending_process = threading.Thread(target=self._sending_thread)
        self.__sending_process.start()
        print('connected')



    @staticmethod
    def push(data, remote_ip):
        # print
        # print('[static method]received data from ip, it\'s from', remote_ip)
        tcp = TCP()
        tcp.from_bytes(data)
        # print('current buffer_list is')
        # print(buffer_list)

        # print('src_port is', tcp.src_port, end=' ')
        # print('dst_port is', tcp.dst_port, end=' ')
        # print('content is', str(tcp))

        # print()
        # print basic info
        # names = ['CWR','ECE','URG','ACK','PSH','RST','SYN','FIN']
        # byte = tcp.flag_bits
        # byte = bin(int.from_bytes(byte, 'little'))[2:]
        # print(bytes)
        # for i in range(8):
        #     print("{}:{}".format(names[i], byte[i]))
        #
        # if tcp.SYN == 1 and tcp.ACK != 1:
        #     b
        remote_address = (remote_ip, tcp.src_port)
        if tcp.RST:
            raise ConnectionResetError
        try:
            if tcp.SYN == 1 and tcp.ACK == 1:
                print('detect second handshake')
                try:
                    buffer_list['connecting'][remote_address].append(data)
                except:
                    server_isn = tcp.sequence_number
                    client_isn = tcp.acknowledgement_number
                    remote_port = tcp.src_port
                    local_port = tcp.dst_port
                    tcp = TCP()
                    tcp.sequence_number = client_isn
                    tcp.acknowledgement_number = server_isn
                    tcp.src_port = local_port
                    tcp.dst_port = remote_port
                    tmp_ip = IP.IP(protocol=IP.PROTOCOL_TCP, src_ip=local_ip, dst_ip=remote_ip)
                    print('retransmitted', tcp.sequence_number, tcp.acknowledge_number)
                    print("str", str(tcp))
                    tmp_ip.send(bytes(tcp))

            elif tcp.SYN == 1:
                # it's first handshake
                print('detect first handshake')
                buffer_list['listening'][tcp.dst_port]['queue'].append((data, remote_address))
            else:
                # it's not first handshake

                # self.__last_acked_number = tcp.acknowledgement_number # todo update last_acked_number

                # check whether it's a third handshake
                if remote_address in buffer_list['connected']['objects'].keys():
                    print('detect normal message')
                    # it's not a third handshake
                    # buffer_list['connected'][remote_address].append((data, remote_address))

                    # get tcp object
                    obj = buffer_list['connected']['objects'][remote_address]

                    # let obj add data
                    obj._add_data(data)
                else:
                    # it's a third handshake
                    print('detect third handshake')
                    buffer_list['connecting'][remote_address].append((data, remote_address))
        except:
            local_port = tcp.dst_port
            remote_port = tcp.src_port
            sequence_number = tcp.acknowledgement_number
            acknowledge_number = tcp.sequence_number
            tcp = TCP()
            tcp.RST = 1
            tcp.ACK = 0
            tcp.src_port = local_port
            tcp.dst_port = remote_port
            tcp.sequence_number = sequence_number
            tcp.acknowledgement_number = acknowledge_number
            tmp_ip = IP.IP(protocol=IP.PROTOCOL_TCP, src_ip= local_ip, dst_ip=remote_ip)
            tmp_ip.send(bytes(tcp))


        # print()


    def _get_new_timeout(self):  # todo calculate a new time out
        if not self.__estimated_RTT:
            self.__estimated_RTT = self.__sample_RTT
        if not self.__dev_RTT:
            self.__dev_RTT = 0
        self.__estimated_RTT = 0.85 * self.__estimated_RTT + 0.125 * self.__sample_RTT
        self.__dev_RTT = 0.75 * self.__dev_RTT + 0.25 * abs(self.__sample_RTT - self.__estimated_RTT)
        self.__time_out = self.__estimated_RTT + self.__dev_RTT
        if self.__time_out == 0:
            self.__time_out = 1
        return self.__time_out


    def __check_time(self, start_time, time_interval):
        print('timer start')
        self.__timer_pid = os.getpid()
        pid = os.getpid()
        while 1:
            # print('ticking', time.time(), start_time, time_interval)
            time.sleep(time_interval * 0.1)
            if self.__timer_pid != pid:
                return
            current_time = time.time()
            if current_time - start_time > time_interval:
                self.__is_time_out = True
                self.timer_thread = threading.Thread()
                return

    def close(self):
        try:
            buffer_list['listening'].pop(self.__local_address[1])
        except:
            pass
        try:
            buffer_list['connecting'].pop(self.__remote_address)
        except:
            pass
        try:
            buffer_list['connected']['objects'][self.__remote_address]
        except:
            pass


    def __del__(self):
        pass
# todo discuss the buffer size, currently 1GMb # when server is listening, the key of the dict inside is the port of server. # objcts is a dict that stores every connected tcpSocket object as its value while their remote_address is the key # when sockey (client or user) is connected, the key of the dict inside is the address of remote address # when client is connecting, put self here, the remote address is key, list is value # status # const # a list of tuple, in the order of sending order. Each tuple contain a wanting ack number and data(NOT TCP OBJECT) # a dict, the key of which is the wanting ack number, the value of which is the start_time (or None, indicating this message won’t count to sample_RTT). # double # double # double # a list of tuples, a tuple is in the format of (sequence_number, data) # bytes, used to store complete infomation, which is also in order. # init is_timeout # todo init timeout # helper variable for timer # init duplicate ack number # if the socket is build by server # pass sequence number and last_acked_number # SendBase # init remote address and set up ip layer # start sending process listen([backlog])                          Enable a server to accept connections.  If backlog is specified, it must be                         at least 0 (if it is lower, it is set to 0); it specifies the number of                         unaccepted connections that the system will allow before refusing new                         connections. If not specified, a default reasonable value is chosen. # print('start listening') # open buffer for listening # print('create buffer_list') # print(buffer_list) # set up ip layer accept() -> address tuple, server_isn int          Wait for an incoming connection.  Return a new socket         representing the connection, and the address of the client.         For IP sockets, the address info is a pair (hostaddr, port). # fd, addr = self._accept() # If our type has the SOCK_NONBLOCK flag, we shouldn't pass it onto the # new socket. We do not currently allow passing SOCK_NONBLOCK to # accept4, so the returned socket is always blocking. # type = self.type & ~globals().get("SOCK_NONBLOCK", 0) # sock = socket(self.family, type, self.proto, fileno=fd) # Issue #7995: if no default timeout is set and the listening # socket had a (non-zero) timeout, force the new socket in blocking # mode to override platform-specific socket flags inheritance. # if getdefaulttimeout() is None and self.gettimeout(): #     sock.setblocking(True) # return address, server_isn # if not self.__address: #     raise AddressNotSpecified("Did you bind address for this socket?") # wait until one connected # retrive first handshake # print("wrong tcp package received, it's not the first handshake") # reformat remote_address # generate a inital server_isn # build a tcp with server_isn and client_isn + 1 # send the second handshake and register a place for handshake # record the first time of send package # wait until third handshake appear # record the time of receiving second package # retrive the third handshake # check third handshake # update server_isn # open a place for the newly connected socket # delete the original space # add data to the buffer if any # buffer_list['connected'][address].append(tcp.data) # Build a new tcpSocket # put the conneceted object in buffer # add data if any # real signature unknown; restored from __doc__ recv(buffersize[, flags]) -> data          Receive up to buffersize bytes from the socket.  For the optional flags         argument, see the Unix manual.  When no data is available, block until         at least one byte is available or until the remote end is closed.  When         the remote end is closed and all data is read, return the empty string. # real signature unknown; restored from __doc__ send(data[, flags]) -> count                  Send a data string to the socket.  For the optional flags                 argument, see the Unix manual.  Return the number of bytes                 sent; this may be less than len(data) if the network is busy. # if self.__type != SOCK_STREAM: #     raise TypeNotRightException("type is not correct, is the socket assigned TCP protocol?") # NextSeqNum: int = 0 # build empty tcp object # print('sending thread begin') # detect whether there is act_to_be_sent # check time_out # get first send_but_not_acked data in tcp.data # modify tcp.sequence_number # tcp.sequence_number = waiting_ack_number - len(tcp.data) # double timeout # cancel SampleRTT recording for this object # Done with here # print('no timeout detected') # calculate the spare room in sent_but_not_acked # specify squence number andNextSeqNum # prepare data # delete that data from send_buffer # update tcp object # check tcp modified # set sequence number # tcp.sequence_number = self.__next_sequence_number # if data included, update next_sequence_number # if the tcp contains data # check the data is first sent or not # add current value to sent_but_not_acked # it's first sent. # record send time # print('----------') # print('record time') # print(self.__sample_RTT_to_record) # print('----------') # check whether there is already a tiemr # there is no timer # calculate a new time_out # start a new timer # it's not first sent # double timeout # send tcp object # build new tcp object # print('built empty tcp object') # print('retrived data from IP layer') # if has ack info # print("detect ACK info, it's", tcp.acknowledgement_number) # it's duplicated ack # fast retransmission # stop timer and make timeout to be true # it's not duplicated ack # update SendBase # calculating a new SampleRTT # print('----------') # print('receive time') # print(self.__sample_RTT_to_record) # print('tcp info:') # print('sequence_number:{}, acknowledgement_number:{}, content:{}'.format(tcp.sequence_number, tcp.acknowledgement_number, str(tcp))) # print('----------') # remove self.__send_but_not_acked objects according to the ack number # print('updated') # check whether a timer is running # check whether there are still sent_but_not_acked # restart timer # stop timer # if has data info: # check whether it's duplicate data # it's duplicate data # it's not duplicate data # put it in self.__window_buffer and sort # check tmp_buffer in-order data, if any, put it to recv_buffer # retrive from window_buffer(tmp_buffer) # calculate and update last_ack_sent # put data into recv_buffer # put last_ack_sent to ack_to_be_sent # real signature unknown; restored from __doc__ connect(address)          Connect the socket to a remote address.  For IP sockets, the address         is a pair (host, port). # init remote_address # connect to IP layer # generate client_isn # build a tcp object with SYN # sign a space in buffer_list # sent tcp object # record first_package_sent_time # wait for sencond hanshake # record first_package_receive_time # retrive data # parse tcp object # check tcp object is right # if it's right, update server_isn, client_isn # remove from buffer_list['connecting'], added to buffer_list['connected'] # generate last_ack_sent and update ack_to_be_sent list, last_ack_received # start sending thread # print # print('[static method]received data from ip, it\'s from', remote_ip) # print('current buffer_list is') # print(buffer_list) # print('src_port is', tcp.src_port, end=' ') # print('dst_port is', tcp.dst_port, end=' ') # print('content is', str(tcp)) # print() # print basic info # names = ['CWR','ECE','URG','ACK','PSH','RST','SYN','FIN'] # byte = tcp.flag_bits # byte = bin(int.from_bytes(byte, 'little'))[2:] # print(bytes) # for i in range(8): #     print("{}:{}".format(names[i], byte[i])) # # if tcp.SYN == 1 and tcp.ACK != 1: #     b # it's first handshake # it's not first handshake # self.__last_acked_number = tcp.acknowledgement_number # todo update last_acked_number # check whether it's a third handshake # it's not a third handshake # buffer_list['connected'][remote_address].append((data, remote_address)) # get tcp object # let obj add data # it's a third handshake # print() # todo calculate a new time out # print('ticking', time.time(), start_time, time_interval)