socket 编程

# socket 编程,被翻译为‘套接字(ip+port)’是计算机之间进行通信的一种约定
# socket 是应用层与TCP/IP 协议族通信的中间软件抽象层,它是一组借口。在设计模式中就是一个门面模式,他把复杂的TCP/IP协议族隐藏在socket接口后面。
#   套接字主要有两种类型,分别是基于文件型和基于网络型
'基于文件类型的AF_UNIX,UNIX一切皆为文件,基于文件的套接字调用就是底层的文件系统来存取数据,两个套接字进程运行在同一机器,可以通过访问 同一个文件系统间接完成本机通信'

'基于网络类型的AF_INET 可以基于ipv4或ipv6,通过端口,可以实现本机或不同机器的2个应用程序之间的通信'

"""
python内置了2个常用的模块给我们完成socket编程
低级别的网络服务支持基本的socket,它提供了标准的BSD socket API,可以访问底层操作系统socket接口的全部方法
高级别的网络服务模块socketserver,它提供了服务器中心类,可以简化网络服务器的开发
"""
# socket 模块
import socket
'创建socket对象,也叫套接字对象'
"""
socket.socket(socket_family,socket_type,protocal=0)
参数: family 套接字家族(类型)
          基于文件类型的socket套接字家族
               AF_UNIX
          基于网路类型的socket套接字家族
               AF_INET  基于IPV4 网络(默认值)
               AF_INET6 基于IPV6 网络
      type 套接字数据传输格式
          SOCK_STREAM  字节流传输方式,基于TCP通信 (默认值)
          SOCK_DGRAM   数据报文传输方式,基于UDP通信

      protocal  一般不填,默认值为0
"""

# 获取TCP/IP 套接字
tcpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(tcpsocket)

# 获取UCP/IP 套接字
udpsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print(udpsocket)

"""
其中上面打印出来的 0.0.0.0 表示一个IPV4的特殊地址,表示当前机器中的任意网卡地址可以通过ip a 查看linux系统下的网卡地址
"""

# 也可以采用省略参数直接创建socket 套接字
sk = socket.socket

# 常用函数
"服务端套接字函数"
# s.bind(address)    将套接字绑定到地址,在AF_INET下,以元祖(host,port)的形式表示地址
# s.listen(backlog)  开始监听TCP传入连接,backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。通俗点说,就是TCP通信是默认一对一的,所以当服务器连接了一个客户端以后,其他的客户端访问到服务端时,就需要等待了,listen设置的backlog就是等待的客户端连接数量最多可以有几个
# s.accept()         接受TCP连接并返回连接结果(conn,address)其中conn是连接对象,可以用来接收和发送数据。address是连接的客户端地址

"客户端套接字函数"
# s.connect(address)   连接到address处的套接字,一般address的格式为元祖(hostname,port)如果连接出错,返回socket。error错误
# s.connect_ex(address) connect() 函数的拓展版本,成功返回0,出错返回error出错码,而不是抛出异常

