socket

socket是什么:socket起源于linux的设计思想一切皆文件,即是对文件的打开,读写,关闭操作,而socket就是对这种思想模式的实现。所以说socket是一个特殊的文件,而常用的socket函数就是对其进行(打开,读写,关闭)。

socket工作流程:

服务器端

  1. 创建socket对象
  2. 绑定地址端口
  3. 无限循环监听处理请求
  4. 监听得到请求的ip
  5. 得到客户端发送的请求
  6. 构建服务器返回数据
  7. 返回数据 

客户端socket流程

  1. 创建socket对象
  2. 连接主机
  3. 构造发送请求
  4. 接收服务器返回信息

socket常用方法

import socket as sk
#创建socket对象
obj = sk.socket()
#绑定地址,传递形式为(host,port)
obj.bind(("127.0.0.1",8080))
#开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
sk.listen(backlog)
#是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错
sk.setblocking(bool)
# 接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
sk.accept()
#连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误
sk.connect(address)
# 同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061
sk.connect_ex(address)
# 关闭socket对象
sk.close()
#接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。
sk.recv(bufsize[,flag])
#与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
sk.recvfrom(bufsize[.flag])
#将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
sk.send(string[,flag])
#将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。内部通过递归调用send,将所有内容发送出去
sk.sendall(string[,flag])
#将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
sk.sendto(string[,flag],address)
# 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
sk.settimeout(timeout)
# 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
sk.getpeername()
# 返回套接字自己的地址。通常是一个元组(ipaddr,port)
sk.getsockname()
# 套接字的文件描述符
sk.fileno()

一个简单的socket 简单对话demo实现

服务器端

import socketserver

class Serv_my(socketserver.BaseRequestHandler):
        def handle(self):
            conn = self.request
            # conn.sendall("nihao, wo shi ji qi ren",encoding='utf-8')
            conn.sendall(bytes("ni hao wo shi ji qi ren.",encoding='utf-8'))
            while True:
                ret_bytes = conn.recv(1024)
                ret_str = str(ret_bytes, encoding='utf-8')
                if ret_str == 'q':
                    break
                print(ret_str)
                # conn.sendall(bytes(ret_str+"ni hao wo hao da jia hao",encoding='utf-8'))
                conn.sendall(bytes("你好呀,我是机器人。",encoding='utf-8'))

if __name__ == "__main__":
    server = socketserver.ThreadingTCPServer(("localhost",8000),Serv_my)
    server.serve_forever()

客户端

import socket

obj = socket.socket()
obj.connect(("localhost",8000))

ret_bytes = obj.recv(1024)
ret_str = str(ret_bytes,encoding='utf-8')

print(ret_str)

while True:
    inp = input("please input >>>:")
    if inp == 'q':
        obj.sendall(bytes(inp, encoding='utf-8'))
        break
    else:
        obj.sendall(bytes(inp,encoding='utf-8'))
        ret_bytes = obj.recv(1024)
        ret_str = str(ret_bytes, encoding='utf-8')
        print(ret_str)

socket传送图片的小demo

服务端

import socket

sk = socket.socket()#创建socket对象
sk.bind(("localhost",8000))#绑定地址
sk.listen(5)#最大监听数

while True:
    conn, address = sk.accept()#进行接收,conn套接字对象,address客户端连接地址
    conn.sendall(bytes("欢迎到乱七八糟界面:",encoding='utf-8'))#发送所有的数据,成功返回None,否则抛出异常
    size = conn.recv(1024)#接收套接字数据
    file_size = int(size)#

    conn.sendall(bytes("开始传送:",encoding='utf-8'))
    has_size = 0
    f = open('db_new.jpg','wb')
    while True:
        if file_size == has_size:
            break
        data = conn.recv(1024)#最多接收1024数据
        f.write(data)#写入db_new.jpg
        has_size += len(data)#对已保存的数据大小进行累加

    f.close()#关闭文件

客户端

import socket
import os
obj =socket.socket()#创建socket对象
obj.connect(("localhost",8000))#进行连接

ret_bytes = obj.recv(1024)#指定接收数据大小,并接收数据
ret_str = str(ret_bytes, encoding='utf-8')#对数据进行编码
print(ret_str)#

size = os.stat("xxxx000019.JPG").st_size#获取图片大小
obj.sendall(bytes(str(size),encoding='utf-8'))#发送图片大小参数
obj.recv(1024)#接收服务器传递的数据

with open("xxxx000019.JPG",'rb') as f:
    for line in f:
        obj.sendall(line)

UDP demo

import socket
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
sk.bind(("localhost",8000))

while True:
    data = sk.recv(1024)
    print(str(data, encoding='utf-8'))
import socket

sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:
    inp = input("输入:").strip()
    if inp == 'exit':
        break
    sk.sendto(bytes(inp, encoding='utf-8'),("localhost",8000))

sk.close()

IO多路复用概念:

io多路复用是一种机制,可以监视多个socket一旦某个socket就绪(读写就绪),就会通知程序进行响应的读写操作。

python中select模块实现了io的多路复用

import select
import socket

sk1 = socket.socket()
sk1.bind(("localhost",8000))
sk1.listen()

sk2 = socket.socket()
sk2.bind(("localhost",8000))
sk2.listen()

sk3 = socket.socket()
sk3.bind(("localhost",8000))
sk3.listen()

lin = [sk1,sk2,sk3]
while True:
    r_list,w_list,e_list = select.select(li,[],[],1)
    for line in r_list:
        conn, address = line.accept()
        conn.sendall(bytes("hi nihao.",encoding='utf-8'))

#select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。

#1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中

#2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中

#3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中

#4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化

#5、当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。

select 使用示例demo

服务端

import select
import socket

sk1 = socket.socket()
sk1.bind(("localhost",8000))
sk1.listen()

sk2 = socket.socket()
sk2.bind(("localhost",8001))
sk2.listen()

sk3 = socket.socket()
sk3.bind(("localhost",8002))
sk3.listen()

li = [sk1,sk2,sk3]

while True:
    r_list,w_list,e_list = select.select(li,[],[],1)
    for sk in r_list:
        if sk == sk1:
            conn,address = sk.accept()
            li.append(conn)
        else:
            ret = str(sk.recv(1024),encoding='utf-8')
            sk.sendall(bytes(ret+'suffix',encoding='utf-8'))

客户端

import socket

obj = socket.socket()

obj.connect(('localhost',8000))

while True:
    inp = input("Please(q\退出):\n>>>")
    obj.sendall(bytes(inp,encoding="utf-8"))
    if inp == "q":
        break
    ret = str(obj.recv(1024),encoding="utf-8")
    print(ret)

ThreadingTCPServer

  • 创建一个继承自 SocketServer.BaseRequestHandler 的类
  • 类中必须定义一个名称为 handle 的方法
  • 启动ThreadingTCPServer

SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)

感谢博主让我学到了这么多,借鉴链接:https://blog.csdn.net/flyingleo1981/article/details/83828600

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值