百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Python3网络编程——socket编程之Tcp编程

toyiye 2024-08-27 22:10 6 浏览 0 评论

1:Python3中socket编程介绍

这里就不介绍网络编程的基础知识了,比如TCP/IP协议,OSI模型,TCP的三次握手等。下面直接介绍python中socket编程;

2:简单的点对点

只接受单个连接的服务端:

01_TcpServer.py:

# -*- coding: utf-8 -*-
 
import socket
import time
 
server = socket.socket()
ip_port = ('127.0.0.1',8000)
server.bind(ip_port)  # 绑定ip+端口
server.listen(10)  # 监听
 
# print(help(server.listen))
 
print('启动服务:等待客户端的连接......')
conn,addr = server.accept()
print('客户端已连接:')
print(conn)
print(addr)
 
while True:
    print('等待客户端的数据:')
    client_data = conn.recv(1024)  # 接收客户端的数据,接收的数据是当前时间的秒数,下面处理是把秒转换为字符串
    client_data = client_data.decode('utf-8')
    stru_t = time.localtime(float(client_data))
    strTime = time.strftime('%Y-%m-%d %H:%M:%S',stru_t)
    print(f'接收来自{addr}的数据:{strTime}' )
 
    conn.send(str(time.time()).encode('utf-8'))  # 给客户端发送当前时间的秒数
 
conn.close()
server.close()

01_TcpClient.py

# -*- coding: utf-8 -*-
 
import socket
import  time
client = socket.socket()
ip_port = ('127.0.0.1',8000)
client.connect(ip_port)
 
while True:
    client.send(str(time.time()).encode('utf-8'))
    print('等待服务端的数据:')
 
    server_data = client.recv(1024)
    server_data = server_data.decode('utf-8')
    stru_t = time.localtime(float(server_data))
    strTime = time.strftime('%Y-%m-%d %H:%M:%S', stru_t)
    print(f'来自服务端的数据:{strTime}')
    time.sleep(10)
 
client.close()

3:并发服务端

3.1:创建线程处理

对于每个客户端连接都创建一个线程来处理:

02_TcpServer.py:

# -*- coding: utf-8 -*-
 
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
import time
import threading  # 线程模块
import traceback  # 打印异常信息
 
# 处理每个链接
def handle_client(conn, addr):
    print(conn, addr)
    while True:
        try:
            print(f'等待客户端{addr}的数据:')
            client_data = conn.recv(1024)  # 接收客户端的数据,接收的数据是当前时间的秒数,下面处理是把秒转换为字符串
            client_data = client_data.decode('utf-8')
            stru_t = time.localtime(float(client_data))
            strTime = time.strftime('%Y-%m-%d %H:%M:%S', stru_t)
            print(f'接收来自{addr}的数据:{strTime}')
 
            conn.send(str(time.time()).encode('utf-8'))  # 给客户端发送当前时间的秒数
        except ConnectionResetError:
            val = traceback.format_exc()
            print(val)
            break
        except Exception:
            val = traceback.format_exc()
            print(val)
 
    print(f'关闭链接{addr}')
    conn.close()
 
 
def StartTcpServer(ip,port):
    server = socket.socket()
    server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 在bind前加,允许地址重用
    server.bind((ip,port))  # 绑定ip+端口
    server.listen(10)  # 监听
 
    # print(help(server.accept))
 
    while True:
        try:
            print('等待客户端的连接......')
            conn, addr = server.accept()  # 一直会阻塞在这里,直到有新的连接进来
            # 创建一个线程来处理每个链接
            threading.Thread(target=handle_client, args=(conn, addr)).start()
        except Exception:
            val = traceback.format_exc()
            print(val)
            break  # 有异常退出循环
 
    server.close()
 
if __name__ == '__main__':
    StartTcpServer('127.0.0.1', 8000)

02_TcpClient.py:

# -*- coding: utf-8 -*-
 
import socket
import  time
import traceback
 
