python中的socket套接字基础 (客户端服务器信息交互)

news2024/10/5 21:45:11

文章目录

  • 1 socket概述
  • 2 socket使用
      • 2.1 socket常用函数
      • 2.2 简单实现 ==客户端与服务器== 信息交互
      • 2.3 ==多线程==实现服务器和客户端信息交互

1 socket概述

  Python中,我们利用Socket套接字来实现网络通信,可以说套接字是实现网络编程进行数据传输的一种技术手段。Socket用于描述IP地址和端口,应用程序通常通过"套接字"向网络发出请求或者应答网络请求
  Socket主要是基于应用层和传输层之间,是一个中间的抽象层,功能是将负责的复杂的TCP/IP协议族隐藏在Socket接口后面。
  应用程序通过套接字发送或接收数据,socket模块针对服务器端和客户端Socket进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

2 socket使用

通给指定地址簇和socket类型来进行创建。

地址簇描述
socket.AF_UNIX只能够用于单一的Unix系统进程间通信
socket.AF_INET服务器之间网络通信IPv4
socket.AF_INET6IPv6
socket类型描述
socket.SOCK_STREAM 流式socket , for TCP
socket.SOCK_DGRAM数据报式socket , for UDP
socket.SOCK_RAW原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。

服务器与客户端交互过程:
在这里插入图片描述
服务器:
1 创建socket套接字
2 绑定地址和端口
3 监听客户端socket请求
4 等待客户端连接
5 创建新套接字描述符,等待客户端发送请求
客户端:
1 创建套接字
2 发送请求,连接服务器地址和端口
3 连接成功后,发送/接收数据

2.1 socket常用函数

socket常用函数描述
sk.bind(address)将socket绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。
sk.listen(backlog)开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
sk.connect(address)连接到address处的socket。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
sk.connect_ex(address)同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码。
sk.close()关闭socket。
sk.recv(bufsize)接受socket的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。
sk.send(string)将string中的数据发送到连接的socket。
sk.sendall(string)将string中的数据发送到连接的socket,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
sk.settimeout(timeout)设置socket操作的超时期,timeout是一个浮点数,单位是秒。超时期应该在刚创建socket时设置.
sk.accept()接受连接并返回(conn,address),其中conn是新的socket对象,可以用来接收和发送数据。address是连接客户端的地址。

2.2 简单实现 客户端与服务器 信息交互

服务器:

import socket
from loggers import log

# 1 创建socket套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
# 格式socket.socket(地址簇,socket类型)
# 返回一个通信套接字s,为本机向网络通信的接口。

# 2 绑定地址和端口
port = 18080
host = socket.gethostname() 
# 绑定本地地址,只有主机上的进程可以连接到服务器,如果host传空字符串,服务器将接受本机所有可用的 IPv4 地址。
s.bind((host, port))  


# 3 等待客户端连接
s.listen(3)
# 建立最多三个连接监听,在拒绝连接之前,操作系统可以挂起的最大连接数量。

# 4 连接成功后,发送/接收数据
while True:
    # (阻塞式)等待连接的到来
    conn, addr = s.accept()
    # conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
    log.info("欢迎{}连接".format(addr))
    while True:
        data = conn.recv(1024)
        dt = data.decode('utf-8')
        log.info("服务器收到{}".format(dt))
        log.info("服务器发送:")
        p = input()
        if p == 'quit':
            conn.close()
            s.close()
        else:
            conn.send(p1.encode('utf-8'))

客户端:

import sys
import socket
from loggers import log

def client():
    log.info("客户端启动")
    
    # 1 创建套接字
    c = socket.socket()
    port = 18080
    host = socket.gethostname()
    
    # 2 连接地址和端口
    c.connect((host, port))
    
    # 3 连接成功后,发送/接收数据
    while True:
        log.info("客户端发送:")
        a = input()
        if a == 'quit':
            c.close()
            sys.exit(0)
        else:
            c.send(a.encode('utf-8'))
            data = c.recv(1024)
            log.info("客户端收到:{}".format(data))

if __name__ == '__main__':
    client()

注意先运行服务器,再运行客户端,运行结果如下:
在这里插入图片描述
在这里插入图片描述

2.3 多线程实现服务器和客户端信息交互

  实例一实现了一个客户端与服务器的通信,但存在一些问题:不能多个客户端同时与服务器通信;发送和接收不能同时进行等。通过使用多线程,就可以解决一些问题。
