python的学习之路day7-socket网络编程

python基础部分学习完了,时间也已通过了两个月左右,感受没学到什么,多是我学习以后忘记的太多了。python

因为没钱买书,要是去培训就更没钱了,因此在网上找了一本书,感受还不错,讲的比较好,比较详细。linux

Python核心编程(第3版)PDF高清晰完整中文版 ,须要的小伙伴能够去下载,固然若是你们不像我这么穷逼,也能够去网上购买。数据库

 

入门半小时,精通一 杯子” !!!编程

接下来继续更新Python缓存

 

大纲

一、一些原理及解释服务器

客户端/服务器的架构原理网络

客户端/服务器的网络编程数据结构

套接字:通讯端点架构

二、Python中的网络编程并发

 socket()模块函数

套接字对象(内置)方法

建立一个简单的TCP服务器以及客户端

建立一个简单的UDP服务器以及客户端

三、socket()模块属性

四、SocketServer模块

SocketServer模块类

SocketServerTCP服务器和客户端(这个地方的代码有问题,若是有大神路过,但愿能帮我看看,谢谢了)

 

一些原理及解释:

客户端/服务器的架构原理

什么是客户端/服务器架构?对于不一样的人来讲,它意味着不一样的东西,这取决于你问谁以及描述的是软件仍是硬件系统。在这两种状况中的任何一种下,前提都很简单:服务器就是一系列硬件或软件,为一个或多个客户端(服务的用户)提供所需的“服务”。它存在惟一目的就是等待客户端的请求,并响应它们(提供服务),而后等待更多请求。另外一方面,客户端因特定的请求而联系服务器,并发送必要的数据,而后等待服务器的回应,最后完成请求或给出故障的缘由。服务器无限地运行下去,并不断地处理请求;而客户端会对服务进行一次性请求,而后接收该服务,最后结束它们之间的事务。客户端在一段时间后可能会再次发出其余请求,但这些都被看成不一样的事务。

客户端/服务器的网络编程

在服务器响应客户端请求以前,必须进行一些初步的设置流程来为以后的工做作准备。首先会建立一个通讯端点,它可以使服务器监听请求。能够把服务器比做公司前台,或者应答公司主线呼叫的总机接线员。一旦电话号码和设备安装成功且接线员到达时,服务就能够开始了。

这个过程与网络世界同样,一旦一个通讯端点已经创建,监听服务器就能够进入无限循环中,等待客户端的链接并响应它们的请求。固然,为了使公司电话接待员一直处于忙碌状态,咱们毫不能忘记将电话号码放在公司信笺、广告或一些新闻稿上;不然,将没有人会打电话过来!
类似地,必须让潜在的客户知道存在这样的服务器来处理他们的需求;不然,服务器将永远不会获得任何请求。想象着建立一个全新的网站,这多是最了不得的、劲爆的、使人惊异的、有用的而且最酷的网站,但若是该网站的 Web 地址或 URL 历来没有以任何方式广播或进行广告宣传,那么永远也不会有人知道它,而且也将永远不会看到任何访问者。如今你已经很是了解了服务器是如何工做的,这就已经解决了较困难的部分。客户端比服务器端更简单,客户端所须要作的只是建立它的单一通讯端点,而后创建一个到服务器的链接。而后,客户端就能够发出请求,该请求包括任何须要的数据交换。一旦请求被服务器处理,且客户端收到结果或某种确认信息,这次通讯就会被终止。

套接字:通讯端点

套接字是计算机网络数据结构,它体现了上节中所描述的“通讯端点”的概念。在任何类型的通讯开始以前,网络应用程序必须建立套接字。能够将它们比做电话插孔,没有它将没法进行通讯。
套接字的起源能够追溯到 20 世纪 70 年代,它是加利福尼亚大学的伯克利版本 UNIX(称为 BSD UNIX)的一部分。所以,有时你可能会听过将套接字称为伯克利套接字或 BSD 套接字。套接字最初是为同一主机上的应用程序所建立,使得主机上运行的一个程序(又名一个进程)与另外一个运行的程序进行通讯。这就是所谓的进程间通讯(Inter Process Communication,IPC)。有两种类型的套接字:基于文件的和面向网络的。
UNIX 套接字是咱们所讲的套接字的第一个家族,而且拥有一个“家族名字”AF_UNIX(又名 AF_LOCAL,在 POSIX1.g 标准中指定),它表明地址家族(address family):UNIX。包括 Python 在内的大多数受欢迎的平台都使用术语地址家族及其缩写 AF;其余比较旧的系统可能会将地址家族表示成域(domain)或协议家族(protocol family),并使用其缩写 PF 而非 AF。相似地,AF_LOCAL(在 2000~2001 年标准化)将代替 AF_UNIX。然而,考虑到后向兼容性,不少系统都同时使用两者,只是对同一个常数使用不一样的别名。Python 自己仍然在使用 AF_UNIX。

