Python入门——网络编程-Socket(套接字)

套接字概括

三要素:
	1. IP地址: 网络环境下每一台计算机的唯一标识, 通过IP地址来找到指定的计算机
	2. 端口: 用于标识进程的逻辑地址, 通过端口来找到指定的进程
	3. 协议: 定义通信规则, 符合协议则可以通信, 否则无法正常通信
	
	端口: 程序间进行数据交互
	端口号: 程序在设备的唯一标识

TCP协议

TCP特点:
	面向连接: 通信双方必须先建立好连接才能进行数据传输,数据传输完成后,需要断开连接,以释放系统资源
	可靠传输: 
		1. 都建立了连接, 传输数据可靠
		2. 必须要先建立连接, 相对效率较低
		3. 大多数服务器程序都是使用TCP协议开发的,比如文件下载,网页浏览等

知名端口号: 0-1023(被系统占用或为保留端口)
动态端口号: 1024 - 65535

socket(简称 套接字)是进程之间的一个通信工具,好比家用电器工作都是基于插座进行,进程之间想要进行网络通信需要socket

socket负责进程之间的网络数据传输,好比数据的搬运工

两个进程之间通过socket进行相互通讯,就必须有服务端和客户端,服务端等待其他进程的连接,客户端主动连接服务端,都可以发送、接受数据


# socket(AddressFamily, Type)
# 参数 AddressFamily可以选择 AF_INET用于Internet进程间通信,实际工作中常用
# 参数 Type表示套接字类型, 可以是 SOCK_STREAM流式套接字,主要用于 TCP协议

tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 
# family=AddressFamily.AF_INET:这表示socket使用的地址族是IPv4(AF_INET)。如果你使用的是IPv6,这里会是AddressFamily.AF_INET6。
  • fd=368:这是socket的文件描述符(file descriptor),是操作系统用来标识和访问socket的一个数字。每个socket都有一个唯一的文件描述符。这个值在每次创建时都有可能不同
  • family=2:这表示socket使用的地址族。在Python的socket模块中,地址族通常是通过枚举来指定的,但在某些情况下(比如打印对象时),它们可能会以数值形式显示。2 通常对应于 socket.AF_INET,即IPv4地址族。
  • type=1:这表示socket的类型。同样,类型通常是通过枚举来指定的,但在这里以数值形式显示。1 通常对应于 socket.SOCK_STREAM,即面向连接的TCP协议。
  • proto=0:这表示socket使用的协议。对于TCP和UDP,这个值通常是0,因为它们的协议类型已经由地址族和类型参数指定了。

套接字详解

1. 什么是套接字(Socket)?

套接字是计算机网络编程的基础,是操作系统提供的一种通信机制,用于在两个节点之间传递数据。它是应用层与传输层之间的接口,允许程序在网络上传输数据,支持多种协议,如TCP、UDP等。

2. 套接字的基本工作原理

  • 套接字创建:程序创建一个套接字,并指定使用的协议(如TCP、UDP)以及网络层协议(如IPv4、IPv6)。
  • 绑定地址:服务器套接字会绑定到一个特定的IP地址和端口号,表示它将监听这个地址上的传入请求。
  • 监听:在服务器端,套接字会进入监听模式,等待客户端的连接。
  • 连接:客户端使用套接字连接到服务器的指定地址和端口。
  • 数据传输:一旦连接建立,客户端和服务器可以通过套接字发送和接收数据。
  • 关闭连接:数据传输完成后,双方关闭套接字以释放资源。

