刚看了反应堆模式的原理,特意复习了socket编程,本文主要介绍python的基本socket使用和select使用,主要用于了解socket通信过程

一、socket模块

socket — Low-level networking interface

This module provides access to the BSD socket interface. It is available on all modern Unix systems, Windows, MacOS, and probably additional platforms.

更多详细信息请看官方文档 https://docs.python.org/3/library/socket.html

1、Socket类型

socket 常量  

描述

socket.AF_UNIX

只能够用于单一的Unix系统进程间通信

socket.AF_INET

服务器之间网络通信

socket.AF_INET6

IPv6

socket.SOCK_STREAM

流式socket , for TCP

socket.SOCK_DGRAM

数据报式socket , for UDP

socket.SOCK_RAW

原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。

socket.SOCK_SEQPACKET

可靠的连续数据包服务

创建TCP Socket:

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

创建UDP Socket:

s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

2、socket函数

服务器端 Socket 函数

Socket 函数 描述
s.bind(address) 将套接字绑定到地址,在AF_INET下,以tuple(host, port)的方式传入,如s.bind((host, port))
s.listen(backlog) 开始监听TCP传入连接,backlog指定在拒绝链接前,操作系统可以挂起的最大连接数,该值最少为1,大部分应用程序设为5就够用了
s.accpet() 接受TCP链接并返回(conn, address),其中conn是新的套接字对象,可以用来接收和发送数据,address是链接客户端的地址。

客户端 Socket 函数

Socket 函数 描述
s.connect(address) 链接到address处的套接字,一般address的格式为tuple(host, port),如果链接出错,则返回socket.error错误
s.connect_ex(address) 功能与s.connect(address)相同,但成功返回0,失败返回errno的值

公共 Socket 函数

Socket 函数 描述
s.recv(bufsize[, flag]) 接受TCP套接字的数据,数据以字符串形式返回,buffsize指定要接受的最大数据量,flag提供有关消息的其他信息,通常可以忽略
s.send(string[, flag]) 发送TCP数据,将字符串中的数据发送到链接的套接字,返回值是要发送的字节数量,该数量可能小于string的字节大小
s.sendall(string[, flag]) 完整发送TCP数据,将字符串中的数据发送到链接的套接字,但在返回之前尝试发送所有数据。成功返回None,失败则抛出异常
s.recvfrom(bufsize[, flag]) 接受UDP套接字的数据u,与recv()类似,但返回值是tuple(data, address)。其中data是包含接受数据的字符串,address是发送数据的套接字地址
s.sendto(string[, flag], address) 发送UDP数据,将数据发送到套接字,address形式为tuple(ipaddr, port),指定远程地址发送,返回值是发送的字节数
s.close() 关闭套接字
s.getpeername() 返回套接字的远程地址,返回值通常是一个tuple(ipaddr, port)
s.getsockname() 返回套接字自己的地址,返回值通常是一个tuple(ipaddr, port)
s.setsockopt(level, optname, value) 设置给定套接字选项的值
s.getsockopt(level, optname[, buflen]) 返回套接字选项的值
s.settimeout(timeout) 设置套接字操作的超时时间,timeout是一个浮点数,单位是秒,值为None则表示永远不会超时。一般超时期应在刚创建套接字时设置,因为他们可能用于连接的操作,如s.connect()
s.gettimeout() 返回当前超时值,单位是秒,如果没有设置超时则返回None
s.fileno() 返回套接字的文件描述
s.setblocking(flag) 如果flag为0,则将套接字设置为非阻塞模式,否则将套接字设置为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
s.makefile() 创建一个与该套接字相关的文件

3、socket异常

Exception   解释
socket.error

由Socket相关错误引发

 socket.herror  由地址相关错误引发
 socket.gaierror  由地址相关错误,如getaddrinfo()或getnameinfo()引发
 socket.timeout  当socket出现超时时引发。超时时间由settimeout()提前设定

二、socket编程

1、基于TCP(面向连接)的Socket编程(C++)

