计算机网络基础——套接字

套接字

基于tcp协议的套接字

一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理。
在这里插入图片描述

案例

用代码的形式进行通信:

模拟客户端:

import socket

# 1 、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2、拨通电话
phone.connect(('127.0.0.1', 8080))

# 3、通信
phone.send('hello egon 哈哈哈'.encode('utf-8')) #是以Bytes类型发送的信息
data = phone.recv(1024)
print(data.decode('utf-8'))

# 4、关闭链接
phone.close()
'''
服务端发送的消息: HELLO EGON 哈哈哈
'''

模拟服务端:

import socket

# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 流式协议=》tcp协议

# 2、绑定手机卡
phone.bind(('127.0.0.1', 8080))  #绑定ip+端口 服务端的ip是固定不变的

# 3、开机
phone.listen(5)  # 5指的是半连接池的大小

# 4、等待电话链接请求
conn, client_addr = phone.accept()
print(conn)
print('客户端的ip和端口:', client_addr)

# 5、收消息
data = conn.recv(1024)  # 最大接收的数据量为1024Bytes,收到是bytes类型
print('客户端发来的消息:',data.decode('utf-8'))
conn.send(data.upper())

# 6、关闭电连接conn
conn.close()

# 7、关机(可选操作)s
phone.close()


'''
<socket.socket fd=340, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, 
laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 50425)>
客户端的ip和端口: ('127.0.0.1', 50425)
客户端发来的消息: hello egon 哈哈哈
'''

如果将客户端发送的信息定义为用户输入,只需加入input(),如果想让通信循环,也只需加入while语句,当用户输入quit时,退出通信

客户端:

while True:
    msg = input("请输入你要发送的信息:").strip()
    phone.send(msg.encode('utf-8'))
    if msg == 'quit':
        break
    data = phone.recv(1024)
    print('服务端发送的消息:', data.decode('utf-8'))

服务端:

while True:
    data = conn.recv(1024)  # 最大接收的数据量为1024Bytes,收到是bytes类型
    if data.decode('utf-8') == 'quit':
        break
    print('客户端发来的消息:', data.decode('utf-8'))
    conn.send(data.upper())

但其实其中是有两处BUG的:

第一处BUG:
当客户端输入“ ”空格时,服务端不会返回消息,而是会处于一直在接收信息的状态。
要理解其中的原因,要先了解socket对于信息的收发原理
在这里插入图片描述
当客户端进行发送消息时,不会直接发送到服务端进行响应,也就是说客户端的send()发生,服务端的recv( )也不会第一时间就响应,而是客户端send()发送数据给自身机器的内存中,再由操作系统,通过网卡等物理设施,将信息传送给服务端的物理设施,物理设施再将信息存入服务端的内存,服务端的recv()再进行接送数据。

所以在上述的bug中,客户端可以进行发送空格,但是在服务端的内存中,并没有如何的数据可以让服务端的recv接收,也就导致了客户端无法收到服务端的回应。
在实际应用中,空格也是不允许被发送的。所以解决方法就是对用户的输入进行判断,如果是空格就continue,让用户重新输入。

msg = input("请输入你要发送的信息:").strip()
    if len(msg) == 0:
        continue

第二处BUG:当客户端与服务端进行通信时,客户端突然结束进程,服务端就会出错:
(window平台)

Traceback (most recent call last):
  File "D:\python服务端.py", line 19, in <module>
    data = conn.recv(1024)  # 最大接收的数据量为1024Bytes,收到是bytes类型
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。

如果是unix平台,服务端会出现死循环,一旦data收到空,就以为是一种异常行为:客户非法断开了链接。

    if len(data) == 0:
        break

针对window:捕捉异常处理

while True:
    try:
        data = conn.recv(1024)  # 最大接收的数据量为1024Bytes,收到是bytes类型
        if len(data) == 0:
            break
        if data.decode('utf-8') == 'quit':
            break
        print('客户端发来的消息:', data.decode('utf-8'))
        conn.send(data.upper())
    except Exception:
        break

此时,服务端不会报错,但是还是有优化的地方:当客户端断开链接时,服务端应该继续进行等待接收的状态,而不是直接关闭,这就牵扯到了链接循环问题。

套娃循环:

while True:
    conn, client_addr = phone.accept()
    print(conn)
    print('客户端的ip和端口:', client_addr)
    while True:
        try:
            data = conn.recv(1024) 
            if len(data) == 0:
                break
            if data.decode('utf-8') == 'quit':
                break
            print('客户端发来的消息:', data.decode('utf-8'))
            conn.send(data.upper())
        except Exception:
            break

    conn.close()

基于udp协议的套接字通信

udp是无链接的,先启动哪一端都不会报错

udp服务端

1 ss = socket()   #创建一个服务器的套接字
2 ss.bind()       #绑定服务器套接字
3 inf_loop:       #服务器无限循环
4     cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
5 ss.close()                         # 关闭服务器套接字
udp客户端

cs = socket()   # 创建客户套接字
comm_loop:      # 通讯循环
    cs.sendto()/cs.recvfrom()   # 对话(发送/接收)
cs.close()                      # 关闭客户套接字

案例

客户端

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

while True:
    msg = input('请输入信息:').strip()
    if msg == 'quit':
        break
    client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
    data, server_addr = client.recvfrom(1024)
    print(data.decode('utf-8'))
client.close()

服务端

# 基于udp协议
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 数据报协议=>udp协议

server.bind(('127.0.0.1', 8080))

while True:
    data, client_addr = server.recvfrom(1024)
    if data.decode('utf-8') == 'quit':
        break
    print('客户端传递的信息:', data.decode('utf-8'))
    server.sendto(data.upper(), client_addr)

server.close()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值