Python使用Socket和多线程实现简单的TCP服务端和客户端通信

2023-11-02

本文主要是使用Socket的方式进行Python的网络编程,结合多线程完成服务端同时连接多个客户端的程序,学习了解Socket的主要工作流程。

一、关于Socket

1.Socket简介

Socket是指套接字,是对网络中不同主机上的应用进程之间进行双向通信的端点的一种抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。

2.Socket的主要类型

Socket主要有三种类型:流套接字、数据报套接字、原始套接字。

  • 流套接字(SOCK_STREAM):采用了TCP协议,用于提供面向连接、可靠的数据传输服务。
  • 数据报套接字(SOCK_DGRAM):采用了UDP协议,提供一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。
  • 原始套接字(SOCK_RAW):与上面两种套接字的区别在于原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。

本文中讲的是采用TCP协议,即流套接字。

3.Socket的工作流程

由于是双向通信,所以Socket的工作流程需要一对套接字连接进行使用,一个是作为服务端(Server),一个是作为客户端(Client)。Socket工作的基本流程和Python中主要的几个方法如下图所示。
在这里插入图片描述
服务端使用 socket()创建套接字之后,通过bind()方法绑定端口,然后使用listen()对端口进行阻塞式地监听,等待客户端发来建立连接的请求。当接收到建立连接的请求时,使用accept()方法接受客户端的连接请求,此后进入recv()send()不断进行接收数据和发送数据的操作。最后,使用close()关闭套接字终止程序,不过服务端程序一般不会主动进行关闭。
客户端相对来说比较简单,同样使用socket()close()来创建和关闭套接字。客户端使用connect()向目标的地址和端口发出建立连接的请求,建立连接成功之后就会进入recv()send()中不断进行接收数据和发送数据的操作。

二、Python的socket模块

Python中进行网络编程的主要是使用socket模块,当然还有高级一点的网络服务模块SocketServer等内容。本文中主要使用的是socket模块。
socket模块中首先需要使用socket()方法创建套接字对象,代码示例如下:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

其中,第一个参数是代表套接字家族,一般有socket.AF_UNIX、socket.AF_INET、socket.AF_INET6可以选择。AF_UNIX是本机的通信,AF_INET和AF_INET6分别是IPv4和IPv6。第二个参数是套接字类型,有socket.SOCK_STREAM、socket.SOCK_DGRAM、socket.SOCK_RAW,分别代表套接字的三种类型。
以下简要介绍socket对象中函数的使用描述。

1.服务端使用的函数

函数 描述
s.bind() 绑定地址(host,port)到套接字, 在 AF_INET下,以元组(host,port)的形式表示地址
s.listen() 开始 TCP 监听。参数backlog是指操作系统可以挂起的最大连接数量。该值至少为 1,一般设置为5
s.accept() 被动接受TCP客户端连接,阻塞式等待连接的到来

2.客户端使用的函数

函数 描述
s.connect() TCP服务器连接,参数address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

3.服务端和客户端都可以使用的函数

函数 描述
s.recv() 接收 TCP 数据,数据以字符串形式返回,bufsize 指定要接收的最大数据量
s.send() 发送 TCP 数据,将参数string 中的数据发送到连接的套接字
s.sendall() 完整发送 TCP 数据。将参数 string 中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回 None,失败则抛出异常。
s.close() 关闭套接字
s.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)
s.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt() 设置给定套接字选项的值
s.getsockopt() 返回套接字选项的值
s.settimeout() 设置套接字操作的超时期,参数timeout是一个浮点数,单位是秒。值为None表示没有超时期
s.gettimeout() 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None
s.setblocking() 如果参数flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常

三、服务端程序

服务端的程序除了使用套接字的通信之外,还要使用多线程的方式来与多个客户端保持通信连接。因此,服务端的基本流程如下图。当每次建立起一个新的连接时,就使用threading模块创建一个新的线程,向新的线程中传入该客户端套接字的信息,并保持通信,同时该线程需要通过thd.setDaemon(True)设置为守护主线程。
在这里插入图片描述
在这里呢,为了体现简单地体现服务端对数据的处理和转发的功能,我假设了一个场景,是将客户端分为APP和设备两种,模拟一下设备通过服务端的转发向APP发送数据的情况。这里的设备可以理解为某种传感器或者某些被管理的计算机等。
在服务端程序中,创建了两个列表来储存当前在线的设备客户端和APP客户端,在连接创建和连接断开时更新列表的数据。为了识别客户端是APP还是设备,需要客户端在连接建立时发送一个字符串作为信号告诉服务端自己的身份,用dev代表设备,用app代表APP客户端。