由于两个进程运行在同一台计算机上,因此这些套接字都是基于文件的,这意味着文件系统支持它们的底层基础结构。这是可以说得通的,由于文件系统是一个运行在同一主机上的多个进程之间的共享常量。第二种类型的套接字是基于网络的,它也有本身的家族名字 AF_INET,或者地址家族:因特网。另外一个地址家族 AF_INET6 用于第 6 版因特网协议(IPv6)寻址。此外,还有其余的地址家族,这些要么是专业的、过期的、不多使用的,要么是仍未实现的。在全部的地址家族之中,目前 AF_INET 是使用得最普遍的。
Python 2.5 中引入了对特殊类型的 Linux 套接字的支持。套接字的 AF_NETLINK 家族(无链接[见2.3.3节])容许使用标准的BSD套接字接口进行用户级别和内核级别代码之间的IPC。
以前那种解决方案比较麻烦,而这个解决方案能够看做一种比前一种更加优雅且风险更低的解决方案,例如,添加新系统调用、/proc 支持,或者对一个操做系统的“IOCTL”。针对 Linux 的另外一种特性(Python 2.6 中新增)就是支持透明的进程间通讯(TIPC)协议。TIPC 容许计算机集群之中的机器相互通讯,而无须使用基于 IP 的寻址方式。Python 对TIPC 的支持以 AF_TIPC 家族的方式呈现。
总的来讲,Python 只支持 AF_UNIX、AF_NETLINK、AF_TIPC 和 AF_INET 家族。

 

Python中的网络编程

socket()模块函数

要建立套接字,必须使用socket.socket()函数

如何建立socket()函数:
socket(socket_family, socket_type, protocol=0)
socket_family是AF_UNIX或AF_INET(AF_INET6)(ipv4和ipv6)
socket_type是SOCK_STREAM或SOCK_DGRAM(前者表示TCP,后者表示UDP)
protocol一般省略,默认为0

import socket

# 建立TCP/IP套接字:
tcpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 建立UDP/IP套接字:
udpsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

有不少socket模块属性,因此可使用“from socket import *”:

from socket import * tcpsocket = socket(AF_INET, SOCK_STREAM)

 

一旦有了一个套接字对象,那么使用套接字对象的方法将能够进行进一步的交互

 

套接字对象(内置)方法:

名称

描述

服务器套接字方法

s.bind()

将地址(主机名、端口号对)绑定到套接字上

s.listen()

设置并启动TCP监听器

s.accept()

被动接受TCP客户端链接,一直等待直到链接到达(阻塞)

客户端套接字方法

s.connect()

主动发起TCP服务器链接

s.connect_ex()

Connect()的扩展版本,此时会以错误码的形式返回问题,而不是抛出一个异常

普通的套接字方法

s.recv()

接收TCP消息

s.recv_into()

接收TCP消息到指定的缓冲区

s.send()

发送TCP消息

s.sendall()

完整地发送TCP消息

s.recvfrom()

接收UDP消息

s.recvfrom_into()

接收UDP消息到指定的缓冲区

s.sendto()

发送UDP消息

s.getpeername()

链接到套接字(TCP)的远程地址

s.getsockname()

当前套接字的地址

s.getsockopt()

返回给定套接字选项的值

s.setsockopt()

设置给定套接字选项的值

s.shutdown()

关闭链接

s.close()

关闭套接字

s.detach()

在未关闭文件描述符的状况下关闭套接字,返回文件描述符

s.ioctl()

控制套接字的模式(仅支持Windows)

面向阻塞的套接字方法

s.setblocking()

设置套接字的阻塞或非阻塞模式

s.settimeout()

设置阻塞套接字操做的超时时间

s.gettimeout()

获取阻塞套接字操做的超时时间

面向文件的套接字方法

s.fileno()

套接字的文件描述符

s.makefile()

建立与套接字关联的文件对象

数据属性

s.family

套接字家族

s.type

套接字类型

s.proto

套接字协议

 