服务器端顺序:
(1. 加载套接字库
(2. 创建套接字(serversocket)
(3. 将套接字绑定到一个本地地址和端口上(bind)
(4. 将套接字设为监听模式,准备接收客户请求(listen)
(5. 等待客户请求的到来;当请求带来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)
(6. 用返回的套接字和客户端进行通信(send/recv)调用socket类的getOutputStream()和getInputStream()获取输出流和输入流
(7. 返回,等待另一个客户请求
(8. 关闭套接字(closesocket)
客户端程序:
(1. 加载套接字库
(2. 创建套接字(socket)
(3. 向服务器发送连接请求(connect)
(4. 和服务器端进行通信(send/receive) 调用socket类的getOutputStream()和getInputStream()获取输出流和输入流
(5. 关闭套接字(closesocket)
2、基于UDP(面向无连接)的socket编程(C++)
服务器端(接收端)程序:
(1. 加载套接字库
(2. 创建套接字(socket)
(3. 将套接字绑定到一个本地地址和端口上(bind)
(4. 等待接收数据(recvfrom)
(5. 关闭套接字(closesocket)
客户端(发送端)程序
(1. 加载套接字库
(2. 创建套接字(socket)
(3. 向服务器发送数据(sendto)
(4. 关闭套接字(closesocket)

3、socket tcp 编程实例,c/s程序

#!/bin/env python
# -*- coding:utf8 -*-
"""
server.py
"""                                                   import socket                                                                              host = ('10.1.32.80', 33333)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # 网络通信, TCP流
s.bind(host)
s.listen(5)  # listen 5 client
print "i'm waiting for connection..."                                     while True:                                                               conn, addr = s.accept()   # connection  and ip address                print 'connected by', addr                                            while True:                                                           data = conn.recv(1024)                                            print "receive from %s:%s" % (addr, data)                         conn.sendall("server receive your messages, good bye.")           conn.close()                                                      break
# s.close()

#!/bin/env python
# -*- coding:utf8 -*-                                                     import socket
"""
client.py
"""                                                                       host = ('10.1.32.80', 33333)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # 网络通信, TCP流
s.connect(host)                                                           while True:                                                               msg = raw_input("Please input message: ")                             try:                                                                  s.sendall(msg)                                                    except socket.error:                                                  print "i'm die, bye bye~"                                         break                                                             data = s.recv(1024)                                                   print data                                                            if "good bye" in data:                                                break
s.close()                                                                 

三、基于select的网络编程

1、select介绍

在python中,select函数是一个对底层操作系统的直接访问的接口。它用来监控sockets、files和pipes,等待IO完成(Waiting for I/O completion)。当有可读、可写或是异常事件产生时,select可以很容易的监控到。
select.select(rlist, wlist, xlist[, timeout]) 传递三个参数,一个为输入而观察的文件对象列表,一个为输出而观察的文件对象列表和一个观察错误异常的文件列表。第四个是一个可选参数,表示超时秒数。其返回3个tuple,每个tuple都是一个准备好的对象列表,它和前边的参数是一样的顺序。

2、使用select编程,聊天室程序如下。运行多个client,则可互相聊天,输入"exit"即可退出

服务器代码

#!/bin/env python
#-*- coding:utf8 -*-"""
server select
"""import sys
import time
import socket
import select
import logging
import Queueg_select_timeout = 10class Server(object):def __init__(self, host='10.1.32.80', port=33333, timeout=2, client_nums=10):self.__host = hostself.__port = portself.__timeout = timeoutself.__client_nums = client_numsself.__buffer_size = 1024self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.server.setblocking(False)self.server.settimeout(self.__timeout)self.server.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) #keepaliveself.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #端口复用server_host = (self.__host, self.__port)try:self.server.bind(server_host)self.server.listen(self.__client_nums)except:raiseself.inputs = [self.server] #select 接收文件描述符列表  self.outputs = [] #输出文件描述符列表self.message_queues = {}#消息队列self.client_info = {}def run(self):while True:readable , writable , exceptional = select.select(self.inputs, self.outputs, self.inputs, g_select_timeout)if not (readable or writable or exceptional) :continuefor s in readable :if s is self.server:#是客户端连接connection, client_address = s.accept()#print "connection", connectionprint "%s connect." % str(client_address)connection.setblocking(0) #非阻塞self.inputs.append(connection) #客户端添加到inputsself.client_info[connection] = str(client_address)self.message_queues[connection] = Queue.Queue()  #每个客户端一个消息队列else:#是client, 数据发送过来try:data = s.recv(self.__buffer_size)except:err_msg = "Client Error!"logging.error(err_msg)if data :#print datadata = "%s %s say: %s" % (time.strftime("%Y-%m-%d %H:%M:%S"), self.client_info[s], data)self.message_queues[s].put(data) #队列添加消息 if s not in self.outputs: #要回复消息
                            self.outputs.append(s)else: #客户端断开#Interpret empty result as closed connectionprint "Client:%s Close." % str(self.client_info[s])if s in self.outputs :self.outputs.remove(s)self.inputs.remove(s)s.close()del self.message_queues[s]del self.client_info[s]for s in writable: #outputs 有消息就要发出去了try:next_msg = self.message_queues[s].get_nowait()  #非阻塞获取except Queue.Empty:err_msg = "Output Queue is Empty!"#g_logFd.writeFormatMsg(g_logFd.LEVEL_INFO, err_msg)
                    self.outputs.remove(s)except Exception, e:  #发送的时候客户端关闭了则会出现writable和readable同时有数据,会出现message_queues的keyerrorerr_msg = "Send Data Error! ErrMsg:%s" % str(e)logging.error(err_msg)if s in self.outputs:self.outputs.remove(s)else:for cli in self.client_info: #发送给其他客户端if cli is not s:try:cli.sendall(next_msg)except Exception, e: #发送失败就关掉err_msg = "Send Data to %s  Error! ErrMsg:%s" % (str(self.client_info[cli]), str(e))logging.error(err_msg)print "Client: %s Close Error." % str(self.client_info[cli])if cli in self.inputs:self.inputs.remove(cli)cli.close()if cli in self.outputs:self.outputs.remove(s)if cli in self.message_queues:del self.message_queues[s]del self.client_info[cli]for s in exceptional:logging.error("Client:%s Close Error." % str(self.client_info[cli]))if s in self.inputs:self.inputs.remove(s)s.close()if s in self.outputs:self.outputs.remove(s)if s in self.message_queues:del self.message_queues[s]del self.client_info[s]if "__main__" == __name__:Server().run()

客户端代码

#!/usr/local/bin/python
# *-* coding:utf-8 -*-"""
client.py
"""import sys
import time
import socket
import threadingclass Client(object):def __init__(self, host, port=33333, timeout=1, reconnect=2):self.__host = hostself.__port = portself.__timeout = timeoutself.__buffer_size = 1024self.__flag = 1self.client = Noneself.__lock = threading.Lock() @propertydef flag(self):return self.__flag@flag.setterdef flag(self, new_num):self.__flag = new_numdef __connect(self):client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#client.bind(('0.0.0.0', 12345,))
        client.setblocking(True)client.settimeout(self.__timeout)client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #端口复用server_host = (self.__host, self.__port)try:client.connect(server_host)except:raisereturn clientdef send_msg(self):if not self.client:returnwhile True:time.sleep(0.1)#data = raw_input()data = sys.stdin.readline().strip()if "exit" == data.lower():with self.__lock:self.flag = 0breakself.client.sendall(data)returndef recv_msg(self):if not self.client:returnwhile True:data = Nonewith self.__lock:if not self.flag:print 'ByeBye~~'breaktry:data = self.client.recv(self.__buffer_size)except socket.timeout:continueexcept:raiseif data:print "%s\n" % datatime.sleep(0.1)returndef run(self):self.client = self.__connect()send_proc = threading.Thread(target=self.send_msg)recv_proc = threading.Thread(target=self.recv_msg)recv_proc.start()send_proc.start()recv_proc.join()send_proc.join()self.client.close()if "__main__" == __name__:Client('10.1.32.80').run()

四、多路IO复用介绍和区别

sellect、poll、epoll三者的区别(多路IO复用都是同步的)
select
select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。
另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。
poll
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。
poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。
epoll
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。
epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
 
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符当进程调用epoll_wait()时便得到通知。

转载于:https://www.cnblogs.com/lxmhhy/p/6091730.html

python select网络编程详细介绍相关推荐

  1. java socket 高级编程_Java高级编程-网络编程详细介绍 (一)

    java.net 包中的类和接口提供了可用于低层和高层网络编程的 API.低层 API 可以让你直接访问网络协议,但是为此你不得不使用低层的 TCP 套接字和 UDP 数据包.高层的 API (如 U ...

  2. Python之网络编程(TCP套接字与UDP套接字)

    文章目录 基于tcp的套接字 实现目标 tcp服务端源码 tcp客户端源码 tcp效果实现 基于udp的套接字 udp作用介绍 udp服务端源码 udp客户端源码 udp效果实现 用udp实现一个时间 ...

  3. Python 异步网络编程实战

    Python 异步网络编程实战 - songcser - 掘金小册 小册介绍 第一部分是对 Python 协程的讲解,从字节码开始简单讲解了 Python 虚拟机的执行过程,可以大体了解到 Pytho ...

  4. Python高级网络编程系列之第一篇

    在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...

  5. Python Socket网络编程(二)局域网内和局域网与广域网的持续通信

    目录 前言 IP地址 简介 公有IP 私有IP 局域网之间网络通信 前提 功能描述 源码 运行结果 局域网与广域网网络通信 前提 源码 结语 前言 本系列博客是笔者学习Python Socket的过程 ...

  6. Python Socket网络编程(一)初识Socket和Socket初步使用

    目录 前言 网络编程 实质 IP地址和端口 数据传输协议 协议 Socket 概念 套接字 socket对象方法 初步使用 功能 源码 运行结果 结语 前言 本系列博客是笔者学习Python Sock ...

  7. python recv_python网络编程调用recv函数完整接收数据的三种方法

    最近在使用python进行网络编程开发一个通用的tcpclient测试小工具.在使用socket进行网络编程中,如何判定对端发送一条报文是否接收完成,是进行socket网络开发必须要考虑的一个问题.这 ...

  8. java 网络编程详细解析

    文章目录 java 网络编程详细解析 网络编程的常识 七层网络模型 相关的协议(笔试题) 协议的概念 TCP协议 UDP协议 IP地址(重点) 查看IP地址的方式: 特殊的地址 端口号(重点) 基于t ...

  9. 用 Python 写网络编程(四)

    本文首发于TesterHome社区,作者是资深游戏测试开发工程师陈子昂.用 Python 写网络编程共四篇,今天分享的是第四篇.原文链接:https://testerhome.com/topics/2 ...

  10. 0x011.Python学习-网络编程、PortScan

    Python3 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...

最新文章

  1. 对我国6G早期研究布局的几点建议
  2. 应该始终以PreparedStatement代替Statement
  3. js断点和调试学习总结3
  4. Windows下的bat文件的@echo off 作用
  5. 推荐一套开源通用后台管理系统(附源码)
  6. wxWidgets:wxLogFormatter类用法
  7. 20个jQuery 图片及多媒体画廊插件
  8. 注塑模介绍、设计中文语音多媒体教学光盘 1CD
  9. 如何一键下载或保存微博里面的短视频?
  10. 《计算机视觉工程师养成计划》·开篇
  11. 深度学习中的batch、epoch、iteration
  12. 做哪些事情可以提升生活品质?四十七个小建议
  13. Ubuntu20.04+Nvidia RTX 3060 显卡驱动安装
  14. NLP算法之一(朴素贝叶斯理论部分)
  15. java代码:双色球号随机生成(极其简洁!)
  16. 阿里云Maven仓库地址及设置
  17. python网络爬虫——pyquery的使用(六)
  18. 环洋市场调研-2021年全球抗衰老肽护肤品行业调研及趋势分析报告
  19. 【APICloud系列|5】一键生成APP所有图标
  20. redhat7.6添加中文语言支持

热门文章

  1. centos 7 下安装haproxy
  2. java设计模式-可复用面向对象软件的基础(一)
  3. android安卓 通知notification
  4. likely,unlikely宏与GCC内建函数__builtin_expect()
  5. 动软.Net代码自动生成器下载
  6. test.php变成夏总,test.php
  7. go 中 = 与:= 区别
  8. centos 安装指定版本的node
  9. java中卫语句详解
  10. 网站安全之密码明文传输漏洞