服务器:

class MyServer(object):
    def __init__(self):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        host = socket.gethostname()
        port = 18080
        self.server.bind((host, port))
        self.server.listen(5)
        # 将客户端连接的socket,addr保存为键值对,放在字典中。
        self.socket_mapping = {}
        self.socket_list = []
        self.maxSize = 1024
        self.loc = threading.Lock()

    def run(self):
        """
        创建用户连接线程和发送信息线程
        """
        t2 = threading.Thread(name="client02", target=my_server.creat_client_socket, args=())
        t2.start()
        t1 = threading.Thread(name="client01", target=my_server.send_to_client, args=())
        t1.start()

    def send_to_client(self):
        """
        获取键盘输入并发送给客户端
        :param socket:
        :return:
        """
        while True:
            info = input('输入发送内容\n')
            print("您要发送的内容为:%s" % info)
            id = input('请选择需要发送的线程ID:0~' + str(len(self.socket_list) - 1) + '\n')
            print('输入的id为:', id)
            self.socket_list[int(id)].send(info.encode('utf-8'))

    def create_client_socket(self):
		"""
		连接客户端,开启连接客户端线程
		"""
        while True:
            soc, addr = self.server.accept()
            soc.send('success!'.encode('utf-8'))
            self.socket_list.append(soc)
            print("新的客户端", soc)
            threading.Thread(target=self.recv_from_client, args=(soc, addr)).start()

    def recv_from_client(self, soc, addr):
        """
        接收客户端信息
        :param socket:
        :return:
        """
        while True:
        	addr = addr
        	recv_info = soc.recv(self.maxSize).decode('utf-8')
        	print('服务器收到客户端{}的信息: {}'.format(addr, recv_info))

客户端:

import socket
import threading
from loggers import log

class MyClient:
    def __init__(self):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        host = socket.gethostname()
        port = 18080
        self.server.connect((host, port))
        self.maxSize = 1024

# 将收和发分别创建线程,可以解决收发不能同时进行的问题
    def run(self):
        """
		同时收发信息
        """
        tr = threading.Thread(target=self.text_recv)
        tr.start()
        ts = threading.Thread(target=self.text_send)
        ts.start()

# 收和发分别封装成函数
    def text_recv(self):
        """
        接收服务端发来的信息
        """
        while True:
            data = self.server.recv(1024)
            dt = data.decode('utf-8')
            log.info("客户端收到{}".format(dt))

    def text_send(self):
        """
        发送信息给服务器
        """
        while True:
            try:
                msg = input()
                self.server.sendall(msg.encode('utf8'))
                if msg == 'quit':
                    self.server.close()
            except Exception as e:
                log.error(e)

运行结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

问题记录:
  如下是服务器存在问题的一部分,连接多个客户端,并同时开启发送和接收线程。
  但在发送数据时,存在问题。假设有两个客户端0和1同时连接,在进行send_to_client时,想通过输入来选择发送给哪一个客户端,input()阻塞式输入,会一直等待输入,输入选择第0个客户端后,不继续往下执行之后的代码,会再执行客户端1的 “选择客户端”,然后再切换到客户端0的发送线程继续执行之后的输入信息,再切换到客户端1的发送线程继续执行。这样达不到选择客户端,并进行信息发送的目的。
  上述正确实现中,将发送信息线程单独拿出来,连接的多个客户端创建为多个线程,发送信息时,选择客户端线程发送信息。

    def run(self):
        """
        多线程同时收发信息
        """
       while True:
            socket, addr = self.server.accept()
            log.info("{}连接成功".format(addr))
            socket.send('success!'.encode('utf-8'))
            self.socket_mapping[socket] = addr
            threading.Thread(name="client01", target=self.send_to_client, args=(socket,)).start()
            threading.Thread(name="client02", target=self.recv_from_client, args=(socket,)).start()
    
    def send_to_client(self):
        """
        获取键盘输入,发送给客户端
        :param
        :return:
        """
        while True:
            socket_list = list(self.socket_mapping.keys())
            addr_list = list(self.socket_mapping.values())
            log.info("addr_list:{}".format(addr_list))
            print(len(addr_list))
            if len(self.socket_mapping) > 1:
                choose = input("选择客户端:")
                for i in range(len(addr_list)):
                    print(i)

