加入收藏 | 设为首页 | 会员中心 | 我要投稿 开发网_新乡站长网 (https://www.0373zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Unix > 正文

对于python socket编程的初探

发布时间:2023-01-29 12:53:22 所属栏目:Unix 来源:
导读:  socket编程步骤

  服务端创建一个socket,绑定地址和端口,然后监听端口上传入的连接,一旦有连接进来,就通过accept函数接收传入的连接。

  客户端也是创建一个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)
 
  以上主要是针对TCP流数据的socket编程。对于UDP协议的数据,处理略有不同。譬如发送接收UDP数据包处理函数为:
 
  socket.sendto(string, flags, address)
 
  socket.recvfrom(bufsize[, flags]) #返回(string, address),string是返回的数据,address是发送方的socket地址
 
  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()
 
  以上是本人对socket相关的理解unix进程通信,有什么不当或错误之处,还请指出。
 

(编辑:开发网_新乡站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章