对等方关闭TCP连接后如何接收所有数据?

时间:2019-07-17 09:27:42

标签: python linux sockets tcp polling

运行一些生产代码,我遇到以下问题: 向某些服务器发送HTTP请求时,服务器在发送响应后立即关闭连接,由于某种原因,这会导致数据丢失。

分析TCP转储,我可以看到对话是这样的: 客户要求

服务器确认

服务器推送

服务器鳍,确认(在上一次推送后〜0.000020秒后发送)

结果是我的代码无法获取服务器发送的数据,(我猜测是因为push POLLHUP事件之后的延迟很小,可能早于POLLIN吗?)

试图模仿该问题,我编写了以下代码: (它模仿了我这边的客户行为) 客户:

import time
import socket

from errno import EAGAIN
from select import poll, POLLIN, POLLPRI, POLLERR, POLLHUP, POLLNVAL


def main(buf=""):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.setblocking(False)
    client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    client.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)

    polling_object = poll()
    polling_object.register(client, POLLPRI | POLLIN | POLLERR | POLLHUP)

    in_buf = ""
    sock_closed = False
    try:
        client.connect(("127.0.0.1", 8877))
    except socket.error, e:
        pass

    while True and not sock_closed:
        events = polling_object.poll(0)
        for _, e in events:
            if e & (POLLIN | POLLPRI):
                while True:
                    try:
                        data = client.recv(1024)
                        if data:
                            in_buf += data
                        elif data == "":
                            client.close()
                            sock_closed = True
                            break
                    except socket.error, e:
                        if e.args[0] == EAGAIN:
                            break
                        else:
                            raise
            elif e & (POLLERR|POLLHUP|POLLNVAL):
                client.close()
                sock_closed = True

        if buf and not sock_closed:
            try:
                b_sent = client.send(buf)
                if b_sent == len(buf):
                    buf = ""
                else:
                    buf = buf[b_sent:]
            except socket.error, e:
                if e.args[0] != EAGAIN:
                    client.close()
                    sock_closed = True

        time.sleep(0.5)
        if sock_closed:
            return in_buf


if __name__ == '__main__':
    import sys
    if len(sys.argv) > 1:
        buf = sys.argv[1]
    else:
        buf = 'hello'

    print main(buf)

服务器

import datetime
import time
import socket


def main():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(("127.0.0.1", 8877))
    server.listen(0)
    client, _ = server.accept()
    t1 = time.time()
    data = ""
    while not data:
        data += client.recv(1024)
    print "recv data %s" % data
    client.sendall('{"ok": 1}')
    t2 = time.time()
    client.close()
    t3 = time.time()
    server.close()

    return t1, t2, t3


if __name__ == '__main__':
    c_r, d_s, c_c = main()
    print "Connection received at ", datetime.datetime.fromtimestamp(c_r)
    print "All Data sent after %.12f secs" % (d_s - c_r)
    print "Connection closed after %.12f secs" % (c_c - d_s)

运行此代码不会帮助我重现此问题,因为我的客户端仍然可以从套接字缓冲区中获取数据,这显然是通过遵循代码来实现的。唯一的区别是,在tcp dump中,它是这样的:

客户请求

服务器确认

服务器推送

客户确认

服务器鳍,确认

我想知道是否有一种方法可以在推送后立即发送fin,ack,而不会“让”客户端发送已发送的ack?可以使用python完成吗?

0 个答案:

没有答案