#            if self.loc.acquire(True):
#                info = input("输入发送给客户端:")
#                if info != "quit" and len(info) > 0:
#                    socket_list[i].send(info.encode("utf-8"))
#                else:
#                    socket_list[i].close()
#                self.loc.release()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/194642.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UNIX环境高级编程_文件IO_共享文件操作

这篇笔记记录下,多进程(包括父子进程)操作同一个文件,或者同一个文件被同一个进程多次打开时的情况,总是傻傻分不清,必须记录一下了。 1 文件IO_共享文件 分下面四种情况记录。 情况1:同一进程多次open同一个文件 …

SAP MESSAGE :000 消息提示「BUG」

SAP MESSAGE :000 消息提示 错误消息 问题分析 前言&#xff1a;这个问题 DEBUG 模式没有抓到&#xff0c;所以才有了下文&#xff1a; 这是迁移程序时会出现的问题&#xff0c;参见下面的图片&#xff1a; 在迁移前&#xff0c;MATNR 转码 FUNC 就没有规范书写 sy-subrc <…

第十三章 常用类(包装类和String 相关类)

一、包装类 1. 包装类的分类 &#xff08;1&#xff09;针对八种基本数据类型相应的引用类型—包装类 &#xff08;2&#xff09;有了类的特点&#xff0c;就可以调用类中的方法。 2. 包装类和基本数据的转换 &#xff08;1&#xff09;jdk5 前的手动装箱和拆箱方式 public cl…

好友靠JVM成功进入阿里,阿里P8力荐的JVM笔记到底有什么魔力?

大家都是有经验的Java开发人员&#xff0c;想想为何要学习JVM? [面试?调优?装逼? ] 不管出于何种原因&#xff0c;总之你得先学好。那怎么学好呢? 每个人对于JVM的了解可能不一样&#xff0c;这就要考虑到怎么切入 既然大家都学习过Java&#xff0c;那不妨就从Java开始…

数据分析思维(四)|分类/矩阵思维

分类与矩阵思维 1、概念 在进行数据分析工作时&#xff0c;我们往往会涉及到多个核心指标&#xff0c;而对于不同数值核心指标的结合又会产生多种不同的结果&#xff0c;我们将相似结果的内容放到一起进行统一决策就会大大节省数据分析的时间&#xff0c;这种思想我们称之为分…

[python入门㊲] - python的继承

目录 ❤ 什么是继承 ❤ 派生和继承 ❤ 单继承 ❤ 多继承 ❤ MRO[方法搜索顺序](多继承顺序) ❤ 新式类和旧式&#xff08;经典&#xff09;类 ❤ 什么是继承 通过继承基类来得到基类的功能 所以我们把被继承的类称作父类或基类&#xff0c;继承者被称作子类 可…

注册外贸公司需要注意的问题

关于注册海外公司需要注意事项&#xff0c;米贸搜以美国为例&#xff0c;整理以下信息&#xff0c;希望可以帮助到你一、注册美国公司注意事项&#xff1a;1、拟定要注册的美国公司名称三个&#xff08;英文&#xff09;&#xff0c;核名如无重复则可使用&#xff1b;2、美国公…

matlab机电耦合系统相位分岔图

1、内容简介略638-可以交流、咨询、答疑2、内容说明略3、仿真分析clcclose allclear%% parameterglobal CC 48;tspan [0 5]; % 仿真时间x0 [0.1 0.1 0.1 0.1]; % 变量初始值[T,X] ode45(model_diff, tspan, x0); % 调用求解器X3_dot [0;diff(X(:,3))./diff(T)]; % 求解x3的…

【已解决】SpringCloudConfig客户端启动无法读取到配置参数

自己部署了一个Spring Cloud微服务项目&#xff0c;实践Spring Cloud Config分布式配置组件&#xff0c;按照Spring Cloud Config 资料Config&#xff1a;Spring Cloud分布式配置组件 先后创建了Eureka注册中心服务、 Spring Cloud Config Server服务、 Spring Cloud Config Cl…

极客星球 | Elasticsearch入门与实战技术分享

为持续夯实MobTech袤博科技的数智技术创新能力和技术布道能力&#xff0c;本期极客星球邀请了MobTech企业服务研发部工程师勤佳&#xff0c;从Elasticsearch集群安装、DSL语句讲解、深度分页、IK分词器、滚动索引等方面进行了阐述和分享。 一、集群环境安装 elasticsearch 是…

