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

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’))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

客户端:

import sys
import socket
from loggers import log

def client():
log.info(“客户端启动”)

<span class="token comment"># 1 创建套接字</span>
c <span class="token operator">=</span> socket<span class="token punctuation">.</span>socket<span class="token punctuation">(</span><span class="token punctuation">)</span>
port <span class="token operator">=</span> <span class="token number">18080</span>
host <span class="token operator">=</span> socket<span class="token punctuation">.</span>gethostname<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token comment"># 2 连接地址和端口</span>
c<span class="token punctuation">.</span>connect<span class="token punctuation">(</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> port<span class="token punctuation">)</span><span class="token punctuation">)</span>

<span class="token comment"># 3 连接成功后,发送/接收数据</span>
<span class="token keyword">while</span> <span class="token boolean">True</span><span class="token punctuation">:</span>
    log<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">"客户端发送:"</span><span class="token punctuation">)</span>
    a <span class="token operator">=</span> <span class="token builtin">input</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">if</span> a <span class="token operator">==</span> <span class="token string">'quit'</span><span class="token punctuation">:</span>
        c<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span>
        sys<span class="token punctuation">.</span>exit<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token keyword">else</span><span class="token punctuation">:</span>
        c<span class="token punctuation">.</span>send<span class="token punctuation">(</span>a<span class="token punctuation">.</span>encode<span class="token punctuation">(</span><span class="token string">'utf-8'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        data <span class="token operator">=</span> c<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">1024</span><span class="token punctuation">)</span>
        log<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">"客户端收到:{}"</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span>

if name == main:
client()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

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

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()
<span class="token keyword">def</span> <span class="token function">run</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    创建用户连接线程和发送信息线程
    """</span>
    t2 <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>name<span class="token operator">=</span><span class="token string">"client02"</span><span class="token punctuation">,</span> target<span class="token operator">=</span>my_server<span class="token punctuation">.</span>creat_client_socket<span class="token punctuation">,</span> args<span class="token operator">=</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    t2<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>
    t1 <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>name<span class="token operator">=</span><span class="token string">"client01"</span><span class="token punctuation">,</span> target<span class="token operator">=</span>my_server<span class="token punctuation">.</span>send_to_client<span class="token punctuation">,</span> args<span class="token operator">=</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    t1<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>

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

<span class="token keyword">def</span> <span class="token function">create_client_socket</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
	<span class="token triple-quoted-string string">"""
	连接客户端,开启连接客户端线程
	"""</span>
    <span class="token keyword">while</span> <span class="token boolean">True</span><span class="token punctuation">:</span>
        soc<span class="token punctuation">,</span> addr <span class="token operator">=</span> self<span class="token punctuation">.</span>server<span class="token punctuation">.</span>accept<span class="token punctuation">(</span><span class="token punctuation">)</span>
        soc<span class="token punctuation">.</span>send<span class="token punctuation">(</span><span class="token string">'success!'</span><span class="token punctuation">.</span>encode<span class="token punctuation">(</span><span class="token string">'utf-8'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>socket_list<span class="token punctuation">.</span>append<span class="token punctuation">(</span>soc<span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"新的客户端"</span><span class="token punctuation">,</span> soc<span class="token punctuation">)</span>
        threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>target<span class="token operator">=</span>self<span class="token punctuation">.</span>recv_from_client<span class="token punctuation">,</span> args<span class="token operator">=</span><span class="token punctuation">(</span>soc<span class="token punctuation">,</span> addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">recv_from_client</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> soc<span class="token punctuation">,</span> addr<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    接收客户端信息
    :param socket:
    :return:
    """</span>
    <span class="token keyword">while</span> <span class="token boolean">True</span><span class="token punctuation">:</span>
    	addr <span class="token operator">=</span> addr
    	recv_info <span class="token operator">=</span> soc<span class="token punctuation">.</span>recv<span class="token punctuation">(</span>self<span class="token punctuation">.</span>maxSize<span class="token punctuation">)</span><span class="token punctuation">.</span>decode<span class="token punctuation">(</span><span class="token string">'utf-8'</span><span class="token punctuation">)</span>
    	<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">'服务器收到客户端{}的信息: {}'</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>addr<span class="token punctuation">,</span> recv_info<span class="token punctuation">)</span><span class="token punctuation">)</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

客户端:

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))

<span class="token keyword">def</span> <span class="token function">text_send</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    发送信息给服务器
    """</span>
    <span class="token keyword">while</span> <span class="token boolean">True</span><span class="token punctuation">:</span>
        <span class="token keyword">try</span><span class="token punctuation">:</span>
            msg <span class="token operator">=</span> <span class="token builtin">input</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            self<span class="token punctuation">.</span>server<span class="token punctuation">.</span>sendall<span class="token punctuation">(</span>msg<span class="token punctuation">.</span>encode<span class="token punctuation">(</span><span class="token string">'utf8'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">if</span> msg <span class="token operator">==</span> <span class="token string">'quit'</span><span class="token punctuation">:</span>
                self<span class="token punctuation">.</span>server<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">except</span> Exception <span class="token keyword">as</span> e<span class="token punctuation">:</span>
            log<span class="token punctuation">.</span>error<span class="token punctuation">(</span>e<span class="token punctuation">)</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

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

问题记录:
  如下是服务器存在问题的一部分,连接多个客户端,并同时开启发送和接收线程。
  但在发送数据时,存在问题。假设有两个客户端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()
<span class="token keyword">def</span> <span class="token function">send_to_client</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    获取键盘输入,发送给客户端
    :param
    :return:
    """</span>
    <span class="token keyword">while</span> <span class="token boolean">True</span><span class="token punctuation">:</span>
        socket_list <span class="token operator">=</span> <span class="token builtin">list</span><span class="token punctuation">(</span>self<span class="token punctuation">.</span>socket_mapping<span class="token punctuation">.</span>keys<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        addr_list <span class="token operator">=</span> <span class="token builtin">list</span><span class="token punctuation">(</span>self<span class="token punctuation">.</span>socket_mapping<span class="token punctuation">.</span>values<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        log<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">"addr_list:{}"</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>addr_list<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token builtin">len</span><span class="token punctuation">(</span>addr_list<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token builtin">len</span><span class="token punctuation">(</span>self<span class="token punctuation">.</span>socket_mapping<span class="token punctuation">)</span> <span class="token operator">&gt;</span> <span class="token number">1</span><span class="token punctuation">:</span>
            choose <span class="token operator">=</span> <span class="token builtin">input</span><span class="token punctuation">(</span><span class="token string">"选择客户端:"</span><span class="token punctuation">)</span>
            <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token builtin">len</span><span class="token punctuation">(</span>addr_list<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
                <span class="token keyword">print</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span>

# 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()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
文章知识点与官方知识档案匹配,可进一步学习相关知识
Python入门技能树基本技能网络编程 438668 人正在系统学习中
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值