3. 套接字类型

  • 流式套接字(TCP,SOCK_STREAM
    • 这种套接字使用TCP协议,面向连接,提供可靠的、基于字节流的通信。
    • 特点:保证数据的有序性、可靠性,并处理丢包和数据重传。
    • 场景:文件传输、HTTP请求、数据库连接等需要可靠传输的场景。
  • 数据报套接字(UDP,SOCK_DGRAM
    • 这种套接字使用UDP协议,无连接,不保证数据的传输顺序和可靠性。
    • 特点**:速度快,开销小,适用于对数据可靠性要求不高但对效率要求高的场景。**
    • 场景:视频直播、在线游戏、VoIP等。

4. Python套接字编程

Python提供了socket模块来处理套接字编程。以下是TCP和UDP套接字编程的基本步骤和示例。


4.1 TCP 套接字编程

TCP 服务端

import socket

# 创建TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定地址(IP和端口)
server_socket.bind(('localhost', 8080))

# 开始监听,允许的最大连接数为5
# listen方法内接受一个整数传参数,表示接受的连接数量
server_socket.listen(5)
print("Server is listening on port 8080...")

while True:
    # 接收客户端连接
    # accept方法返回的是二元元组(客户端和服务端的本次链接对象,客户端地址信息)
    # accept()方法是 阻塞方法,若无连接,将不向下运行
    client_socket, addr = server_socket.accept()
    print(f"Connection established with {addr}")

    # 接收客户端数据
    # recv接受的是缓冲区大小,一般给1024即可
    # recv方法返回的是一个字节数组(bytes)对象
    # 可以通过 decode方法用UTF-8编码转换为字符串(解码)
    data = client_socket.recv(1024).decode('utf-8')
    print(f"Received from client: {data}")

    if data == 'exit':
        # 关闭客户端连接
        client_socket.close()
        # server_socket.close()

    # 发送数据给客户端
    """
    client_socket是在server_socket.accept()后创建的,
    它负责与客户端通信。server_socket只用于监听连接,而client_socket
    用于与特定客户端进行数据传输。
    encode可以将字符串 编码 为字节数组对象
    """
    replay = input(' ').encode('utf-8')
    client_socket.send(replay)

    # 关闭客户端连接
    client_socket.close()

TCP 客户端

import socket

# 1. 创建 socket对象
socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务端
socket_client.connect(('localhost', 8888))

# 3. 发送消息、接受返回消息
while True:
    data = input('客户端:')
    if data == 'exit':
        break
    socket_client.send(data.encode('UTF-8'))
    reply = socket_client.recv(1024).decode('UTF-8')
    print(f'服务端:{reply}')

# 4. 关闭连接
socket_client.close()

4.2 UDP 套接字编程

UDP 服务端

import socket

# 创建UDP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 绑定地址
server_socket.bind(('localhost', 8080))
print("UDP server is up and listening...")

while True:
    # 接收数据
    data, addr = server_socket.recvfrom(1024)
    print(f"Received from {addr}: {data.decode('utf-8')}")
    
    # 发送响应数据
    server_socket.sendto(b"Hello from UDP server!", addr)

UDP 客户端

import socket

# 创建UDP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 发送数据给服务器
client_socket.sendto(b"Hello from UDP client!", ('localhost', 8080))

# 接收服务器的数据
response, server_addr = client_socket.recvfrom(1024)
print(f"Received from server: {response.decode('utf-8')}")

# 关闭套接字
client_socket.close()


5. 套接字函数和方法

  • socket.socket(family, type):创建一个套接字对象。
    • family: 地址族(AF_INET 表示IPv4, AF_INET6 表示IPv6)。
    • type: 套接字类型(SOCK_STREAM 表示TCP,SOCK_DGRAM 表示UDP)。
  • socket.bind(address):将套接字绑定到一个地址(IP, 端口)。
  • socket.listen(backlog):使套接字进入监听模式,backlog指定最大等待连接的数量。
  • socket.accept():接受客户端连接,返回(客户端套接字,客户端地址)。
  • socket.connect(address):客户端使用该函数连接到服务器。
  • socket.recv(bufsize):接收数据,bufsize指定接收数据的最大字节数。
  • socket.send(data):发送数据,data是要发送的字节串。
  • socket.close():关闭套接字。
  • socket.recvfrom(bufsize):UDP套接字接收数据,返回(数据,发送者地址)。
  • socket.sendto(data, address):UDP套接字发送数据。

6. 套接字异常处理

在网络编程中,可能会遇到各种异常情况,如连接超时、网络中断等,因此在编写套接字程序时,必须处理这些异常。

import socket

try:
    # 尝试创建套接字
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 尝试连接到服务器
    client_socket.connect(('localhost', 8080))
    
    # 发送数据
    client_socket.send(b"Hello!")
    
except socket.error as e:
    print(f"Socket error: {e}")

finally:
    # 确保关闭套接字
    client_socket.close()


7. 套接字的超时设置

可以设置套接字的超时,以避免程序无限期等待某些操作。

import socket

# 创建TCP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 设置超时时间为5秒
client_socket.settimeout(5)

try:
    client_socket.connect(('localhost', 8080))
    client_socket.send(b"Hello!")
    
except socket.timeout:
    print("Connection timed out.")
    
finally:
    client_socket.close()


8. 总结

套接字是网络编程的核心技术之一,它提供了不同节点间的数据传输方式。理解和掌握套接字的使用方法可以帮助我们构建各种网络应用程序,例如聊天系统、Web服务器、文件传输工具等。

你可能感兴趣的:(Python入门,1024程序员节,python,开发语言,网络,青少年编程)