节后转岗“浪潮”来了!瞅准“趋势向上”的行业!

“跨行跨岗人员&#xff0c;怎么能顺利转行&#xff1f;”的话题一直存在&#xff01;成功人士给出一条转岗转行的原则&#xff1a;迁移到技能相近但趋势向上的岗位。那么&#xff0c;什么叫“技能相近”和“趋势向上”呢&#xff1f;让我们来看看。趋势向上除了技能相近&#…

现阶段元宇宙经常偏离原有的发展轨道,使其失去该有的功能和意义

人们总是会自然地陷入到约定俗成的俗套之中。对于元宇宙&#xff0c;同样未能免俗。即使是那些处于头部的玩家&#xff0c;亦不例外。比如&#xff0c;扎克伯格就仅仅只是将元宇宙看成了一个将脸书带离泥潭的工具&#xff0c;一味地迎合资本&#xff0c;而最终忽略了元宇宙最本…

日常避坑--input输入框type=number仍可以输入“e“,“.“等符号

问题发现在使用ElementUI的input框时候&#xff0c;我们有时候需要只让用户输入数字类型。这个时候你可能就会想到<input type"number">,思路没错&#xff0c;但是踩着坑啦。我定义了一个number类型的input框但是&#xff0c;输入框仍旧可以输入"e",…

hadoop安装(二、hadoop)(备忘)

hadoop安装hadoop更改文件配置配置core-site.xml配置hadoop-env.sh配置hdfs-site.xml配置mapred-site.xml配置yarn-site.xml配置环境hadoop安装安装hadoop 紧接上文&#xff0c;解压过的hadoop內部文件为 再进入etc内部的hadoop 修改hadoop313的权限 在/opt目录下&#xff0…

分享24个网页游戏源代码,总有一个是你想要的

分享24个网页游戏源代码 24个游戏源代码下载链接&#xff1a;https://pan.baidu.com/s/1gYJlj8enJbh5mFS_wMaZBA?pwd4ncb 提取码&#xff1a;4ncb 下面是项目的名字&#xff0c;我放了一些图片&#xff0c;大家下载后可以看到。 Html5JS网页版捕鱼达人游戏 HTML5水果忍者游戏…

杂谈---名言警句记录

我们总喜欢拿顺其自然来敷衍人生道路上的荆棘与坎坷,却很少承认真正的顺其自然其实是竭尽所能之后的不强求,而并非两手一摊的不作为.没有那一次巨大的历史灾难,不是以历史的进步作为补偿.今日长缨在手,何时缚住苍龙?男人遇到真爱时第一反应是胆怯&#xff0c;女人遇到真爱的第…

这份2023软件测试面试技巧,助你拿下满意offer

求职&#xff0c;就像打仗&#xff0c;不仅是一场挑战自己的战斗&#xff0c;也是一场与用人单位的较量。而求职者只有准备足够充分&#xff0c;才能在这场毫无硝烟的“战场”上取得胜利。那么软件测试面试需要做哪些准备以及软件测试面试需要哪些技巧呢&#xff1f;1、熟悉岗位…

JDBC数据库连接池

目录JDBC原理图1. 数据库连接的5种方式2.ResultSet底层3.Statement sql注入3.1 PreparedStatement4. Jdbc工具类4.1 工具类应用5.Jdbc事务6.批处理6.1 批处理原理7.数据库连接池7.1 c3p07.2 德鲁伊7.2.1 德鲁伊工具类8.Apache-DBUtils工具类8.1多条查询&#xff0c;BeanListHan…

Web3中文|火遍全网的去中心化推特「Damus」是什么?(附操作指南)

Damus是一个建立在去中心化网络上的社交软件&#xff0c;被称为“推特杀手”&#xff0c;现已在苹果应用商店上线。 1月31日&#xff0c;Damus团队在推特上证实了这一消息&#xff0c;此前该团队称已经被苹果公司拒绝了至少三次。 不久之后&#xff0c;Twitter联合创始人Jack…

Anaconda3安装

Anaconda3安装 step1 下载Anaconda 网址&#xff1a;https://www.anaconda.com/products/individual 点击红色方框中的Download下载最新版本的Anaconda软件&#xff0c;可选择windows/Linux/Mac系统。 Step2 安装过程 双击.exe文件 2.选择Next>,进行下一步。 3.点击 …