def StartTcpClient(ip_port):
    # 开3个客户端
    client_list = [socket.socket() for i in range(3)]
    # ip_port = ('127.0.0.1',8000)
 
    for client in client_list:
        print(client)
        client.connect(ip_port)
 
    while True:
        for index,client in enumerate(client_list):
            try:
                client.send(str(time.time()).encode('utf-8'))
                print(f'clirnt[{index}],等待服务端的数据:')
 
                server_data = client.recv(1024)
                server_data = server_data.decode('utf-8')
                stru_t = time.localtime(float(server_data))
                strTime = time.strftime('%Y-%m-%d %H:%M:%S', stru_t)
                print(f'来自服务端的数据:{strTime}')
                time.sleep(2)
 
            except Exception:
                val = traceback.format_exc()
                print(val)
                client.close()
                client_list.remove(client)
 
 
if __name__ == '__main__':
    StartTcpClient(('127.0.0.1',8000))

3.2:socketserver模块处理

在上面使用每来一个连接,就创建一个线程的方式来处理,如果连接的数量过多,创建线程就会出现问题。

在Python中提供了socketserver模块,socketserver在内部使用IO多路复用以及多线程/进程机制,实现了并发处理多个客户端请求的socket服务端。

03_TcpServer.py

# -*- coding: utf-8 -*-
 
import socketserver
import time
import traceback  # 打印异常信息
 
def handle_client_data(data,addr):
    try:
        data = data.decode('utf-8')
        # print(type(data),data)
        stru_t = time.localtime(int(data))
        strTime = time.strftime('%Y-%m-%d %H:%M:%S', stru_t)
        print(f"来自{addr}的客户端向你发来信息:{data},转换之后:{strTime}"  )
    except Exception:
        pass
 
# 每个链接都使用一个 TcpServer 实例对象来处理,并且会自动调用handle方法,退出handle方法链接就会自动断开。
class TcpServer(socketserver.BaseRequestHandler):
    """
    必须继承socketserver.BaseRequestHandler类
    """
    def handle(self):
        """
        必须实现这个方法!
        :return:
        """
        conn = self.request         # request里封装了所有请求的数据
        conn.sendall(str(time.time()).encode('utf-8'))
        print(f"1111:{self.client_address}")    # 打日志验证每个链接是不是都会进来一次
        while True:
            try:
                data = conn.recv(1024)
                handle_client_data(data,self.client_address)
                conn.sendall(str(int(time.time())).encode('utf-8'))
            except Exception:
                val = traceback.format_exc()
                print(val)
                break
        print(f'退出客户端{self.client_address}的处理。')
 
def StartTcpServer(ip,port):
    # 创建一个多线程TCP服务器
    server = socketserver.ThreadingTCPServer((ip,port), TcpServer)
    print("启动socketserver服务器!")
    # 启动服务器,服务器将一直保持运行状态
    server.serve_forever()
 
if __name__ == '__main__':
    StartTcpServer('127.0.0.1', 8000)

03_TcpClient.py:

# -*- coding: utf-8 -*-
 
import socket
import  time
import traceback
 
def StartTcpClient(ip_port,nums):
    client_list = [socket.socket() for i in range(nums)]
    for client in client_list:
        print(client)
        client.connect(ip_port)
 
    while True:
        try:
            for index,client in enumerate(client_list):
                try:
                    # client.send(str( int(time.time()) ).encode('utf-8'))
                    client.sendall(str( int(time.time()) ).encode('utf-8'))
                    print(f'clirnt[{index}],等待服务端的数据:')
 
                    server_data = client.recv(1024)
                    # print(f'来自服务端的数据:{server_data}')
                    try:
                        server_data = server_data.decode('utf-8')
                        stru_t = time.localtime(float(server_data))
                        strTime = time.strftime('%Y-%m-%d %H:%M:%S', stru_t)
                        print(f'来自服务端的数据:{server_data} 转换之后:{strTime}')
                    except Exception:
                        pass
                    time.sleep(1)
 
 
                except (ConnectionResetError,ConnectionAbortedError):
                    val = traceback.format_exc()
                    print(val)
                    client.close()
                    client_list.remove(client)
 
                    if len(client_list) < 1:
                        raise
 
                except Exception:
                    val = traceback.format_exc()
                    print(val)
 
        except Exception:
            val = traceback.format_exc()
            print(val)
            print('退出')
            break
 
