TCP 套接字 方法
- socket 函数:是 socket API 中的一个重要函数,用于创建一个套接字。套接字可以看作是网络通信的端点,它类似于电话插座,通过它可以建立网络连接并进行数据传输。在不同的编程语言和操作系统中,socket 函数的具体使用方法和参数可能会有所不同,但总体功能是相似的,即初始化一个套接字对象,为后续的网络通信操作做好准备。例如,在 Python 中,可以使用
import socket
导入 socket 模块,然后通过s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
这样的语句来创建一个 TCP 套接字(其中AF_INET
表示使用 IPv4 地址族,SOCK_STREAM
表示使用 TCP 协议)。
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
用于创建 TCP 套接字,socket.AF_INET
表示使用 IPv4 地址族,socket.SOCK_STREAM
表示使用 TCP 协议。socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
用于创建 UDP 套接字,socket.SOCK_DGRAM
表示使用 UDP 协议。bind
方法用于绑定服务器的 IP 地址和端口号。listen
方法用于监听客户端连接(仅 TCP 服务器)。accept
方法用于接受客户端连接(仅 TCP 服务器),返回客户端套接字和客户端地址。connect
方法用于客户端连接服务器(TCP 和 UDP 客户端)。send
和recv
方法用于 TCP 套接字发送和接收数据,sendto
和recvfrom
方法用于 UDP 套接字发送和接收数据。
TCP套接字编程详解_tcp/ip_霸道小明-开放原子开发者工作坊
listen函数的第一个参数时即将要监听的socket描述字,第二个参数为相应的socket可以排队的最大连接数。socket()创建的socket默认是一个主动类型,listen则将socket变成被动类型,等待客户连接请求。
对于调用listen进行监听的套接字,操作系统会为其维护2个队列:未完成队列,已完成队列。
- 当客户端发送TCP三次握手的第一次,服务器会在未完成队列创建一个对应项(之后就是等待TCP的第二次和第三次完成)
- 当三次握手完成后,该连接就会放在完成队列中,接着就可以执行accept函数从队列中移出。
listen的第二参数backlog则表示,这两个队列的和不能超过baklog。
TCP 套接字 方法
以下是 TCP 套接字的一些常见方法及其用法:
1. socket()
:创建套接字
- 语法:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- 示例:
收起
python
import socket tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- 这里创建了一个基于 IPv4(
AF_INET
)和 TCP 协议(SOCK_STREAM
)的套接字对象tcp_socket
。
- 这里创建了一个基于 IPv4(
2. bind()
:绑定地址(服务器端)
- 语法:
socket.bind((host, port))
- 示例:
收起
python
server_address = ('127.0.0.1', 8888) tcp_socket.bind(server_address)
- 将套接字绑定到指定的 IP 地址(
127.0.0.1
,本地回环地址)和端口号(8888
),以便服务器在该地址和端口上监听客户端连接。
- 将套接字绑定到指定的 IP 地址(
3. listen()
:监听连接(服务器端)
- 语法:
socket.listen(backlog)
- 示例:
收起
python
tcp_socket.listen(5)
- 使服务器套接字进入监听状态,
backlog
参数指定了在拒绝连接之前,操作系统可以挂起的最大连接数量。这里设置为5
,表示最多可以有5
个客户端连接在等待队列中。
- 使服务器套接字进入监听状态,
4. accept()
:接受连接(服务器端)
- 语法:
client_socket, client_address = socket.accept()
- 示例:
收起
python
client_socket, client_address = tcp_socket.accept() print(f"客户端 {client_address} 已连接")
- 当有客户端连接请求时,
accept()
方法会返回一个新的套接字对象client_socket
,用于与该客户端进行通信,同时返回客户端的地址client_address
。
- 当有客户端连接请求时,
5. connect()
:连接到服务器(客户端)
- 语法:
socket.connect((host, port))
- 示例:
收起
python
server_address = ('127.0.0.1', 8888) tcp_socket.connect(server_address)
- 客户端使用
connect()
方法连接到指定的服务器地址和端口。
- 客户端使用
6. send()
:发送数据
- 语法:
socket.send(data)
- 示例:
收起
python
message = "Hello, Server!" tcp_socket.send(message.encode())
- 将数据发送到连接的另一端。需要注意的是,
send()
方法返回实际发送的字节数,可能小于要发送的数据长度,需要根据返回值进行处理。
- 将数据发送到连接的另一端。需要注意的是,
7. recv()
:接收数据
- 语法:
data = socket.recv(buffer_size)
- 示例:
收起
python
received_data = tcp_socket.recv(1024) print(f"收到数据: {received_data.decode()}")
- 从连接的另一端接收数据,
buffer_size
指定了一次接收的最大字节数。recv()
方法会阻塞直到接收到数据或发生错误。
- 从连接的另一端接收数据,
8. close()
:关闭套接字
- 语法:
socket.close()
- 示例:
收起
python
tcp_socket.close()
- 关闭套接字,释放相关资源。无论是服务器端还是客户端,在通信结束后都应该关闭套接字。
这些是 TCP 套接字的基本方法,在实际应用中,还需要处理各种异常情况,如连接超时、连接拒绝、数据传输错误等,以确保网络通信的稳定性和可靠性。此外,还可以结合多线程或多进程技术来处理多个客户端的连接,提高服务器的并发处理能力。
image.png
import socket
import socket
是 Python 中导入socket
模块的语句。socket
模块提供了访问 BSD 套接字接口的功能,用于实现网络通信。以下是一些使用import socket
后的常见操作示例:
一、创建 TCP 服务器
收起
python
import socket# 创建TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定地址和端口
server_address = ('127.0.0.1', 8888)
server_socket.bind(server_address)# 监听连接
server_socket.listen(5)print('服务器启动,等待客户端连接...')while True:# 接受客户端连接client_socket, client_address = server_socket.accept()print(f'客户端 {client_address} 已连接')# 接收数据data = client_socket.recv(1024)print(f'收到客户端消息: {data.decode()}')# 发送数据response = 'Hello, client!'client_socket.send(response.encode())# 关闭客户端套接字client_socket.close()
二、创建 TCP 客户端
收起
python
import socket# 创建TCP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 连接服务器
server_address = ('127.0.0.1', 8888)
client_socket.connect(server_address)# 发送数据
message = 'Hi, server!'
client_socket.send(message.encode())# 接收数据
data = client_socket.recv(1024)
print(f'收到服务器回复: {data.decode()}')# 关闭套接字
client_socket.close()
三、创建 UDP 服务器
收起
python
import socket# 创建UDP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 绑定地址和端口
server_address = ('127.0.0.1', 9999)
server_socket.bind(server_address)print('UDP服务器启动,等待接收数据...')while True:# 接收数据和客户端地址data, client_address = server_socket.recvfrom(1024)print(f'收到来自 {client_address} 的消息: {data.decode()}')# 发送回复response = 'UDP Server received your message!'server_socket.sendto(response.encode(), client_address)
四、创建 UDP 客户端
收起
python
import socket# 创建UDP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 服务器地址
server_address = ('127.0.0.1', 9999)# 发送数据
message = 'Hello, UDP Server!'
client_socket.sendto(message.encode(), server_address)# 接收数据
data, server_address = client_socket.recvfrom(1024)
print(f'收到服务器回复: {data.decode()}')# 关闭套接字
client_socket.close()
这些示例展示了socket
模块在 TCP 和 UDP 通信中的基本用法,包括创建套接字、绑定地址、监听连接、接受连接、发送数据、接收数据和关闭套接字等操作。在实际应用中,还需要根据具体需求进行错误处理、数据编码解码、多线程 / 多进程支持等进一步的开发。
image.png
server_address = ('127.0.0.1', 8888) server_socket.bind(server_address)
以下是对这两行代码的详细解释:
1. server_address = ('127.0.0.1', 8888)
- 这行代码定义了一个服务器地址的元组。其中:
'127.0.0.1'
是一个特殊的 IP 地址,被称为本地回环地址(Loopback Address)。它用于在本地计算机上进行网络通信测试,即当你使用这个地址时,数据不会真正通过网络接口发送到外部网络,而是在本地计算机内部的网络协议栈中进行传输和处理。这样可以方便地在本地开发和测试网络应用程序,而无需依赖外部网络环境。8888
是一个端口号。端口号用于标识网络应用程序在计算机上的特定服务或进程。在同一台计算机上可以运行多个网络应用程序,每个应用程序通过不同的端口号来区分和接收数据。端口号的取值范围是 0 到 65535,其中 0 到 1023 是一些知名端口,被系统服务和常用应用程序占用,例如 HTTP 服务通常使用 80 端口,FTP 服务使用 21 端口等。这里选择 8888 端口是一个自定义的端口号,只要该端口没有被其他程序占用,就可以用于当前的网络应用程序。
2. server_socket.bind(server_address)
- 这行代码的作用是将创建的服务器套接字(
server_socket
)绑定到前面定义的服务器地址(server_address
)上。 - 套接字(Socket)是网络通信的端点,它就像一个电话插座,用于建立网络连接和传输数据。在服务器端,需要将套接字绑定到一个特定的地址和端口上,以便客户端能够知道如何连接到服务器。通过
bind
方法,服务器套接字就与指定的 IP 地址和端口号关联起来了,这样当客户端尝试连接到该 IP 地址和端口时,服务器就能接收到连接请求并进行相应的处理。
例如,在一个简单的 TCP 服务器程序中,这两行代码通常会出现在服务器初始化的部分,用于设置服务器的监听地址和端口,后续还会有listen
方法来监听连接请求,以及accept
方法来接受客户端连接等操作,共同实现服务器与客户端之间的网络通信。
以下是一个完整的简单 TCP 服务器示例,包含了上述代码以及后续的基本操作:
收起
python
import socket# 创建TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 定义服务器地址
server_address = ('127.0.0.1', 8888)# 绑定服务器地址到套接字
server_socket.bind(server_address)# 监听连接,参数5表示允许排队的最大连接数
server_socket.listen(5)print('服务器启动,等待客户端连接...')while True:# 接受客户端连接client_socket, client_address = server_socket.accept()print(f'客户端 {client_address} 已连接')# 接收客户端发送的数据,最多接收1024字节data = client_socket.recv(1024)print(f'收到客户端消息: {data.decode()}')# 向客户端发送响应数据response = 'Hello, client!'client_socket.send(response.encode())# 关闭与客户端的连接client_socket.close()
在这个示例中,服务器首先创建套接字,绑定地址,然后监听连接。当有客户端连接时,接受连接并进行数据的接收和发送,最后关闭与客户端的连接,继续等待下一个客户端连接。这是一个简单的网络通信模型,实际应用中可能会更加复杂,需要处理更多的错误情况、多客户端并发等问题。
image.png
socket API 中的 函数
socket API
(套接字应用程序接口)提供了一系列用于网络编程的函数,这些函数能够实现不同计算机上的进程之间的通信。以下是一些常见的socket API
中的函数及其功能:
1. socket()
- 功能:创建一个套接字(socket),它是网络通信的端点,类似于电话插座,用于建立网络连接和传输数据。
- 语法示例(Python):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
,这里创建了一个基于 IPv4(AF_INET
)和 TCP 协议(SOCK_STREAM
)的套接字对象s
。 - 用途:是网络编程的第一步,无论是服务器端还是客户端程序,都需要先创建套接字才能进行后续的网络操作。
2. bind()
- 功能:将套接字绑定到特定的地址(IP 地址和端口号)上,使得服务器能够在该地址上监听客户端的连接请求。
- 语法示例(Python):
server_socket.bind((host, port))
,如server_socket.bind(('127.0.0.1', 8888))
将服务器套接字绑定到本地回环地址127.0.0.1
的8888
端口。 - 用途:主要用于服务器端,确定服务器监听的网络地址和端口,以便客户端能够找到并连接到服务器。
3. listen()
- 功能:使服务器套接字进入监听状态,等待客户端的连接请求。
- 语法示例(Python):
server_socket.listen(backlog)
,其中backlog
参数指定了在拒绝连接之前,操作系统可以挂起的最大连接数量,例如server_socket.listen(5)
表示最多允许 5 个客户端连接在等待队列中。 - 用途:服务器在绑定地址后,调用
listen
函数开始监听客户端连接,是服务器启动过程中的重要步骤。
4. accept()
- 功能:在服务器端,当有客户端连接请求时,
accept
函数会接受该连接,并返回一个新的套接字对象(用于与该客户端进行通信)和客户端的地址信息。 - 语法示例(Python):
client_socket, client_address = server_socket.accept()
,client_socket
是与客户端通信的套接字,client_address
是客户端的地址(如('192.168.1.100', 50000)
)。 - 用途:用于服务器与客户端建立连接后,获取与客户端通信的套接字,以便后续进行数据的收发操作。
5. connect()
- 功能:客户端使用
connect
函数向服务器发起连接请求,尝试连接到指定的服务器地址(IP 地址和端口号)。 - 语法示例(Python):
client_socket.connect((server_host, server_port))
,例如client_socket.connect(('127.0.0.1', 8888))
表示客户端连接到本地127.0.0.1
地址的8888
端口的服务器。 - 用途:客户端通过
connect
函数与服务器建立连接,是客户端发起网络通信的关键步骤。
6. send()
和sendall()
- 功能:用于在已建立连接的套接字上发送数据。
send
函数尝试发送指定的数据,返回实际发送的字节数;sendall
函数则会持续发送数据,直到所有数据都被发送完毕或发生错误。 - 语法示例(Python):
bytes_sent = client_socket.send(data.encode())
或client_socket.sendall(data.encode())
,其中data
是要发送的数据字符串,需要先进行编码(如encode()
)。 - 用途:在客户端向服务器发送请求数据或服务器向客户端发送响应数据时使用,实现数据的传输。
7. recv()
- 功能:在已建立连接的套接字上接收数据,它会阻塞程序执行,直到接收到数据或发生错误。
- 语法示例(Python):
data = client_socket.recv(buffer_size)
,buffer_size
指定了一次最多接收的字节数,例如data = client_socket.recv(1024)
表示最多接收 1024 字节的数据。 - 用途:用于接收对方发送过来的数据,如客户端接收服务器的响应数据或服务器接收客户端的请求数据。
8. close()
- 功能:关闭套接字,释放与该套接字相关的系统资源,包括内存、端口等。
- 语法示例(Python):
socket.close()
,无论是服务器端还是客户端,在通信结束后都应该关闭套接字,以避免资源浪费和潜在的错误。 - 用途:在网络通信结束后,正确关闭套接字,确保系统资源的合理利用和程序的正常退出。
这些函数是socket API
中的核心部分,通过它们的组合使用,可以实现各种网络应用程序,如网络聊天工具、网页浏览器、服务器程序等。不同的编程语言在实现socket API
时可能会有一些语法和细节上的差异,但基本的功能和使用方式是相似的。
image.png
函数 方法
一、函数(Function)
- 定义与概念
- 函数是一段封装好的、可重复使用的代码块,用于执行特定的任务。它接受输入参数(也可以没有参数),经过一系列操作后,可能返回一个结果(也可以不返回结果)。函数的主要目的是将一个复杂的程序分解为多个较小的、易于管理和理解的部分,提高代码的复用性、可读性和可维护性。
- 例如,在 Python 中定义一个计算两个数之和的函数:
收起
python
def add_numbers(a, b):return a + b
- 这里
add_numbers
就是一个函数,它接受两个参数a
和b
,并返回它们的和。
- 这里
- 函数的特点
- 封装性:将实现特定功能的代码封装在函数内部,外部程序不需要了解函数内部的具体实现细节,只需要知道如何调用函数以及函数的功能和参数要求。这使得代码结构更加清晰,降低了不同部分代码之间的耦合度。
- 可复用性:可以在程序的不同地方多次调用同一个函数,避免了重复编写相同功能的代码。例如,在一个程序中需要多次计算不同数值的和,就可以反复调用
add_numbers
函数。 - 独立性:函数内部的变量和操作相对独立,不会影响到函数外部的代码,除非通过返回值或其他方式进行交互。这有助于提高程序的稳定性和可调试性,当函数出现问题时,可以更容易地定位和解决问题。
二、方法(Method)
- 定义与概念
- 方法是与对象相关联的函数,它定义了对象的行为或操作。方法只能通过特定的对象来调用,它可以访问和操作对象的属性(数据成员),并可以根据对象的状态执行相应的操作。在面向对象编程(OOP)中,类定义了对象的属性和方法,对象是类的实例,通过调用对象的方法来实现对对象的操作。
- 例如,在 Python 中定义一个简单的
Person
类,其中包含一个say_hello
方法:收起
python
class Person:def __init__(self, name):self.name = namedef say_hello(self):print(f"Hello, my name is {self.name}")
- 这里
say_hello
就是Person
类的一个方法,它可以通过Person
类的实例(对象)来调用,用于打印出对象的姓名。
- 这里
- 方法的特点
- 对象关联性:方法与特定的对象紧密相关,它操作的是对象的属性和状态。不同的对象调用相同的方法可能会产生不同的结果,因为它们的属性值可能不同。例如,创建两个
Person
对象person1
和person2
,它们调用say_hello
方法时会打印出各自不同的名字。 - 继承性(在 OOP 中):类可以继承父类的方法,并且可以根据需要重写(覆盖)这些方法,以实现不同的行为。这使得代码具有更好的扩展性和层次性,能够根据具体需求定制对象的行为。例如,定义一个
Student
类继承自Person
类,可以重写say_hello
方法来添加学生特有的信息。 - 访问控制(在部分编程语言中):可以通过访问修饰符(如
public
、private
、protected
等)来控制方法的访问权限,限制外部代码对方法的访问。这有助于保护对象的内部状态,确保数据的安全性和完整性。例如,在一些编程语言中,将某些方法设置为private
,只能在类内部使用,不允许外部代码直接调用。
- 对象关联性:方法与特定的对象紧密相关,它操作的是对象的属性和状态。不同的对象调用相同的方法可能会产生不同的结果,因为它们的属性值可能不同。例如,创建两个
三、函数与方法的区别与联系
- 区别
- 调用方式:函数通常是独立调用的,例如
add_numbers(3, 5)
;而方法必须通过对象来调用,例如person1.say_hello()
。 - 与对象的关系:函数不依赖于特定的对象,它可以在任何合适的地方被调用;方法与对象相关联,它操作对象的属性并反映对象的行为。
- 定义位置(在面向对象编程中):函数可以定义在全局作用域或模块内部;方法定义在类内部,是类的成员函数。
- 调用方式:函数通常是独立调用的,例如
- 联系
- 从本质上讲,方法也是一种函数,只是它具有与对象相关的特殊语义和调用方式。它们都是为了实现特定的功能,提高代码的组织性和复用性。在一些编程语言中,函数和方法的语法和实现方式可能有相似之处,只是在使用场景和语义上有所不同。例如,在 Python 中,函数和方法的定义语法相似,但方法的第一个参数通常是
self
,用于指代调用该方法的对象。在其他编程语言中,如 Java,方法必须定义在类内部,并且有更严格的访问控制和语法规则,但它们与函数的基本目的都是为了实现代码的模块化和功能封装。
- 从本质上讲,方法也是一种函数,只是它具有与对象相关的特殊语义和调用方式。它们都是为了实现特定的功能,提高代码的组织性和复用性。在一些编程语言中,函数和方法的语法和实现方式可能有相似之处,只是在使用场景和语义上有所不同。例如,在 Python 中,函数和方法的定义语法相似,但方法的第一个参数通常是
server_socket.listen
server_socket.listen
是socket
模块中用于使服务器套接字进入监听状态的方法,它的主要作用和特点如下:
一、作用
- 等待客户端连接
- 当服务器套接字调用
listen
方法后,它就开始在指定的端口上监听客户端的连接请求。操作系统会为该套接字维护一个连接请求队列,用于存放等待处理的客户端连接请求。例如,在一个 Web 服务器程序中,服务器套接字调用listen
后,就可以等待来自浏览器(客户端)的连接请求,以便为用户提供网页服务。
- 当服务器套接字调用
- 设置连接队列大小
listen
方法接受一个参数(通常称为backlog
),这个参数用于指定连接请求队列的最大长度,即操作系统在拒绝新的连接请求之前,可以同时挂起的最大连接数量。例如,server_socket.listen(5)
表示服务器最多可以同时处理 5 个未完成的连接请求,当第 6 个连接请求到来时,如果队列已满,操作系统将拒绝该请求(具体的拒绝行为可能因操作系统而异)。合理设置backlog
参数可以根据服务器的处理能力和预期的并发连接数量来优化服务器性能,避免因过多的连接请求导致服务器资源耗尽或响应变慢。
二、特点
-
阻塞操作(部分情况)
- 在一般情况下,
listen
方法本身不会阻塞程序的执行,它只是设置服务器套接字的监听状态。然而,当连接请求队列已满,并且又有新的连接请求到来时,根据操作系统的不同实现,listen
方法可能会表现出不同的行为。在一些操作系统中,listen
方法可能会阻塞当前线程,直到有可用的队列空间来接受新的连接请求;而在另一些操作系统中,listen
方法可能会立即返回一个错误,表示连接请求队列已满。因此,在编写服务器程序时,需要考虑到这种情况,并进行适当的错误处理和优化。
- 在一般情况下,
-
与其他套接字方法协同工作
listen
方法必须在服务器套接字绑定到一个地址(使用bind
方法)之后调用,并且在调用listen
之后,服务器通常会进入一个循环,不断调用accept
方法来接受客户端连接并处理连接后的通信。例如:收起
python
import socket# 创建TCP套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定地址 server_address = ('127.0.0.1', 8888) server_socket.bind(server_address)# 监听连接 server_socket.listen(5)print('服务器启动,等待客户端连接...')while True:# 接受客户端连接client_socket, client_address = server_socket.accept()print(f'客户端 {client_address} 已连接')# 这里可以进行与客户端通信的操作,如接收和发送数据等client_socket.close()
- 在这个示例中,
listen
方法在bind
之后被调用,使服务器进入监听状态,然后通过accept
方法接受客户端连接并进行后续处理,它们共同实现了服务器的基本功能。
- 在这个示例中,
-
影响服务器性能和资源利用
- 连接请求队列的大小(由
listen
方法的参数决定)直接影响服务器的性能和资源利用。如果队列设置过小,可能会导致一些客户端连接请求被拒绝,影响用户体验;如果队列设置过大,可能会占用过多的系统资源(如内存),因为每个等待的连接请求都需要占用一定的系统资源来维护连接状态。因此,需要根据服务器的实际应用场景和硬件资源情况,合理调整backlog
参数,以达到最佳的性能和资源利用平衡。例如,对于一个高并发的 Web 服务器,可能需要根据服务器的内存大小、CPU 性能以及预期的并发连接数等因素来确定合适的backlog
值,一般可能会设置为几百或几千,但具体数值需要经过性能测试和优化来确定。
- 连接请求队列的大小(由