首先以一个简单的hello/hi网络聊天程序作为示例,它使用了python提供的Socket API接口,程序的功能就是:客户端向服务器发送一条消息,服务器端返回一条消息给客户端
server.py
import socket host='127.0.0.1' port=1234 serv_sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) serv_sock.bind((host,port)) serv_sock.listen(10) #接收客户端套接字 clnt_sock,addr=serv_sock.accept() msg=clnt_sock.recv(1024) str_msg=msg.decode("utf-8") #返回给客户端消息 r_msg="Hi,"+str_msg[10:] clnt_sock.send(r_msg.encode("utf-8")) #关闭连接 serv_sock.close() clnt_sock.close()
client.py
import socket host='127.0.0.1' port=1234 sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect((host,port)) #向服务器发送数据 msg=input("input:") sock.send(msg.encode("utf-8")) #接收服务器返回的消息 recive=sock.recv(1024) print(recive)
sock.close()
在上述这个简单的聊天程序中,调用了python提供的下列网络函数
socket.
socket
(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)socket.
connect
(address),其中address是一个元组(host,port)socket.
bind
(address)socket.
listen
([backlog])socket.
accept
(),返回一个元组 (conn,address)socket.
send
(bytes[, flags])socket.
recv
(bufsize[, flags])
python的官方文档上提到,socket模块提供了访问BSD Socket的接口,上述的网络函数应该和系统提供的Socket API对应。使用strace这个工具,可以查看一个应用程序使用的的系统调用,接下来我会使用strace检查客户端用到的Linux Socket API。
只需要启动服务端,然后输入
strace python ./client.py
即可查看系统调用,其中和网络有关的系统调用信息如下
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3 connect(3, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0 sendto(3, "Hello,I'm liu", 13, 0, NULL, 0) = 13 recvfrom(3, "Hi,liu", 1024, 0, NULL, NULL) = 6 write(1, "Hi,liu\n", 7Hi,liu) = 7 close(3) = 0
可以从这些信息中看的python提供的网络接口API和Linux Socket API间一一对应的关系:
- 在通过socket.socket()创建套接字时,底层调用的是socket()这个API,创建的套接字文件描述符是3(Shell启动的进程会打开三个与标准输入输出相关的文件,描述符为0,1,2)
- socket.connect()对应的是connect() API,第一个参数指明文件描述符3,即在前面创建的套接字上建立连接,然后python帮我们自动填充了sockaddr类型的结构体
- 可以看到,尽管创建的是流式套接字,但底层发送、接收数据还是使用的sendto和recvfrom,不过后两个参数(目的/源地址及其长度)被设为了NULL,《Unix网络编程》中说,在TCP中,connect函数调用后可以使用sendto及recvfrom,但还是不清楚python为什么这样实现socket.send()和socket.recv()
服务器端的系统调用类似,其中socket.accept()函数调用了accpet来实现,并创建了新套接字(描述符为4),此处不再赘述