# -*- coding:utf-8 -*-

import socket
import threading

tcp_port = 填写服务器端的端口号
client_num = 填写可以挂起的最大的客户端数量,一般为5
app_list = list()
dev_list = list()


def app_handle(app_client, data):
    print()


def dev_handle(dev_client, data):
    data = data.encode()
    for app in app_list:  # 转发设备的数据给APP
        app.send(data)


def message_handle(tcp_client, tcp_client_ip):
    client_type = ""  # 用于标记该客户端的身份
    while True:
        try:
            recv_data = tcp_client.recv(4096)
            if recv_data:
                recv_data = recv_data.decode()
                if recv_data == "dev":
                    dev_list.append(tcp_client)
                    client_type = "dev"
                    print(str(tcp_client_ip) + "客户端为设备")
                    print("设备:" + str(dev_list))
                elif recv_data == "app":
                    app_list.append(tcp_client)
                    client_type = "app"
                    print(str(tcp_client_ip) + "客户端为APP")
                    print("app:" + str(app_list))
                else:
                    if client_type == "app":
                        app_handle(tcp_client, recv_data)
                    elif client_type == "dev":
                        dev_handle(tcp_client, recv_data)
        except Exception as e:  # 连接出现异常的处理
            print(str(tcp_client_ip) + str(e))
            if client_type == "app":
                app_list.remove(tcp_client)
                print("app:" + str(app_list))
            elif client_type == "dev":
                dev_list.remove(tcp_client)
                print("设备:" + str(dev_list))
            break


if __name__ == '__main__':
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 使用TCP协议
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    server.bind(("", tcp_port))
    server.listen(client_num)

    while True:
        client, client_ip = server.accept()
        client.settimeout(120)  # 设置连接的超时期
        print(str(client_ip) + "接入")
        thd = threading.Thread(target=message_handle, args=(client, client_ip))
        thd.setDaemon(True)  # 设置守护主线程
        thd.start()

四、客户端程序

客户端程序相对比较简单,就是发送或者接收数据。
以下是APP的客户端的程序。

# -*- coding:utf-8 -*-
# APP端的程序
import socket

server_ip = 服务器的外网IP地址
server_port = 服务器端的端口

if __name__ == '__main__':
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.settimeout(120)
    client.connect((server_ip, server_port))
    client.send("app".encode())  # 告诉服务端自己的身份
    while True:
        print(client.recv(1024).decode())

以下是设备的客户端的程序。

# -*- coding:utf-8 -*-
# 设备端的程序
import socket

server_ip = 服务器的外网IP地址
server_port = 服务器端的端口

if __name__ == '__main__':
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.settimeout(120)
    client.connect((server_ip, server_port))
    client.send("dev".encode())  # 告诉服务端自己的身份
    client.send("testing".encode())

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python使用Socket和多线程实现简单的TCP服务端和客户端通信 的相关文章

