如何解决python socket server重启后端口被占用的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何解决python socket server重启后端口被占用的问题相关的知识,希望对你有一定的参考价值。

本文介绍下,在solaris 系统下,python socket server重启后,提示端口被占用,telnet端口失败。这里给出一个解决方法,有需要的朋友参考下。

在solaris 系统下,socket server被重启后,提示端口被占用,telnet端口又是不成功的,说明服务已被关闭。
通过netstat可以看到端口还处于于fin_wait_2状态,solaris要4分钟才能关闭。
遇到这个问题时,可以采用如下的方法解决,以减少等待时间。
1,加上s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)。
代码:

复制代码代码示例:
self.host=socket.gethostbyname(socket.gethostname())
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((self.host,self.port))
s.listen(5)
2,修改系统fin_wait,time_wait的时间设置。这个时间改短,也利于系统系能。
修改方法
查看或设置:
使用get命令来确定当前时间间隔,并使用set命令将时间间隔指定为30秒。
例如:

复制代码代码示例:
ndd -get /dev/tcp tcp_time_wait_interval
ndd -set /dev/tcp tcp_time_wait_interval 30000
缺省值:对于 Solaris 操作系统,缺省等待时间间隔为 240000 毫秒(即 4 分钟)。
建议值:60000 毫秒。
Solaris TCP_FIN_WAIT_2_FLUSH_INTERVAL
描述:
指定禁止处于FIN_WAIT_2状态的连接保持该状态的计时器时间间隔。
当连接比率较高时,这将累积大量的TCP/IP连接,从而导致服务器性能下降。在高峰时间段,服务器会发 生延迟。
如果服务器延迟,netstat命令显示对HTTP Server打开的许多套接字处于CLOSE_WAIT或FIN_WAIT_2状态。
明显的延迟可能会长达4分钟,其间服务器无法发送任何响应,但是CPU利用率保持很高,所有活动都在系统进程中。
查看和设置:
使用get命令来确定当前时间间隔,并使用set命令将时间间隔指定为67.5秒。
例如:

复制代码代码示例:
ndd -get /dev/tcp tcp_fin_wait_2_flush_interval
ndd -set /dev/tcp tcp_fin_wait_2_flush_interval 67500
缺省值:675000 毫秒
建议值:67500 毫秒
Solaris TCP_KEEPALIVE_INTERVAL
描述:
“保持活动”包确保连接保持活动和已建立状态。
查看或设置:
使用ndd命令来确定当前值或设置该值。
例如:

复制代码代码示例:
ndd -set /dev/tcp tcp_keepalive_interval 300000
缺省值:7200000 毫秒
建议值:15000 毫秒
参考技术A

可以使用socketserver模块

import socketserver
class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        """
        self.request=======conn
        :return:
        """
        while 1:
            try:
                data = self.request.recv(1024).decode("utf8")  # 阻塞函数
                print("data", data)
                # 针对linux,ios系统
                if len(data) == 0 or data == "q":
                    break
                res = input("回复>>>").encode("utf8")
                self.request.sendall(res)
            # 针对window系统
            except Exception as e:
                break
        self.request.close()

server=socketserver.ThreadingTCPServer(("127.0.0.1",8008),MyServer)
server.serve_forever()

Python网络编程03/ low版解决粘包问题

Python网络编程03/ low版解决粘包问题

1.操作系统的缓存区

1.为什么存在缓冲区
    1. 暂时存储一些数据.
    2. 缓冲区存在如果你的网络波动,保证数据的收发稳定,匀速.
缺点: 造成了粘包现象之一.

技术图片

技术图片

2.基于TCP协议的socket循环通信

2.1 服务端(server)

# import socket
# 
# phone = socket.socket()
# 
# phone.bind(('127.0.0.1',8848))
# 
# phone.listen()
# listen: 允许5个人链接我,剩下的链接也可以链接,等待.
# 
# conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
# print(f'链接来了: conn,addr')
# 
# while 1:
#     try:
#         from_client_data = conn.recv(1024)  # 最多接受1024字节
# 
#         if from_client_data.upper() == b'Q':
#             print('客户端正常退出聊天了')
#             break
# 
#         print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#         to_client_data = input('>>>').strip().encode('utf-8')
#         conn.send(to_client_data)
#     except ConnectionResetError:
#         print('客户端链接中断了')
#         break
# conn.close()
# phone.close()

2.2客户端(client)

# import socket
# 
# phone = socket.socket()
# 
# phone.connect(('127.0.0.1',8848))
# while 1:
#     to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
#     if not to_server_data:
#         # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
#         print('发送内容不能为空')
#         continue
#     phone.send(to_server_data)
#     if to_server_data.upper() == b'Q':
#         break
#     from_server_data = phone.recv(1024)  # 最多接受1024字节
#     print(f'来自服务端消息:from_server_data.decode("utf-8")')
# 
# phone.close()
# 
# # s1 = 'q'
# # s2 = b'q'
# # s3 = '中国'
# # print(s3.encode('utf-8'))
# # print(type(s1),type(s2))
# 
# # s1 = 'q'
# # print(s1.encode('utf-8'))
# 
# # bytes类型:
#     # ASCII字符: 在字符串前面b''
#     # 非ASCII字符: encode 转化成 bytes类型

