摘要:对于网络编程来说,免不了要用到模块。表示另一端的地址。以上主要是针对流数据的编程。对于协议的数据,处理略有不同。通过传入对象调用来监听对象的文件描述符,一旦发现对象就绪,就通知应用程序进行相应的读写操作。
对于python网络编程来说,免不了要用到socket模块。下面分享一下个人对python socket的一些理解。
socket编程步骤服务端创建一个socket,绑定地址和端口,然后监听端口上传入的连接,一旦有连接进来,就通过accept函数接收传入的连接。
客户端也是创建一个socket。绑定远程地址和端口,然后建立连接,发送数据。
服务端socket下面通过一段实例代码来详细说明 服务端 socker_server.py
import socket import sys HOST = "127.0.0.1" PORT = 10000 s = None for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): af, socktype, proto, canonname, sa = res try: s = socket.socket(af, socktype, proto) except socket.error as msg: s = None continue try: s.bind(sa) s.listen(5) except socket.error as msg: s.close() s = None continue break if s is None: print "could not open socket" sys.exit(1) conn, addr = s.accept() print "Connected by", addr while 1: data = conn.recv(1024) if not data: break conn.send(data) conn.close()
首先我们通过socket.getaddrinnfo函数将host/port转换成一个包含5元组的序列。这个5元组包含我们创建一个socket连接所需要的所有必要参数。返回的5元组分别是 (family, sockettype, proto, canonname, sockaddr)
family 地址簇,用与socket()函数的第一个参数。主要有以下几个
socket.AF_UNIX 用与单一机器下的进程通信
socket.AF_INET 用与服务器之间相互通信,通常都用这个。
socket.AF_INET6 支持IPv6
sockettype socket类型,用与socket()函数的第二个参数,常用的有
socket.SOCK_STREAM 默认,用于TCP协议
socket.SOCK_DGRAM 用于UDP协议
proto 协议,用于socket()函数的第三个参数。 getaddrinnfo函数会根据地址格式和socket类型,返回合适的协议
canonname 一个规范化的host name。
sockaddr 描述了一个socket address .是一个二元组,主要用于bind()和connect()函数
接下来创建一个socket对象,传入getaddrinnfo函数返回的af,sockettype,proto。
s = socket.socket(af, socktype, proto)
然后绑定我的socket address
s.bind(sa)
开启监听模式
s.listen(5)
listen函数会监听连接到socket上的连接,参数表示在拒绝连接之前系统可以挂起的最大连接队列数量为5。这些连接还没有被accept处理。数量不能无限大,通常指定5。
一旦我们监听到了连接,就会调用accept函数接收连接
conn, addr = s.accept()
accept函数返回一个二元组,conn是一个新的socket对象,用来接收和发送数据。addr表示另一端的socket地址。
接下来我们就可以用conn对象发送和接收数据了
data = conn.recv(1024) # 接收数据, 这里指定一次最多接收的字符数量为1024 conn.send(data) # 发送数据
这里我们接收到一个连接socket就会停止运行,所以如果要循环连接的话,将accept函数放入到一个死循环里。
客户端socket客户端socket编程相对比较简单,通过connect和服务端建立连接之后,就可以相互通信了。socket_client.py如下
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM): af, socktype, proto, canonname, sa = res try: s = socket.socket(af, socktype, proto) except socket.error as msg: s = None continue try: s.connect(sa) except socket.error as msg: s.close() s = None continue break if s is None: print "could not open socket" sys.exit(1) s.sendall("Hello, world") data = s.recv(1024) s.close() print "Received", repr(data)
备注: 对于getaddrinfo函数,可以参考下http://baike.baidu.com/link?u... 这个函数的作用是把协议相关性安全隐藏在这个底层库函数内部。应用程序只要处理由getaddrinfo函数返回的数据即可。
以上主要是针对TCP流数据的socket编程。对于UDP协议的数据,处理略有不同。譬如发送接收UDP数据包处理函数为:
socket.sendto(string, flags, address) socket.recvfrom(bufsize[, flags]) #返回(string, address),string是返回的数据,address是发送方的socket地址
其他详细内容可以参考 http://python.usyiyi.cn/trans...
SocketServer模块python中网络编程除了socket模块还提供了SocketServer模块,这一模块主要是对socket模块进行了封装,将socket的对象的创建,绑定,连接,接收,发送,关闭都封装在里面,大大简化了网络服务的编程。
此模块提供了以下2个主要的网络服务类,用于创建相应的套接字流
TCPServer 创建TCP协议的套接字流
UDPServer 创建UDP协议的套接字流
我们有了套接字流对象,还需要一个请求处理类。SocketServer模块提供了请求处理类有BaseRequestHandler,以及它的派生类StreamRequestHandler和DatagramRequestHandler。所以只要继承这3个类中的一个,然后重写handle函数,此函数将用来处理接收到的请求。下面看一个服务端的代码示例
import SocketServer class MyTCPHandler(SocketServer.StreamRequestHandler): """创建请求处理类,重写handle方法。此外也可以重写setup()和finish()来做一些请求处理前和处理后的一些工作""" def handle(self): # self.request is the TCP socket connected to the client self.data = self.request.recv(1024).strip() print "{} wrote:".format(self.client_address[0]) print self.data # just send back the same data, but upper-cased self.request.sendall(self.data.upper()) if __name__ == "__main__": HOST, PORT = "localhost", 10000 server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler) # Activate the server; this will keep running until you # interrupt the program with Ctrl-C # server.shutdown() server.serve_forever() # 一直循环接收请求 # server.handle_request() # 只处理一次请求就退出
看着是不是代码简单了很多,而且SocketServer模块内部使用了多路复用IO技术,可以实现更好的连接性能。看serve_forever函数的源代码用到了select模块。通过传入socket对象调用select.select()来监听socket对象的文件描述符,一旦发现socket对象就绪,就通知应用程序进行相应的读写操作。源代码如下:
def serve_forever(self, poll_interval=0.5): """Handle one request at a time until shutdown. Polls for shutdown every poll_interval seconds. Ignores self.timeout. If you need to do periodic tasks, do them in another thread. """ self.__is_shut_down.clear() try: while not self.__shutdown_request: # XXX: Consider using another file descriptor or # connecting to the socket to wake this up instead of # polling. Polling reduces our responsiveness to a # shutdown request and wastes cpu at all other times. r, w, e = _eintr_retry(select.select, [self], [], [], poll_interval) if self in r: self._handle_request_noblock() finally: self.__shutdown_request = False self.__is_shut_down.set()
即使使用了select技术,TCPServer,UDPServer处理请求仍然是同步的,意味着一个请求处理完,才能处理下一个请求。但SocketServer模块提供了另外2个类用来支持异步的模式。
ForkingMixIn 利用多进程实现异步
ThreadingMixIn 利用多线程实现异步
看名字就知道使用了mixin模式。而mixin模式可以通过多继承来实现,所以通过对网络服务类进行多继承的方式就可以实现异步模式
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): pass
针对ThreadindMixIn,实现异步的原理也就是在内部对每个请求创建一个线程来处理。看源码
def process_request(self, request, client_address): """Start a new thread to process the request.""" t = threading.Thread(target = self.process_request_thread, args = (request, client_address)) t.daemon = self.daemon_threads t.start()
下面提供一个异步模式的示例
import socket import threading import SocketServer class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler): def handle(self): data = self.request.recv(1024) cur_thread = threading.current_thread() response = "{}: {}".format(cur_thread.name, data) self.request.sendall(response) class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): pass def client(ip, port, message): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip, port)) try: sock.sendall(message) response = sock.recv(1024) print "Received: {}".format(response) finally: sock.close() if __name__ == "__main__": # Port 0 means to select an arbitrary unused port HOST, PORT = "localhost", 0 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler) ip, port = server.server_address # Start a thread with the server -- that thread will then start one # more thread for each request server_thread = threading.Thread(target=server.serve_forever) # Exit the server thread when the main thread terminates server_thread.daemon = True server_thread.start() print "Server loop running in thread:", server_thread.name client(ip, port, "Hello World 1") client(ip, port, "Hello World 2") client(ip, port, "Hello World 3") server.shutdown() server.server_close()
更多对SocketServer模块的了解参考https://docs.python.org/2/lib... 本文所使用的示例就来自官网。毕竟官网的例子实在太好了。
以上是本人对socket相关的理解,有什么不当或错误之处,还请指出。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/44280.html
摘要:从导入相应函数,即传入一个上一步中的类,返回对应的网页内容,具体实现将在后续讲解。后续只要去处理解析请求和响应部分即可,主程序可以不用再修改。下一篇文章编程解析请求头 Flask或者其他框架都是封装的比较完善,我们可以不去关注路由、SESSION等到底是怎么实现的,现在我们使用socket来实现一个带有注册、登录功能的To do网站,这样能对后端框架了解的稍微更深入一点(当然你也可以直...
摘要:请求头的换行使用的是。编写一个类,来解析请求的方法路径和,如下下一篇文章编程响应 前文:【python socket编程】—— 1.初探 在上一篇文章中我们知道,socket.accept()接受的数据是请求头,请求头格式是这样的: POST /login HTTP/1.1 Host: 127.0.0.1:1207 User-Agent: Mozilla/5.0 (X11; Ubunt...
摘要:原文地址的中文名字叫做套接字,这种东西就是对的封装。运行结果如下简单解析一下上述代码来说明一下服务器的流程首先,根据协议族或地址族套接字类型以及具体的的某个协议来创建一个。很容易受到攻击,造成拒绝服务。 [原文地址:https://blog.ti-node.com/blog...] socket的中文名字叫做套接字,这种东西就是对TCP/IP的封装。现实中的网络实际上只有四层而已,从上...
摘要:原文地址要想更好了解编程,有一个不可绕过的环节就是在中,一切皆文件实际上要文件干啥不就是读写么所以,这句话本质就是才是王道用的打开文件关闭文件读读写写,这叫本地文件在编程中,本质就是网络所以,在开始进一步的编程前,我们必须先从概念上认识好 [原文地址:https://blog.ti-node.com/blog...] 要想更好了解socket编程,有一个不可绕过的环节就是IO.在Lin...
阅读 2894·2021-11-24 09:39
阅读 1156·2021-11-02 14:38
阅读 4141·2021-09-10 11:26
阅读 2742·2021-08-25 09:40
阅读 2303·2019-08-30 15:54
阅读 476·2019-08-30 10:56
阅读 2737·2019-08-26 12:14
阅读 3211·2019-08-26 12:13