随机推荐

  • TCP 协议(四)重传与超时

    1 TCP 协议中的计时器 TCP 中有四种计时器 Timer 分别为 重传计时器 Retransmission Timer 持久计时器 Persistent Timer 保活计时器 Keeplive Timer 等待计时器 Timer W
  • gdb+gdbserver远程调试技术

    首先声明 此文是在别人的基础上添加一些自己的体会 之前做嵌入式开发的时候 弄过一段时间gdb gdbserver远程调试 最后无果而终 只好将就用printf 首先感谢这篇刘品的文章 看着整洁清楚 http www cnblogs com
  • 目标检测之YOLOv3算法分析

    基本原理 特征网络 输入输出 输入 416 416 3 416 416 3 416 416 3大小的图片 不唯一 但图片大小必为32的倍数 输出3个尺度的feature map 分别为
  • 虹科分享

    说到应用程序和软件 关键词是 更多 在数字经济需求的推动下 从简化业务运营到创造创新的新收入机会 企业越来越依赖应用程序 云本地应用程序开发更是火上浇油 然而 情况是双向的 这些应用程序通常更复杂 使用的开放源代码比以往任何时候都包含更多的
  • 在Linux中使用线程

    我并不假定你会使用Linux的线程 所以在这里就简单的介绍一下 如果你之前有过多线程方面的编程经验 完全可以忽略本文的内容 因为它非常的初级 首先说明一下 在Linux编写多线程程序需要包含头文件pthread h 也就是说你在任何采用多线
  • printf()和cout的区别

    printf 和cout的区别 printf is a function that takes a variable number of arguments the first argument being a format string
  • Ubuntu使用内网穿透实现外网ssh远程登录

    Ubuntu使用内网穿透实现外网ssh远程登录 想要远程Ubuntu可以使用ssh网络协议进行远程登录 那什么时ssh呢 SSH 为 Secure Shell的缩写 由 IETF 的网络小组 Network Working Group 所制
  • Windows-tomcat 部署Java项目

    windows 通过 tomcat 部署项目 部署环境准备 JDK下载安装及配置 进入 Oracle官网 的 Java 界面 Oracle官网地址 https www oracle com java 1 JDK下载 1 1 在网站页面滚动鼠
  • 入门神经网络——浅层神经网络

    文章目录 一 基础知识 1 浅层神经网络介绍 2 浅层神经网络的正向传播 3 反向传播 二 浅层神经网络代码实例 一 基础知识 1 浅层神经网络介绍 此次构件浅层神经网络 相比于单神经元 浅层神经网络拥有多个神经元 因此又可以称为多神经元网
  • 网安等保

    欢迎关注 全栈工程师修炼指南 公众号 点击 下方卡片 即可关注我哟 设为 星标 每天带你 基础入门 到 进阶实践 再到 放弃学习 花开堪折直须折 莫待无花空折枝 作者主页 https www weiyigeek top 博客 https b
  • xshell连接Linux一直失败解决方法

    文章目录 解决对象 方法 配置 防火墙 关闭Linux防火墙 关闭Windows防火墙 xshell连接Linux一直失败解决方法 解决对象 可能出现以下两个问题 Linux防火墙已关闭和Windows防火墙已经关闭 配置好 vim etc
  • 分布式场景下基于拍卖算法的边缘智能节点任务分配

    摘 要 针对分布式场景下的边缘智能节点任务分配问题 构建了多类型的任务分配模型用于描述边缘智能节点与任务之间的关系 提出了一种基于拍卖算法的任务分配方法 智能边缘节点以动态拍卖的方式获取各自任务 基于拍卖算法的任务分配 智能边缘节点分为拍卖
  • iPad断触问题,iPencil正常,手指断触11个简便解决方法

    最开始上网搜 发现很多人都要此类情况 然后有人说是贴膜 带壳 人体导致的静电 有人说是iPad产品缺陷 有人说是接触不良 这里提供几个简便方法 有人换了三口插头 mac的充电线 链接地线可以放电 可以起到一定作用 简便方法1 一手摸pad屏
  • vue3中script setup获取动态组件component的Dom

    使用
  • MyBatis经典面试题及答案

    1 什么是MyBatis 答 MyBatis是一个可以自定义SQL 存储过程和高级映射的持久层框架 2 讲下MyBatis的缓存 答 MyBatis的缓存分为一级缓存和二级缓存 一级缓存放在session里面 默认就有 二级缓存放在它的命名
  • Composite——设计模式学习笔记

    Composite模式 一 意图 将对象组合成树形结构以表示 部分 整体 的层次结构 Composite使得用户对单个对象操作和组合对象的操作使用具有一致性 二 动机 绘图编辑器和图形捕捉系统图形应用程序中 总是存在简单的图形到简单的组件再
  • 15-Scala使用Option、Some、None,避免使用null

    1 避免null使用 大多数语言都有一个特殊的关键字或者对象来表示一个对象引用的是 无 在Java 它是null 在Java 里 null 是一个关键字 不是一个对象 所以对它调用任何方法都是非法的 但是这对语言设计者来说是一件令人疑惑的选
  • java.lang.reflect.UndeclaredThrowableException原因和解决方法

    在 Spring AOP中调用一个方法来进行数据验证 一旦数据验证失败 抛出一个自定义的异常 然而 却抛出了java lang reflect UndeclaredThrowableException 查了一下 因为我的自定义异常继承Exc
  • PHP-Tpthink5学习记录1(后台管理平台)

    Mysql数据库Mac安装 https www cnblogs com yanlin 10 p 9388911 html Tp5安装 方式一 http www thinkphp cn donate download id 874 html
  • Python使用Socket和多线程实现简单的TCP服务端和客户端通信

    本文主要是使用Socket的方式进行Python的网络编程 结合多线程完成服务端同时连接多个客户端的程序 学习了解Socket的主要工作流程 本文目录 一 关于Socket 1 Socket简介 2 Socket的主要类型 3 Socket