def test():
    print(str(time.time() ),time.time(),int(time.time()))
 
if __name__ == '__main__':
    StartTcpClient(('127.0.0.1',8000),1)
    test()

3.3:使用select模块

Python中的select模块专注于I/O多路复用,提供了select poll epoll三个方法(其中后两个在Linux中可用,windows仅支持select),另外也提供了kqueue方法(freeBSD系统)

select方法:

进程指定内核监听哪些文件描述符(最多监听1024个fd)的哪些事件,当没有文件描述符事件发生时,进程被阻塞;当一个或者多个文件描述符事件发生时,进程被唤醒。

04_TcpServer.py:

# -*- coding: utf-8 -*-
 
import socket
import select
from socket import SOL_SOCKET,SO_REUSEADDR
import time
import traceback  # 打印异常信息
 
 
def handle_client_data(data,addr):
    try:
        data = data.decode('utf-8')
        # print(type(data),data)
        stru_t = time.localtime(int(data))
        strTime = time.strftime('%Y-%m-%d %H:%M:%S', stru_t)
        print(f"来自{addr.getpeername()}的客户端向你发来信息:{data},转换之后:{strTime}")
    except Exception:
        pass
 
 
def StartTcpServer(ip,port):
    server = socket.socket()
    server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 在bind前加,允许地址重用
    server.bind((ip,port))  # 绑定ip+端口
    server.listen(10)  # 监听
 
    read_fd_list = [server,]
 
    while True:
        try:
            r_list, w_list, error_list = select.select(read_fd_list, [], [], 1) # 超时设置为1秒
 
            stru_t = time.localtime(time.time())
            strTime = time.strftime('%Y-%m-%d %H:%M:%S', stru_t)
            print(f'select之后:len(r_list)={len(r_list)},时间:{strTime}')
 
            for fd in r_list:  # r_list 为可读的文件描述符列表
                if fd == server: #   可读的文件描述符 为 server(上面起服务创建的),说明有链接请求过来。
                    conn, addr = fd.accept()  #  接收客户端链接
                    print(addr)
                    read_fd_list.append(conn)  # 把客户端,加入读轮询列表
                    data = conn.recv(1024)
                    print(f'接收数据:msg={data}')
                    handle_client_data(data, conn)
                    conn.sendall(str(time.time()).encode('utf-8'))  # sendall 会循环调用send,把所有数据都发出去。
                    # conn.send(str(time.time()).encode('utf-8'))     # send 发送出去的数据可能是部分数据
                else: # 客户端可读,就是有客户端发送数据过来。
                    try:
                        data = fd.recv(1024)
                        # print(help(fd))
                        handle_client_data(data,fd)
                        fd.sendall(str(int(time.time())).encode('utf-8'))
                    except (ConnectionResetError,ConnectionAbortedError,ConnectionRefusedError):
                        val = traceback.format_exc()
                        print(val)
                        fd.close()
                        read_fd_list.remove(fd)
 
        except Exception:
            val = traceback.format_exc()
            print(val)
            break  # 有异常退出循环
 
    server.close()
 
if __name__ == '__main__':
    StartTcpServer('127.0.0.1', 8000)

04_TcpClient.py:

# -*- coding: utf-8 -*-
 
import socket
import  time
import traceback
 
