python-socket

socket

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

通俗来讲,socket表示一个网络连接,通过这个连接,使得主机间或者一台计算机上的进程间可以通讯。不管是不同主机,还是同一主机。既然是通信,必定有一个发送方,一个接收方。对应一个客户端,和一个服务端。

Socket

import socket
# 指定IPv4协议(AF_INET),IPv6协议请使用AF_INET6
# 指定使用TCP协议(SOCK_STREAM),UDP协议请使用SOCK_DGRAM
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

格式 : socket(family, type, protocal=0)

    
三个参数 : 1.常用协议族, 2.socket 类型, 3.指定的协议(默认0)
AF_INET(默认值)、AF_INET6、AF_UNIX、AF_ROUTE等
SOCK_STREAM(TCP类型)(默认值)、SOCK_DGRAM(UDP类型)、SOCK_RAW(原始类型)
通常不写, 默认为"0", 使用默认的协议和类

方法函数

发送数据

# 发送TCP数据,返回值:发送的字节当量
sk.send("data string")

# 完整发送TCP数据,频繁调用send方法,确保数据发送完成
sk.sendall("data string")

# 发送UDP数据
sk.sendto("data string",address)

接受数据

# 接收TCP数据,一次最大只接收1k数据
sk.recv(1024)

# 接收UDP数据,一次只接收1k数据,
# 返回值:数据和发送方ip
(data,address) = sk.recvfrom(1024)

获取socket信息

import socket

# 获取远程socket的addr,port
(addr, port) = sk.getpeername()

# 获取本地socket的addr,port
(addr, port) = sk.getsockname()


# 获取当前主机名
HostName = socket.gethostname()

# 获取当前主机的ip
HOST = socket.gethostbyname(HostName)

# 获取当前socket连接的文件描述符
file_no = sk.fileno()

设置socket

# 设置连接的超时时间
sk.settimeout(timeout)
sk.gettimeout()
 
# 设置为非阻塞模式,默认是0(阻塞)
# 非阻塞下,accept和recv时一旦无数据,则报错:socket.Error
sk.setblocking(1)

# 设置socket内部参数,
# 具体有哪些参数,可以查看socket类的python源码sk.setsockopt(level,optname,value)
sk.getsockopt(level,optname)
函数 说明
s.bind() 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址
s.listen() 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量,该值至少为1,一般为5就够用了
s.accept() 被动接受TCP客户端连接,(阻塞式)等待连接的到来
s.connect() 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误

公共函数

s.recv() 接收TCP数据,数据以字符串形式返回,缓冲区(bufsize)指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略
s.send() 发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小(提示: 在输入空的时候小于)
s.sendall() 完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常
s.recvfrom() 接收UDP数据,与recv()类似,但返回值是(data_bytes,address)。其中data_bytes是接收的bytes类型的数据,address是发送数据的地址, 以元组('IP', port)形式表示
s.sendto() 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数
s.close() 关闭套接字
s.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)
s.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level,optname,value) 设置给定套接字选项的值。
s.getsockopt(level,optname[.buflen]) 返回套接字选项的值。

socket工作原理

1、服务器绑定IP 并建立监听

客户端初始化Socket动态库后创建套接字,然后指定客户端Socket的地址,循环绑定Socket直至成功,然后开始建立监听,此时客户端处于等待状态,实时监控网络状态

2、客户端向服务端发送请求

客户端的Socket向服务器端提出连接请求,此时客户端描述出它所要连接的Socket,指出要连接的Socket的相关属性,然后向服务器端Socket提出请求

3、连接请求确认并建立

当服务器端套接字监听到来自客户端的连接请求之后,立即响应请求并建立一个新进程,然后将服务器端的套接字的描述反馈给客户端,由客户端确认之后连接就建立成功

4、连接建立成功之后发送数据

然后客户端和服务器两端之间可以相互通信,传输数据,此时服务器端的套接字继续等待监听来自其他客户端的请求(下面会一步步实现)

img

创建socket客户端

import socket