建立一个简单的TCP服务器以及客户端

服务器端:

# TCP时间戳服务器的伪代码
# import socket
# ss = socket.socket()          #建立服务器套接字
# ss.bind()                     #套接字与地址绑定
# ss.listen()                   #监听链接
# inf_loop:                     #服务器无限循环
#     cs = ss.accept()          #接收客户端链接
#     comm_loop:                #通讯循环
#         cs.recv()/cs.send()   #对话(接收/发送)
#     cs.close()                #关闭客户端套接字
# ss.close()                    #关闭服务器套接字(可选)

import socket
import time  # 分别导入两个模块

HOST = ''  # 这是对bind()方法的标识,表示可使用任何可用的地址
PORT = 1504  # 端口
BUFSIZ = 1024  # 设置缓存区大小
ADDR = (HOST, PORT)
tcp_ser_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_ser_sock.bind(ADDR)  # 将(地址、端口号)绑定到套接字在上
tcp_ser_sock.listen(5)
while True:  # 进入一个无限的循环
    print("waiting for connection...")  # 提示信息
    tcp_cli_sock, addr = tcp_ser_sock.accept()  # 被动的等待客户端链接
    print("...connected from: ", addr)  # 某个客户端已链接
    while True:  # 进入一个对话循环
        data = tcp_cli_sock.recv(BUFSIZ).decode()  # 接收TCP消息
        if not data:
            break  # 若是接收的消息为空,跳出当前循环,继续等待
        tcp_cli_sock.send(('[%s] %s' % (time.ctime(), data)).encode())  # 发送接收的消息,而且在前面加上当前时间
    tcp_cli_sock.close()  # 关闭客户端

客户端:

# 伪代码
# import socket
# cs = socket.socket()    #建立客户端套接字
# cs.connect()            #尝试链接服务器
# comm_loop:              #通讯循环
#     cs.send()/cs.recv() #对话(发送/接收)
# cs.close()              #关闭客户端套接字

import socket

HOST = 'localhost'
PORT = 1504
BUFSIZ = 1024
ADDR = (HOST, PORT)
tcp_cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_cli_sock.connect(ADDR)  # 主动发起TCP服务器链接

while True:
    data = input(">>>")
    if not data:  # 判断输入是否为空
        break
    tcp_cli_sock.send(data.encode())  # 发送输入的内容
    data = tcp_cli_sock.recv(BUFSIZ).decode()  # 接收返回的内容
    if not data:  # 判断返回的内容是否为空
        break
    print(data)  # 将返回的内容打印出来
tcp_cli_sock.close()  # 而后关闭链接

 输出:

服务器:
waiting for connection...
...connected from:  ('127.0.0.1', 52064)
waiting for connection...

客户端:
>>>qwe
[Thu Jan 18 20:09:59 2018] qwe
>>>456
[Thu Jan 18 20:10:04 2018] 456
>>>

 

当咱们从服务器退出时,必须跳出它,这就会致使一个异常。为了不这种错误,最好的方式就是建立一种更优雅的退出方式

核心提示:优雅地退出和调用服务器 close()方法
在开发中,建立这种“友好的”退出方式的一种方法就是,将服务器的 while 循环放在一个 try-except 语句中的 except 子句中,并监控 EOFError 或 KeyboardInterrupt 异常,这样你就能够在 except 或 finally 字句中关闭服务器的套接字。在生产环境中,你将想要可以以一种更加自动化的方式启动和关闭服务器。在这些状况下,须要经过使用一个线程或建立一个特殊文件或数据库条目来设置一个标记以关闭服务。

 

建立一个简单的UDP服务器以及客户端

服务器端:

# 伪代码:
# ss = socket.socket()                  #建立服务器套接字
# ss.bind()                             #绑定服务器套接字
# inf_loop:                             #服务器无限循环
#     cs = cs.recvfrom()/ss.sendto()    #对话(接收/发送)
# ss.close()                            #关闭服务器套接字

import socket
import time  # 分别导入两个模块

HOST = ''  # 主机地址
PORT = 1504  # 端口号
BUFSIZ = 1024  # 缓存区大小
ADDR = (HOST, PORT)
udp_ser_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 使用udp协议
udp_ser_sock.bind(ADDR)  # 将地址和端口绑定到套接字上
# 能够看到没有listen,由于udp是无链接的,因此不用调用“监听传入链接”
while True:  # 进入一个无限循环
    print("waiting for massage...")  # 提示输出
    data, addr = udp_ser_sock.recvfrom(BUFSIZ)  # 接收UDP消息
    udp_ser_sock.sendto(("[%s] %s" % (time.ctime(), data.decode("utf-8"))).encode(), addr)  # 将接收到的消息加上时间发送给客户端
    print("...received from and returned to:", addr)  # 打印已发送给谁
