天天看点

Python实现网络通信中的套接字(Socket)

       Socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程 序通常通过"套接字"向网络发出请求或者应答网络请求。

      套接字用(IP地址:端口号)表示。

      它是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

Python实现网络通信中的套接字(Socket)

基于文件类型的套接字家族

套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

基于网络类型的套接字家族

套接字家族的名字:AF_INET

(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

基于TCP协议的socket通信

Python实现网络通信中的套接字(Socket)

        Python中socket是一个模块, 与网络通信相关的模块。利用python写一个程序,写一个客户端,写一个服务端,让其实现数据传输。写这样的程序就需要socket模块。

Python实现网络通信中的套接字(Socket)

        先从服务器端说起:服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。

       在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

服务器sever端
import socket

iphone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# socket.AF_INET 网络socket socket.SOCK_STREAM: 基于TCP协议的socket
iphone.bind(("127.0.0.1", 8848))
# 127.0.0.1本地回环地址,可以跟同一内网的其他ip通信
# 端口号: 1~1024 系统专用,1024 ~8000软件占用,8000~ 65535可以用于我们调试
iphone.listen(5)
# 同时监听数,服务端可以接受N个链接,但是只能同一时刻回应5个请求.
conn, addr = iphone.accept()  # 等待接入  没有接入就阻塞一直等待
print(conn)  # 管道
print(addr)  # 客户端的ip 端口
# 打印管道    # <socket.socket fd=212, family=AddressFamily.AF_INET, 
             # type=SocketKind.SOCK_STREAM, proto=0, 
             # laddr=('127.0.0.1', 8848), raddr=('127.0.0.1', 57733)>
# 打印管道    # ('127.0.0.1', 57733)
from_client_date = conn.recv(1024)  # 每次最多接受1024个字节
print(from_client_date.decode("utf-8"))
conn.send(from_client_date.upper())
conn.close()
iphone.close()
           
客户clinet端
import socket

iphone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
iphone.connect(("127.0.0.1", 8848))
iphone.send("hello".encode("utf-8"))
from_sever_date = iphone.recv(1024)
print(from_sever_date.decode("utf-8"))
iphone.close()
           

相关字段解释

import socket
socket.socket(socket_family,socket_type,protocal=0)
socket_family 可以是 AF_UNIX 或 AF_INET。
socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。
protocol 一般不填,默认值为 0。

获取tcp/ip套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

获取udp/ip套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

由于socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。
使用'from socket import *',我们就把 socket 模块里的所有属性都带到我们的
命名空间里了,这样能 大幅减短我们的代码。

例如tcpSock = socket(AF_INET, SOCK_STREAM)
服务端套接字函数
s.bind()    绑定(主机,端口号)到套接字
s.listen()  开始TCP监听
s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数
s.connect()     主动初始化TCP服务器连接
s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数
s.recv()            接收TCP数据
s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,
                    数据丢失,不会发完)
s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据
                    量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom()        接收UDP数据
s.sendto()          发送UDP数据
s.getpeername()     连接到当前套接字的远端的地址
s.getsockname()     当前套接字的地址
s.getsockopt()      返回指定套接字的参数
s.setsockopt()      设置指定套接字的参数
s.close()           关闭套接字

面向锁的套接字方法
s.setblocking()     设置套接字的阻塞与非阻塞模式
s.settimeout()      设置阻塞套接字操作的超时时间
s.gettimeout()      得到阻塞套接字操作的超时时间

面向文件的套接字的函数
s.fileno()          套接字的文件描述符
s.makefile()        创建一个与该套接字相关的文件
           

基于UDP协议的Socket通信

UDP是无链接的,先启动哪一端都不会报错,因此udp通信也不会出现黏包现象。

Python实现网络通信中的套接字(Socket)

       先从服务器端说起,服务器端先初始化Socket,然后与端口绑定(bind),recvform接收消息,这个消息有两项,消息内容和对方客户端的地址,然后回复消息时也要带着你收到的这个客户端的地址,发送回去,最后关闭连接,一次交互结束

# server端
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(("127.0.0.1", 8080))
while True:
    client_date, client_address = server.recvfrom(1024)
    ret = client_date.decode("utf-8")
    if ret == "bye":
        break
    print(ret)
    to_client_date = input("server>>>>").encode("utf-8")
    server.sendto(to_client_date, client_address)
server.close()
           
# client端
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
ip_port = ("127.0.0.1", 8080)
while True:
    to_server_date = input("client>>>>")
    client.sendto(to_server_date.encode("utf-8"), ip_port)
    if to_server_date == "bye":
        break
    server_date, server_addr = client.recvfrom(1024)
    print(server_date.decode("utf-8"))
client.close()
           

模拟类似于qq聊天的代码示例

import socket
ip_port=('127.0.0.1',8081)
udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 
# DGRAM:datagram 数据报文的意思,象征着UDP协议的通信方式
udp_server_sock.bind(ip_port)
# 你对外提供服务的端口就是这一个,所有的客户端都是通过这个端口和你进行通信的

while True:
    qq_msg, addr=udp_server_sock.recvfrom(1024)  # 阻塞状态,等待接收消息
    print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1], qq_msg.decode('utf-8')))
    back_msg=input('回复消息: ').strip()

    udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
           
import socket
BUFSIZE=1024
udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

qq_name_dic={
    'taibai':('127.0.0.1',8081),
    'Jedan':('127.0.0.1',8081),
    'Jack':('127.0.0.1',8081),
    'John':('127.0.0.1',8081),
}

while True:
    qq_name=input('请选择聊天对象:').strip()
    while True:
        msg=input('请输入消息,回车发送,输入q结束和他的聊天:').strip()
        if msg == 'q':
            break
        if not msg or not qq_name or qq_name not in qq_name_dic:
            continue
        udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
        # 必须带着自己的地址,这就是UDP不一样的地方,不需要建立连接,
        # 但是要带着自己的地址给服务端,否则服务端无法判断是谁给我发的消息,
        # 并且不知道该把消息回复到什么地方,因为我们之间没有建立连接通道

        back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
        # 同样也是阻塞状态,等待接收消息
        print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))

udp_client_socket.close()
           

参考链接:https://www.cnblogs.com/weizhixiang/p/6298523.html

                  http://c.biancheng.net/view/2124.html