# 指定IPv4协议(AF_INET),IPv6协议请使用AF_INET6
# 指定使用TCP协议(SOCK_STREAM),UDP协议请使用SOCK_DGRAM
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 参数是一个tuple,tuple里指定服务器地址(域名或ip)和端口号
sock.connect(('www.sina.com.cn', 80))

# 注意这里str格式要遵循HTTP协议标准。
# 注意结尾一定要用 \r\n\r\n
sock.send("GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\n\r\n".encode("utf-8"))



1buffer = []
while True:
    # 每次最多接收1k字节
    d = sock.recv(1024)
    if d:
        # Python3接收到为bytes类型,要转为str
        buffer.append(str(d))
    else:
        break
data = ''.join(buffer)

创建socket服务端

import socket

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

# 注意以元组格式传入,可以是某网卡的公网ip,或0.0.0.0,或127.0.0.1
sock.bind(('127.0.0.1', 9999))

while True:
    # 接受一个新连接,阻塞的,只有接收到新连接才会往下走
    sock, addr = s.accept()
    # 每一次连接,都要创建新线程,否则一次只能处理一个连接
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()

def tcplink(sock, addr):
    while True:
        data = sock.recv(1024)
        if data == 'exit' or not data:
            break
        sock.send('Hello, %s!' % data)
    sock.close()

一个简单的聊天对话服务

客户端

import socket
import time

class ChatClient:
    def __init__(self, username, port):
        self.username = username
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect(("127.0.0.1", port))

    def send_msg(self, msg):
        self.socket.send("{username}::{msg}".format(username=self.username,msg=msg).encode("utf-8"))

    def recv_msg(self):
        data=self.socket.recv(1024)
        if data:
            print("\聊天室"+" "+time.strftime('%Y-%m-%d:%H:%M:%S',time.localtime(time.time())))
            print(data.decode("utf-8"))
            return True
        return False

    def main(self):
        data = self.socket.recv(1024)
        print(data.decode("utf-8"))
        msg = input("请输入消息:")
        self.send_msg(msg)
        while True:
            if self.recv_msg():
                msg=input("\n我:")
                self.send_msg(msg)
                if msg == "exit":
                    print("聊天室已关闭")
                    break

if __name__ == '__main__':
    cc = ChatClient(username="小明", port=8080)
    cc.main()py

服务端

import socket
import time
import threading
import requests
import json


class ChatServer:
    def __init__(self, port):
        # 绑定服务器的ip和端口,注意以tuple的形式
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind(("0.0.0.0", port))
        self.socket.listen(5)
        #授权码
        self.key = "your tuling robot key"
        print("正在监听 127.0.0.1 :{}...".format(port))

    def tcplink(self, sock, addr):
        # 每次连接,开始聊天前,先欢迎下。
        sock.send("你好,欢迎来到聊天室!".encode("utf-8"))
        while True:
            data = sock.recv(1024).decode("utf-8")
            print(sock.getpeername())
            print(sock.getsockname())
            print(sock.fileno())
            username = data.split("::")[0]
            msg = data.split("::")[1]
            if msg == "exit":
                break
            if msg:
                print(+username+time.strftime('%Y-%m-%d:%H:%M:%S',time.localtime(time.time())))
                print(msg)
                response = self.get_response(msg)
                sock.send(response.encode("utf-8"))
        sock.close()
        print("与 {} 结束聊天!".format(username))

    def get_response(self, info):
        # 调用图灵机器人API
        url = 'http://www.tuling123.com/openapi/api?key=' + self.key + '&info=' + info
        res = requests.get(url)
        res.encoding = 'utf-8'
        jd = json.loads(res.text)
        return jd['text']

    def main(self):
        while True:
            sock, addr = self.socket.accept()
            t=threading.Thread(target=self.tcplink, args=(sock, addr))
            t.start()

if __name__ == '__main__':
    cs = ChatServer(port=8080)
    cs.main()

参考文献: https://www.cnblogs.com/wumingxiaoyao/p/7047658.html

posted @ 2021-12-03 18:08  贝壳里的星海  阅读(168)  评论(0编辑  收藏  举报