def StartTcpClient(ip_port,nums):
    client_list = [socket.socket() for i in range(nums)]
    for client in client_list:
        print(client)
        client.connect(ip_port)
 
    while True:
        try:
            for index,client in enumerate(client_list):
                try:
                    client.sendall(str( int(time.time()) ).encode('utf-8'))
                    # client.send(str( int(time.time()) ).encode('utf-8'))
                    print(f'clirnt[{index}],等待服务端的数据:')
 
                    server_data = client.recv(1024)
                    # print(f'来自服务端的数据:{server_data}')
                    server_data = server_data.decode('utf-8')
                    stru_t = time.localtime(float(server_data))
                    strTime = time.strftime('%Y-%m-%d %H:%M:%S', stru_t)
                    print(f'来自服务端的数据:{server_data} 转换之后:{strTime}')
                    # time.sleep(1)
 
                except (ConnectionResetError, ConnectionAbortedError, ConnectionRefusedError):
                    val = traceback.format_exc()
                    print(val)
                    client.close()
                    client_list.remove(client)
 
                    if len(client_list) < 1:
                        raise
 
                except Exception:
                    val = traceback.format_exc()
                    print(val)
 
        except Exception:
            val = traceback.format_exc()
            print(val)
            print('退出')
            break
 
if __name__ == '__main__':
    StartTcpClient(('127.0.0.1',8000),1)
    # StartTcpClient(('127.0.0.1',8000),2000)  

4:socket实现web服务器

4.1:简单web服务器

在浏览器中访问 http://127.0.0.1:8000/home 等

# -*- coding: utf-8 -*-
import socket
 
def StartWebServer(ip,port):
    # 创建socket对象
    sk = socket.socket()
 
    # 绑定IP和端口
    sk.bind((ip,port))
 
    # 监听
    sk.listen()
 
    while True:
        # 等待连接
        conn, addr = sk.accept()
        # 接收数据
        data = conn.recv(8096)
        print(data)
 
        # 返回状态行
        conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
 
        # 返回数据
        try:
            data = data.decode('utf-8')
            url = data.split()[1]
            ret_data = url
            if url == "/test":
                ret_data = "test"
            elif url == "/home":
                ret_data = "home"
            elif url == "/index":
                ret_data = "index"
            else:
                ret_data = "404"
            conn.send(f'<h1>{ret_data}</h1>'.encode('utf-8'))
        except Exception:
            pass
 
        # 关闭连接
        conn.close()
 
 
if __name__ == '__main__':
    StartWebServer('127.0.0.1', 8000)

4.2:简单web服务器:函数版本

# -*- coding: utf-8 -*-
import socket
import time
 
def test(url):
    ret = f'test: {url}'
    return ret.encode('utf-8')
 
def index(url):
    ret = f'index: {url}'
    return ret.encode('utf-8')
 
def home(url):
    ret = f'home: {url}'
    return ret.encode('utf-8')
 
def gettime(url):
    now = time.strftime('%Y-%m-%d %H:%M:%S')
    ret = f'time: {url} {now}'
    return ret.encode('utf-8')
 
url_map = {'/test':test,'/index':index,'/home':home,'/time':gettime}
 
 
def handle_client_data(data):
    data = data.decode('utf-8')
    url = data.split()[1]
 
    ret_data = url
    if url in url_map:
        func = url_map[url]
        ret_data = func(url)
    else:
        ret_data = b"404"
    return ret_data
 
def StartWebServer(ip,port):
    # 创建socket对象
    sk = socket.socket()
 
    # 绑定IP和端口
    sk.bind((ip,port))
 
    # 监听
    sk.listen()
 
    while True:
        # 等待连接
        conn, addr = sk.accept()
        # 接收数据
        data = conn.recv(8096)
        print(data)
 
        # 返回状态行
        conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
 
        # 返回数据
        try:
            ret_data = handle_client_data(data)
            conn.send(ret_data)
        except Exception:
            pass
 
        # 关闭连接
        conn.close()
 
if __name__ == '__main__':
    StartWebServer('127.0.0.1', 8000)

在浏览器中访问:http://127.0.0.1:8000/time

4.3:简单web服务器:返回动态页面

# -*- coding: utf-8 -*-
import socket
import time
 
def test(url):
    ret = f'test: {url}'
    return ret.encode('utf-8')
 
