Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

[Python]-17-socket编程

$
0
0
引言

这篇文章介绍python内置socket模块,使用这个模块模拟一个HTTP GET请求,并将服务器返回的数据保存到本地。

文章目录 0×1.使用TCP传输数据

在互联网上,客户机与服务器通信,实际上是两台机器上的两个不同进程之间的通信,而我们经常听说的socket,只是一个抽象的概念,它通常表示一台机器上所打开的一个连接到目标的"网络接口",这个网络接口包含了目标机器的IP地址,端口号,以及双方使用的协议类型;

下面这段代码使用socket向本站所在服务器发送了一个HTTP GET请求,然后将服务器返回的HTML数据保存到本地的qingsword.html文件中:

#!/usr/bin/env python3 #coding=utf-8 #导入socket模块 import socket #初始化一个socket对象,AF_INET表示使用IPv4协议(AF_INET6表示IPv6协议),SOCK_STREAM表示这个socket使用TCP通信(UTP为SOCK_DGRAM) s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) #连接到www.qingsword.com的80端口,括号中是一个元组 s.connect(("www.qingsword.com",80)) #发送GET请求,请求服务器/目录,请求协议版本HTTP1.1,在请求完成后关闭连接 s.send("GET / HTTP/1.1\r\nHost: www.qingsword.com\r\nConnection: Close\r\n\r\n".encode("utf-8")) #上面的语句可以分开写成下面的样子,在字符串前使用b前缀和在字符串后使用.encode("utf-8")都能将字符串转码成可用于网络传输的字节码(在要发送的字符串中包含中文时,推荐使用.encode("utf-8")转码) #s.send(b"GET / HTTP/1.1\r\n") #s.send(b"Host:www.qingsword.com\r\n") #s.send(b"Connection:Close\r\n\r\n") #初始化一个列表用于接收服务器返回的数据 buffer=[] while True: #每次读取1024个字节 d=s.recv(1024) if d: #如果还有数据,将其添加到列表中 buffer.append(d) else: break #将列表中的数据使用空字符串连接成一个整体 data="".encode("utf-8").join(buffer) s.close() #关闭socket #使用split将data分割一次(遇到第一个"\r\n\r\n"分割成前后两部分) header,html=data.split("\r\n\r\n".encode("utf-8"),1) #将第一部分服务器返回的HTTP头部信息解码后打印出来 print(header.decode("utf-8")) #将第二部分HTML信息保存到当前脚本所在目录中的qingsword.html文件中 with open("qingsword.html","wb") as f1: f1.write(html) 0×2.socket客户端&服务端模型

下面是一个简单的客户端到服务端socket通信模型实例:

服务端:

#!/usr/bin/env python3 #coding=utf-8 from multiprocessing import Process import socket,time #用于处理新的连接,接收新连接的socket和IP:port def Hello_Socket(sock,addr): #addr包含了客户端主机的IP和端口号 print("创建新的客户端连接成功,%s:%s"%addr) #发送一个欢迎信息给客户端 sock.send("欢迎!".encode("utf-8")) while True: #从客户端读取消息,如果消息为空或读取到exit,退出循环 data=sock.recv(1024) time.sleep(1) #模拟网络延迟 if not data or data.decode("utf-8")=="exit": break #在读取到的消息前添加Hello,再发送回客户端 sock.send(("Hello %s"%data.decode("utf-8")).encode("utf-8")) #关闭连接 sock.close() print("%s:%s连接断开..."%addr) if __name__=="__main__": so=socket.socket(socket.AF_INET, socket.SOCK_STREAM) Local_IP_Addr="127.0.0.1" Local_Port=8899 #服务端打开一个socket,并且绑定本地网卡IP和端口号,客户端通过服务端这个IP以及端口和服务器通信 so.bind((Local_IP_Addr,Local_Port)) #监听端口,最多同时支持10个客户端连接 so.listen(10) print("在本地接口%s:%s等待客户端连接...."%(Local_IP_Addr,Local_Port)) while True: #accept()方法会阻塞程序,程序在这里等待客户端连接,如果有多个连接,accept()按先后顺序每次取一个,并返回这个连接的socket以及客户端IP和port,分别保存到前面的两个变量中 sock,addr=so.accept() #创建一个新的进程来处理传入的连接,将连接对应的socket和IP:port传递给这个进程处理程序 p1=Process(target=Hello_Socket,args=(sock,addr)) p1.start() #启动进程

客户端:

#!/usr/bin/env python3 #coding=utf-8 import socket #新建一个socket,连接到服务端IP的对应端口 so=socket.socket(socket.AF_INET, socket.SOCK_STREAM) so.connect(("127.0.0.1",8899)) #接收服务端的欢迎信息 print(so.recv(1024).decode("utf-8")) #将列表中的四个元素发送给服务端 for d in ["A","B","C","D"]: so.send(d.encode("utf-8")) print(so.recv(1024).decode("utf-8")) #发送exit,断开连接 so.send("exit".encode("utf-8")) so.close()

首先启动服务端程序,然后再打开客户端程序,输出如下:

#服务端 在本地接口127.0.0.1:8899等待客户端连接.... 创建新的客户端连接成功,127.0.0.1:45552 127.0.0.1:45552连接断开... #客户端 欢迎! Hello A Hello B Hello C Hello D 0×3.使用UDP传输数据

UDP传输数据时,双方不需要建立连接,在客户端只需要知道服务端的IP和UDP端口,在服务端只需要监听一个UDP端口来接收数据即可,UDP本身并不保证数据的可靠到达,所以数据的传输速度会比TCP快很多,下面是一个服务端和客户端使用UDP通信的实例:

服务端:

#!/usr/bin/env python3 #coding=utf-8 import socket if __name__=="__main__": #SOCK_DGRAM表示这是一个UDP socket s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) Local_IP_Address="127.0.0.1" Local_Port=20086 #UDP的socket只需要在对应网卡接口绑定本地监听端口即可接收客户端的信息 s.bind((Local_IP_Address,Local_Port)) print("在本地接口%s:%s接收客户端消息..."%(Local_IP_Address,Local_Port)) while True: #recvfrom()方法返回两个值,第一个为从socket接收到的信息,第二个为客户端的(IP,port)元组 data,addr=s.recvfrom(1024) print("从%s:%s接收到消息:%s"%(addr[0],addr[1],data.decode("utf-8"))) #sendto函数接受两个必选参数,语法如下 #sendto(发送的数据,(接收端ip,port)) s.sendto("Hello %s".encode("utf-8")%(data),addr)

客户端:

#!/usr/bin/env python3 #coding=utf-8 import socket #客户端只需要初始化一个UDP socket,然后使用sendto方法将数据发送给服务端即可,接收服务端的返回数据时使用recv方法(因为已经直到服务器的ip和固定端口,所以不需要使用recvfrom方法来获取服务端的IP和端口号了),并将接收的数据解码后打印出来 so=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for d in ["A","B","C","D"]: so.sendto(d.encode("utf-8"),("127.0.0.1",20086)) print(so.recv(1024).decode("utf-8")) so.close()

首先启动服务端,然后启动客户端,输出如下:

#服务端 在本地接口127.0.0.1:20086接收客户端消息... 从127.0.0.1:58172接收到消息:A 从127.0.0.1:58172接收到消息:B 从127.0.0.1:58172接收到消息:C 从127.0.0.1:58172接收到消息:D #客户端 Hello A Hello B Hello C Hello D


Viewing all articles
Browse latest Browse all 9596

Trending Articles