一、网络编程中send函数用途

1、发送数据

在网络编程中,send函数主要用于发送数据,将数据(一般是字符串)打包后通过网络传输到另一端,该函数格式如下:

socket.send(bytes[, flags])

其中,bytes表示需要发送的数据,flags为可选参数,可以指定一些标志,如MSG_DONTWAIT、MSG_EOR等。它们将在后面的小标题中做详细介绍。

2、发送文件

在网络编程中,send函数还可以用于发送文件。一般是通过二进制方式读取文件,然后调用send函数将文件数据传输到另一端,示例代码如下:

with open('filename', 'rb') as f:
    while True:
        data = f.read(1024)  # 每次读取1024个字节
        if not data:
            break
        sock.send(data)

二、与socket结合的send函数使用方式

1、结合socket.send()方法使用

在与socket结合使用时,send函数可以与socket.send()方法结合使用,socket.send()只会将数据放入TCP/IP发送缓冲区,而当缓冲区有足够的空间时,send函数会将数据从缓冲区中发送出去,这样可以保证数据的完整性。示例代码如下:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('www.example.com', 80))
s.send(b'GET /index.html HTTP/1.1\r\nHost: www.example.com\r\n\r\n')
resp = b''
while True:
    r = s.recv(1024)
    if not r:
        break
    resp += r
print(resp.decode())

2、与socket.sendall()方法使用

与socket.send()方法不同,sendall方法会一直发送数据,直到数据发送完成。如果发送的数据量大于缓冲区大小,数据可能会分成多段发送。当所有数据发送完成后,函数会返回None。示例代码如下:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('www.example.com', 80))
s.sendall(b'GET /index.html HTTP/1.1\r\nHost: www.example.com\r\n\r\n')
resp = b''
while True:
    r = s.recv(1024)
    if not r:
        break
    resp += r
print(resp.decode())

三、send函数在异步编程中的应用

1、异步IO操作中的send函数

在异步编程中,协程一般会配合asyncio模块使用。send函数可以在异步IO操作中作为协程的接收者,每次调用时,协程会向send函数传入一个值,然后send函数会继续执行,直到下一次遇到yield关键字停下来。示例代码如下:

import asyncio

async def my_coroutine():
    print('Coroutine started')
    value = await asyncio.sleep(1)
    print(f'Coroutine resumed, value: {value}')
    return 'Coroutine returned'

async def main():
    cor = my_coroutine()
    print('Main coroutine started')
    try:
        print(await cor.send(None)) # 向协程my_coroutine传入None并调用send函数,协程会开始执行
        print(await cor.send(42)) # 向协程传入42并调用send函数,协程会继续执行
    except StopIteration as e:
        print(f'Coroutine raised an exception: {e.value}')

asyncio.run(main())

2、生成器中的send函数

在生成器中,send函数的作用与协程中的send函数类似,它可以向生成器中传入一个数据,并让生成器继续执行。随后生成器会返回一个数据给调用方。生成器中的send函数比它的“兄弟”yield要强大很多。示例代码如下:

def my_generator():
    value = yield 1
    print(f'Value received: {value}')
    yield 2

gen = my_generator()
print(next(gen)) # 输出1
print(gen.send('hello')) # 输出Value received: hello,然后输出2

四、send函数中常用flags参数详解

1、MSG_DONTWAIT

MSG_DONTWAIT参数被用来在非阻塞IO模式下立即返回。如果发送操作无法即时完成,函数将返回EAGAIN或EWOULDBLOCK错误码。示例代码如下:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(False) # 设置为非阻塞IO模式
try:
    s.send(b'Hello, world!', socket.MSG_DONTWAIT)
except socket.error as e:
    print(e)

2、MSG_EOR

MSG_EOR参数在SOCK_SEQPACKET类型的套接字中使用,它指定在发送缓冲区中的数据包的末尾处插入EOR标记。示例代码如下:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_SEQPACKET)
s.send(b'Hello, world!', socket.MSG_EOR)

3、MSG_MORE

MSG_MORE参数在SOCK_STREAM类型的套接字中使用,它指定这不是最后一部分数据,内核会在数据末尾添加TCP_NOPUSH标志,并将数据存储在发送缓冲区中。示例代码如下:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('www.example.com', 80))
s.send(b'GET /index.html HTTP/1.1\r\n', socket.MSG_MORE)
s.send(b'Host: www.example.com\r\n\r\n')
resp = b''
while True:
    r = s.recv(1024)
    if not r:
        break
    resp += r
print(resp.decode())

五、总结

通过以上几个方面的介绍,我们可以看出,send函数在python的编程中应用非常广泛,从网络编程到异步编程,再到生成器等等场景,都可以发挥重要作用。 相信通过本文,读者可以更好地理解send函数的诸多应用场景,并以此来提高自己的编程技术。