#服务端 import socket,sys server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) host = socket.gethostname() port = 8060 server.bind((host,port)) server.listen() while True: client,addr = server.accept() print('连接地址:',str(addr)) while True: data = client.recv(1024) print('Client(reciver):',data.decode()) msg = input('Server(Send):') client.send(msg.encode('utf-8')) client.close()
#客户端 import socket,sys client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) host = socket.gethostname() port = 8060 client.connect((host,port)) while True: msg = input('Client(Send):') client.send(msg.encode('utf-8')) rec = client.recv(1024) print('Server(reciver):',rec.decode()) client.close()
系统调用过程中,其中和网络有关的系统调用信息如下:
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()。