"公共套接字函数"
# s.recv(bufsize[,flag])  基于TCP套接字接受数据,数据以bytes形式返回,bufsize指定要接收的最大数据量。flag忽略
# s.send(string,[,flag])  基于TCP套接字发送数据,将string中的数据发送到连接的对端套接字。返回值是要发送的字节数量。string格式是bytes类型
# s.recvfrom(bufsize,[,flag]) 基于UDP套接字接收数据,与recv()类似,但返回值是(data,address)其中data是包含接收数据的bytes字符串,address是发送数据的对端套接字地址,是一个元祖
# s.sendto(string[,flag], address) 基于UDP套接字发送数据。将数据string发送到对端套接字,address是形式为(ipaddr,port)的元祖,指定对端的套接字地址。返回值是发送的字节数。string也是bytes字符串
# s.close()  关闭套接字,释放socket对象"""
实现基于 tcp协议的socket通信
先从服务器端说起,服务器端先初始化socket得到套接字对象,然后绑定端口(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个socket得到套接字对象,然后连接服务器(connect)成功,这时客户端与服务端的连接就建立了(socket内部帮我们完成了三次握手)。客户端发送数据请求,服务器端接收请求并处理,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,通信结束(同样的,socket内部帮我们完成了四次挥手)
"""
# 最初版本:基本实现通信

服务端代码
 

import socket   #服务端
sk1 = socket.socket()      #创建套接字对象
addr = ("127.0.0.1", 9000)  #绑定通信端口
sk.bind(addr)
print(f"服务端绑定通信端口,addr={addr}")

sk.listen()  #监听端口,backlog 指定允许等待连接的客户端数量,默认为1,一般工作中设置为5
print(f"开始监听通信端口")

conn, addr = sk.accept()  #accept等待连接,程序会阻塞,直到tcp协议的三次握手建立完毕,才会继续执行后面的代码
print(f"有客户端{addr}连接过来了,conn={conn}")

content = conn.recv(1024)   #基于TCP套接字接收数据,最大可以接收1024个字节
                            #recv等待接收对端发送的数据,程序会阻塞,收到数据之后,才会继续执行后面的代码
print(f"接收到对端发送过来的数据,content={content}")

message = input(">:")
conn.send(message.encode("utf-8"))   # send 基于TCP套接字发送数据,只能发送二进制的字节流

conn.close()   #释放连接【内部会执行四次回收】

sk1.close()    # 关闭socket对象 [退还占用的端口]

"""客户端"""

import socket
sk2 = socket.socket()   #创建TCP套接字对象(通信的双方,必须保证socket类型与socket传输方式要一致)

addr = ("127.0.0.1", 9000)  # 连接服务器
sk.connect(addr)
print("成功连接服务端")

message = input(">:")    # 基于TCP套接字发送消息(发送的数据是二进制字节流)
sk2.send(message.encode("utf-8"))   # 因为windows 系统默认是使用gbk编码,所以统一指定编码为utf-8

content = sk2.recv(1024)   #接收消息(发送完之后,程序添加阻塞,最大字节1024,等待接收)基于TCP套接字接收数据,最大可以接收1024个字节
print(f"接收到对方发送过来的数据,content={content}")  #recv等待接收对方发送的数据,程序会阻塞,收到数据之后,才会继续执行后面的代码

sk2.close()  #关闭连接以及释放资源


"""
进阶版本:循环收发消息
上面只实现了客户端只能发送一次数据给服务端,而服务端也只能接收一次客户端发送的数据,所以有需要引人无限循环多次发送接收数据
"""

 服务端代码

import socket
sk3 = socket.socket()
addr = ("127.0.0.1", 9000)
sk3.bind(addr)
sk3.listen()
coon, addr = sk3.accept()
while True:   # 增加无限循环,让服务器可以多次接收来自对端发送的数据
    content = conn.recv(1024).decode('uff-8')
    if content == "exit" :break
    if content:
        print(f"{addr}:{content}")


conn.close()
sk3.close()

"""客户端代码"""

import socket
sk4 = socket.socket()
addr = ("127.0.0.0", 9000)
sk4.connect(addr)
while True:
    message = input(">:")
    sk4.send(message.encode("utf-8"))
    if message == "exit":break

sk4.close()

# 上述例子只能通过客户端发送数据给服务端,但是服务端没有回应数据,所以针对上述进行改造,让服务端应答客户端
"""
进阶版本:服务端可以应答客户端
实现服务端原封不动返回客户端发送的数据,叫应答服务器。就是服务器接收了客户端的数据之后,不进行任何处理,直接返回客户端,应答服务器,通常用于测试
"""

'服务端代码'

import socket
sk5 = socket.socket()
addr = ("127.0.0.1", 9000)
sk5.bind(addr)
sk5.listen(5)
conn, addr = sk5.accept()
while True:
    content = conn.recv(1024).decode("utf-8")
    if content == "exit":break
    if content:
        print(f"addr={addr},content={content}")
        # 应答,也叫回显,或echo
        # 表示原封不动的把客户端发送过来的数据返回客户端
        conn.send(content.encode("utf-8"))

conn.close()
sk5.close()

'客户端代码'

import socket
sk6 = socket.socket()
addr = ("127.0.0.1", 9000)
sk6.connect(addr)
while True:
    message = input(">:")
    sk6.send(message.encode("utf-8"))
    if message == "exit":break
    content = sk6.recv(1024).decode("utf-8")
    if content:
        print(f"服务端:{content}")
        
sk.close()

# 以上可以服务端与客户端双方互相发送数据
'如果存在多个多个客户端同时连接服务端,服务端只会连接第一个客户端,后续的其他客户端处于阻塞状态。基于TCP协议,默认是一对一的'
"进阶版本:实现服务器不断接收的连接新客户端"
服务端代码

import socket
sk7 = socket.socket()
laddr = ("127.0.0.1", 9000)  #  绑定通信端口  local 本地,本端 remote 外地,远程,对端
sk7.bind(laddr)   
sk7.listen(5)    # 监听通信端口,backlog参数设置阻塞等待的客户端连接数量,默认为1
while True:     # 再次增加无限循环,实现让服务端可以和多个客户端不断的建立三次握手、收发消息、四次挥手
    conn, raddr = sk7.accept()
    print("客户端连接了服务器")
    while True:
        content = sk7.recv(1024).decode("utf-8")
        if content == "exit":break           # 如果客户端关闭连接,则服务端也要关闭与当前客户端的连接
        if content:
            print(f"{raddr}:{content}") 
            conn.send(content.encode("utf-8"))   # 表示原封不动的把客户端发送过来的数据返回给客户端
    
        conn.close()   # 四次挥手  

     
        
客户端1代码
 

import socket

# 创建TCP套接字对象
sk8 = socket.socket()

# 连接服务端
addr = ("127.0.0.1", 9000)
sk8.connect(addr)

# 增加无限循环,让客户端可以多次发送数据给服务端
while True:
    message = input(">: ")
    sk8.send(message.encode("utf-8"))
    if message == "exit": break  # 客户端退出
    # 接收服务端发送的数据
    content = sk8.recv(1024).decode("utf-8")
    if content:
        print(f"服务端: {content}")

sk8.close()

客户端2代码

import socket

# 创建TCP套接字对象
sk9 = socket.socket()

# 连接服务端
addr = ("127.0.0.1", 9000)
sk9.connect(addr)

# 增加无限循环,让客户端可以多次发送数据给服务端
while True:
    message = input(">: ")
    sk9.send(message.encode("utf-8"))
    if message == "exit": break  # 客户端退出
    # 接收服务端发送的数据
    content = sk9.recv(1024).decode("utf-8")
    if content:
        print(f"服务端: {content}")

sk9.close()

# 进阶版本:服务端发送自定义数据

# TCP通信默认属于一对一的通信,服务端除了可以回显客户端的数据,也可以发送自定义数据,实现类似2个人聊天的效果。
服务端代码

import socket

# 创建TCP套接字对象
sk = socket.socket()

# 绑定通信端口
# local 本地,本端
# remote 外地,远程,对端
laddr = ("127.0.0.1", 9000)
sk.bind(laddr)

# 监听通信端口,backlog参数设置阻塞等待的客户端连接数量,默认为1
sk.listen(5) # 重点!!让多个客户端可以阻塞等待与服务端建立连接!!!# 再次增加无限循环,实现让服务端可以和多个客户端不断的建立三次握手、收发消息、四次挥手
while True:
    # 接收客户端连接(三次握手)
    conn, raddr = sk.accept()
    print(f"客户端{raddr}连接了服务器!")
    # 增加无限循环,让服务端可以多次接收来自对端发送的数据
    while True:
        content = conn.recv(1024).decode('utf-8')
        # 如果客户端关闭连接,则服务端也要关闭与当前客户端的连接
        if content == "exit": break
        if content:
            print(f"{raddr}: {content}")
            # 服务端也可以发送数据给客户端【将来就是通过数据库中的资源数据提供给客户端】
            message = input(">:")
            conn.send(message.encode("utf-8"))
    # 四次挥手
    conn.close()


    
客户端代码

import socket

# 创建TCP套接字对象
sk = socket.socket()

# 连接服务端
addr = ("127.0.0.1", 9000)
sk.connect(addr)

# 增加无限循环,让客户端可以多次发送数据给服务端
while True:
    message = input(">: ")
    sk.send(message.encode("utf-8"))
    if message == "exit": break  # 客户端退出
    # 接收服务端发送的数据
    content = sk.recv(1024).decode("utf-8")
    if content:
        print(f"服务端: {content}")

sk.close()


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值