def index(url):
    with open('index.html', 'rb') as f:
        ret = f.read()
        return ret
 
def home(url):
    ret = f'home: {url}'
    return ret.encode('utf-8')
 
def gettime(url):
    now = time.strftime('%Y-%m-%d %H:%M:%S')
    with open('time.html', 'r', encoding='utf-8') as f:
        data = f.read()
        data = data.replace('##time##', now)
        return data.encode('utf-8')
 
url_map = {'/test':test,'/index':index,'/home':home,'/time':gettime}
 
 
def handle_client_data(data):
    data = data.decode('utf-8')
    url = data.split()[1]
 
    ret_data = url
    if url in url_map:
        func = url_map[url]
        ret_data = func(url)
    else:
        ret_data = b"404"
    return ret_data
 
def StartWebServer(ip,port):
    # 创建socket对象
    sk = socket.socket()
 
    # 绑定IP和端口
    sk.bind((ip,port))
 
    # 监听
    sk.listen()
 
    while True:
        # 等待连接
        conn, addr = sk.accept()
        # 接收数据
        data = conn.recv(8096)
        print(data)
 
        # 返回状态行
        conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
 
        # 返回数据
        try:
            ret_data = handle_client_data(data)
            conn.send(ret_data)
        except Exception:
            pass
 
        # 关闭连接
        conn.close()
 
if __name__ == '__main__':
    StartWebServer('127.0.0.1', 8000)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<div>
    <h1>index</h1>
	<h2>index</h2>
	<h3>index</h3>
	<h4>index</h4>
</div>
</body>
</html>

time.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>time</title>
</head>
<body>
<h1>当前时间是: ##time## </h1>
</body>
</html>

4.4:并发web服务器

使用socketserver实现:

# -*- coding: utf-8 -*-
 
import socketserver
import time
import traceback  # 打印异常信息
 
 
def test(url):
    ret = f'test: {url}'
    return ret.encode('utf-8')
 
def index(url):
    with open('index.html', 'rb') as f:
        ret = f.read()
        return ret
 
def home(url):
    ret = f'home: {url}'
    return ret.encode('utf-8')
 
def gettime(url):
    now = time.strftime('%Y-%m-%d %H:%M:%S')
    with open('time.html', 'r', encoding='utf-8') as f:
        data = f.read()
        data = data.replace('##time##', now)
        return data.encode('utf-8')
 
url_map = {'/test':test,'/index':index,'/home':home,'/time':gettime}
 
 
def handle_client_data(data):
    data = data.decode('utf-8')
    url = data.split()[1]
 
    ret_data = url
    if url in url_map:
        func = url_map[url]
        ret_data = func(url)
    else:
        ret_data = b"404"
    return ret_data
 
 
 
# 每个链接都使用一个 TcpServer 实例对象来处理,并且会自动调用handle方法,退出handle方法链接就会自动断开。
class TcpServer(socketserver.BaseRequestHandler):
    """
    必须继承socketserver.BaseRequestHandler类
    """
    def handle(self):
        """
        必须实现这个方法!
        :return:
        """
        conn = self.request         # request里封装了所有请求的数据
        # conn.sendall(str(time.time()).encode('utf-8'))
        # print(f"1111:{self.client_address}")    # 打日志验证每个链接是不是都会进来一次
        while True:
            try:
                data = conn.recv(8096)
                print(data)
                conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
 
                # 返回数据
                try:
                    ret_data = handle_client_data(data)
                    conn.sendall(ret_data)
                except Exception:
                    pass
 
                break
            except Exception:
                val = traceback.format_exc()
                print(val)
                break
        print(f'退出客户端{self.client_address}的处理。')
 
def StartTcpServer(ip,port):
    # 创建一个多线程TCP服务器
    server = socketserver.ThreadingTCPServer((ip,port), TcpServer)
    print("启动socketserver服务器!")
    # 启动服务器,服务器将一直保持运行状态
    server.serve_forever()
 
if __name__ == '__main__':
    StartTcpServer('127.0.0.1', 8000)


相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码