3.基于TCP协议的socket链接+循环 通信

3.1服务端(server)

# import socket
# 
# phone = socket.socket()
# 
# phone.bind(('127.0.0.1',8848))
# 
# phone.listen(2)
# # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
# 
# while 1:
#     conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
#     print(f'链接来了: conn,addr')
# 
#     while 1:
#         try:
#             from_client_data = conn.recv(1024)  # 最多接受1024字节
# 
#             if from_client_data.upper() == b'Q':
#                 print('客户端正常退出聊天了')
#                 break
# 
#             print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#             to_client_data = input('>>>').strip().encode('utf-8')
#             conn.send(to_client_data)
#         except ConnectionResetError:
#             print('客户端链接中断了')
#             break
#     conn.close()
# phone.close()

3.2 客户端(client)

# import socket
# 
# phone = socket.socket()
# 
# phone.connect(('127.0.0.1',8848))
# while 1:
#     to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
#     if not to_server_data:
#         # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
#         print('发送内容不能为空')
#         continue
#     phone.send(to_server_data)
#     if to_server_data.upper() == b'Q':
#         break
#     from_server_data = phone.recv(1024)  # 最多接受1024字节
#     print(f'来自服务端消息:from_server_data.decode("utf-8")')
# 
# phone.close()

4.基于TCP协议的socket应用实例:执行远程命令

4.1服务端(server)

# import socket
# import subprocess
# phone = socket.socket()
# 
# phone.bind(('127.0.0.1',8848))
# 
# phone.listen(2)
# # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
# 
# while 1:
#     conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
#     print(f'链接来了: conn,addr')
# 
#     while 1:
#         try:
# 
#             from_client_data = conn.recv(1024)  # 最多接受1024字节
# 
# 
#             if from_client_data.upper() == b'Q':
#                 print('客户端正常退出聊天了')
#                 break
# 
#             obj = subprocess.Popen(from_client_data.decode('utf-8'),
#                                    shell=True,
#                                    stdout=subprocess.PIPE,
#                                    stderr=subprocess.PIPE,
# 
#                                    )
#             result = obj.stdout.read() + obj.stderr.read()
# 
#             conn.send(result)
#         except ConnectionResetError:
#             print('客户端链接中断了')
#             break
#     conn.close()
# phone.close()
# 
# 
# 
# 
# # shell: 命令解释器,相当于调用cmd 执行指定的命令。
# # stdout:正确结果丢到管道中。
# # stderr:错了丢到另一个管道中。
# # windows操作系统的默认编码是gbk编码。
#

4.2客户端(client)

# import socket
# 
# phone = socket.socket()
# 
# phone.connect(('127.0.0.1',8848))
# while 1:
#     to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
#     if not to_server_data:
#         # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
#         print('发送内容不能为空')
#         continue
#     phone.send(to_server_data)
#     if to_server_data.upper() == b'Q':
#         break
#     from_server_data = phone.recv(1024)  # 最多接受1024字节
#     print(f'from_server_data.decode("gbk")')
# 
# phone.close()

5.粘包现象

5.1服务端(server)

# 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
# import socket
# import subprocess
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen(2)
# # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
#
# while 1:
#     conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
#     # print(f'链接来了: conn,addr')
#
#     while 1:
#         try:
#
#             from_client_data = conn.recv(1024)  # 最多接受1024字节
#
#
#             if from_client_data.upper() == b'Q':
#                 print('客户端正常退出聊天了')
#                 break
#
#             obj = subprocess.Popen(from_client_data.decode('utf-8'),
#                                    shell=True,
#                                    stdout=subprocess.PIPE,
#                                    stderr=subprocess.PIPE,
#
#                                    )
#             result = obj.stdout.read() + obj.stderr.read()
#             print(f'总字节数:len(result)')
#             conn.send(result)
#         except ConnectionResetError:
#             print('客户端链接中断了')
#             break
#     conn.close()
# phone.close()



# s1 = '太白jx'
# # print(len(s1))
# b1 = s1.encode('utf-8')
# # print(b1)
# print(len(b1))


'''
         客户端                          服务端

第一次:  ipconfig                       317字节
         300个字节                       17个字节
         

         客户端                          服务端

第二次:   dir                           376字节
          17字节                        376字节
         

'''


# 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.

# import socket
#
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen(5)
#
#
# conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
#
# from_client_data = conn.recv(1024)  # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
# conn.close()
# phone.close()


# 展示一些收发的问题。

5.2客户端(client)

# import socket
#
# phone = socket.socket()
#
# phone.connect(('127.0.0.1',8848))
# while 1:
#     to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
#     if not to_server_data:
#         # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
#         print('发送内容不能为空')
#         continue
#     phone.send(to_server_data)
#     if to_server_data.upper() == b'Q':
#         break
#     from_server_data = phone.recv(300)  # 最多接受1024字节
#     # print(f'from_server_data.decode("gbk")')
#     print(len(from_server_data))
#
# phone.close()