udp_ser_sock.close()

 客户端:

# 伪代码
# cs = socket()                     #建立客户端套接字
# comm_loop:                        #通讯循环
#     cs.sendto()/cs.recvfrom()     #对话(发送/接收)
# cs.close()                        #关闭客户端套接字

import socket

HOST = 'localhost'  # 地址
PORT = 1504  # 端口
BUFSIZ = 1024  # 缓存
ADDR = (HOST, PORT)
udp_cli_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 使用udp协议
while True:  # 进入一个无限循环
    data = input(">>>")  # 输入
    if not data:  # 判断输入是否为空
        break
    udp_cli_sock.sendto(data.encode(), ADDR)  # 将输入的内容和本地的地址和端口发送到服务器
    data, ADDR = udp_cli_sock.recvfrom(BUFSIZ)  # 接收服务器传回来的内容
    if not data:
        break
    print(data.decode("utf-8"))  # 打印
udp_cli_sock.close()

 输出:

服务器:
waiting for massage...
...received from and returned to: ('127.0.0.1', 64721)
waiting for massage...
...received from and returned to: ('127.0.0.1', 64722)
waiting for massage...


客户端:
>>>asd
[Fri Jan 19 14:21:47 2018] asd
>>>asd
[Fri Jan 19 14:21:51 2018] qwe

 

socket模块属性

 

SocketServer模块

SocketServer 是标准库中的一个高级模块(Python 3.x 中重命名为 socketserver),它的目标是简化不少样板代码,它们是建立网络客户端和服务器所必需的代码。这个模块中有为你建立的各类各样的类;

SocketServer模块类

 

描述

BaseServer

包含核心服务器功能和 mix-in 类的钩子;仅用于推导,这样不会建立这个类的实例;能够用 TCPServer 或 UDPServer 建立类的实例

TCPServer/UDPServer

基础的网络同步 TCP/UDP 服务器

UnixStreamServer/UnixDatagramServer

基于文件的基础同步 TCP/UDP 服务器

ForkingMixIn/ThreadingMixIn

核心派出或线程功能;只用做 mix-in 类与一个服务器类配合实现一些异步性;不能直接实例化这个类

ForkingTCPServer/ForkingUDPServer

ForkingMixIn 和 TCPServer/UDPServer 的组合

ThreadingTCPServer/ThreadingUDPServer

ThreadingMixIn 和 TCPServer/UDPServer 的组合

BaseRequestHandler

包含处理服务请求的核心功能;仅仅用于推导,这样没法建立这个类的实例;可使用 StreamRequestHandler 或 DatagramRequestHandler 建立类的实例

StreamRequestHandler/DatagramRequestHandler

实现 TCP/UDP 服务器的服务处理器

 

SocketServerTCP服务器和客户端(表示没有成功,书上写的python2,我用的是python3,不知道如何改代码,若是有哪位大神路过看到可否救救我,谢谢了)

服务器端:

import socketserver
import time

HOST = ""
PORT = 1504
ADDR = (HOST, PORT)


class my_request_handler(socketserver.StreamRequestHandler):
    def handle(self):
        print("...connected from:", self.client_address)
        self.wfile.write('[%s] %s' % (time.ctime(), self.rfile.readline()))


tcp_serv = socketserver.TCPServer(ADDR, my_request_handler)
print("waiting for connection...")
tcp_serv.serve_forever()

 客户端:

import socket

HOST = "localhost"
PORT = 1504
BUFSIZ = 1024
ADDR = (HOST, PORT)
while True:
    tcp_cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_cli_sock.connect(ADDR)
    data = input(">>>")
    if not data:
        break
    tcp_cli_sock.send(data.encode())
    data = tcp_cli_sock.recv(BUFSIZ).decode()
    if not data:
        break
    print(data.strip())
    tcp_cli_sock.close()

 输出:

服务器:
waiting for connection...
...connected from: ('127.0.0.1', 59956)

客户端:
>>>sdas
就这样一直卡着若是像书上同样:TypeError: a bytes-like object is required, not 'str'
相关文章
相关标签/搜索