# 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.

# import socket
#
# phone = socket.socket()
#
# phone.connect(('127.0.0.1',8848))
#
#
# phone.send(b'he')
# phone.send(b'll')
# phone.send(b'o')
#
#
# phone.close()
# Nigle算法

5.3展示收发问题的服务端(server)

# 发多次收一次
# import socket
#
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen(5)
#
#
# conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
#
# from_client_data = conn.recv(1024)  # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# from_client_data = conn.recv(1024)  # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
# conn.close()
# phone.close()



# 发一次收多次

# import socket
#
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen(5)
#
#
# conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
#
# from_client_data = conn.recv(3)  # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# from_client_data = conn.recv(3)  # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# from_client_data = conn.recv(3)  # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# from_client_data = conn.recv(3)  # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# conn.close()
# phone.close()

5.4 展示收发问题的客户端(client)

# 发多次收一次
# import socket
#
# phone = socket.socket()
#
# phone.connect(('127.0.0.1',8848))
#
#
# phone.send(b'he')
# phone.send(b'llo')
#
#
# phone.close()
# Nigle算法


# 发一次收多次
# import socket
#
# phone = socket.socket()
#
# phone.connect(('127.0.0.1',8848))
#
#
# phone.send(b'hello world')
#
#
# phone.close()

6.如何解决粘包现象

解决粘包现象的思路:

服务端发一次数据 10000字节,  
客户端接收数据时,循环接收,每次(至多)接收1024个字节,直至将所有的字节全部接收完毕.将接收的数据拼接在一起,最后解码.
1. 遇到的问题: recv的次数无法确定.
   你发送总具体数据之前,先给我发一个总数据的长度:5000个字节。然后在发送总数据。
   客户端: 先接收一个长度。 5000个字节。
   然后我再循环recv 控制循环的条件就是只要你接受的数据< 5000 一直接收。
2. 遇到的问题: 总数据的长度转化成的字节数不固定
服务端:
conn.send(total_size) 

conn.send(result)
total_size int类型


客户端:
total_size_bytes = phone.recv(4)
total_size
data = b''
while len(data) < total_size:
    data = data + phone.recv(1024)
你要将total_size int类型转化成bytes类型才可以发送

387    ---- > str(387)  '387'  ---->bytes   b'387'        长度   3bytes

4185  ---->  str(4185)  '4185'  ---->bytes   b'4185'  长度   4bytes

18000------------------------------------------------------>  长度   5bytes

我们要解决: 
将不固定长度的int类型转化成固定长度的bytes并且还可以翻转回来。
struct模块

技术图片

5.low版解决粘包现象

5.1服务端

# 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
import socket
import subprocess
import struct
phone = socket.socket()

phone.bind(('127.0.0.1',8848))

phone.listen(2)
# listen: 2 允许有两个客户端加到半链接池,超过两个则会报错

while 1:
    conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
    # print(f'链接来了: conn,addr')

    while 1:
        try:

            from_client_data = conn.recv(1024)  # 接收命令


            if from_client_data.upper() == b'Q':
                print('客户端正常退出聊天了')
                break

            obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,

                                   )
            result = obj.stdout.read() + obj.stderr.read()
            total_size = len(result)
            print(f'总字节数:total_size')

            # 1. 制作固定长度的报头
            head_bytes = struct.pack('i',total_size)

            # 2. 发送固定长度的报头
            conn.send(head_bytes)

            # 3. 发送总数据
            conn.send(result)
        except ConnectionResetError:
            print('客户端链接中断了')
            break
    conn.close()
phone.close()


# import struct
# # 将一个数字转化成等长度的bytes类型。
# ret = struct.pack('i', 180000000)
# # print(ret, type(ret), len(ret))
#
# # 通过unpack反解回来
# ret1 = struct.unpack('i',ret)[0]
# # print(ret1)
# print(ret1, type(ret1))

# 总数据:总数据长度

# s1 = 'lagfdkjglkhjklh'
# b1 = s1.encode('utf-8')
# print(b1)
# print(len(b1))

5.2客户端(client)

import socket
import struct
phone = socket.socket()

phone.connect(('127.0.0.1',8848))
while 1:
    to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
    if not to_server_data:
        # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
        print('发送内容不能为空')
        continue
    phone.send(to_server_data)
    if to_server_data.upper() == b'Q':
        break

    # 1. 接收报头
    head_bytes = phone.recv(4)
    # 2. 反解报头
    total_size = struct.unpack('i',head_bytes)[0]

    total_data = b''

    while len(total_data) < total_size:
        total_data += phone.recv(1024)

    print(len(total_data))
    print(total_data.decode('gbk'))

phone.close()

以上是关于如何解决python socket server重启后端口被占用的问题的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中为 IRC 机器人配置 SSL?

Python-socket发送文件并解决粘包问题

python - socket - server

Python网络编程03/ low版解决粘包问题

python3 解决tcp黏包方法一

Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (111)解决方法