socket—–套接字基础
创建socket
import socket
tcpsock = socket.socket(socket.AF_INET,socket.SOCK_STREM)#创建Tcp/Ip套接字
udpsock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)#创建UDp/Ip套接字
内置方法
名称 | 描述 |
---|---|
服务器套接字方法 | |
s.bind() | |
s.listen() | |
s.accept() | |
客户端套接字方法 | |
s.connect() | |
s.connect_ex() | |
普通的套接字方法 | |
s.recv() | |
s.recv_into() | |
s.send() | |
s.sendall() | |
s.recvfrom() | |
s.recvfrom_into() | |
s.sendto() | |
s.getpeername() | |
s.getsockname() | |
s.getsockopt() | |
s.setsockopt() |
s.shutdown() | |
s.close() | |
s.detach() | |
s.ioctl() | |
面向阻塞的套接字方法 | |
s.setblocking() | |
s.settimeout() | |
s.gettimeout() | |
面向文件的套接字方法 | |
s.fileno() | |
s.makefile() | |
数据属性 | |
s.family | |
s.type | |
s.proto |
创建一个TCP服务器(伪代码)
import socket
ss = socket.socket() # 创建服务器套接字
ss.bind() #套接字与地址绑定
ss.listen() #监听连接 可传入最大连接数
inf_loop: #服务器无限循环
cs = ss.accept() #接收客户端连接 简单的(单线程)
comm_loop: #通信循环
cs.revc()/cs.send() #对话(接收/发送)
cs.close() #关闭客户端套接字
ss.close() #关闭服务器套接字 (可选)
TCp时间戳服务器(tsTserv.py)
这个脚本创建一个Tcp服务器,他接收来自客户端的消息,然后将消息加上时间戳前缀并发送回客户端
from socket import *
from time import ctime
HOST = 'localhost'
PORT = 9999
BUFSIZ = 1024
ADDR = (HOST, PORT)
tcpSerSock = socket(AF_INET, SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)
while True:
print('waiting for connection..')
tcpCliSock ,addr = tcpSerSock.accept()
print('...connected from', addr)
while True:
data = tcpCliSock.recv(BUFSIZ)
if not data:
break
tcpCliSock.send('[%s] %s' %(bytes(ctime(), 'utf-8'), data))
tcpCliSock.close()
tcpSerSock.close()
创建一个TCP客户端(伪代码)
import socket
cs = socket.socket() #创建客户端套接字
cs.connect() #尝试连接服务器
comm_loop: #通信循环
cs.send()/cs.recv() #对话(接收/发送)
cs.close() #关闭客户端套接字
TCp时间戳客户端(tsTcln.py)
from socket import *
HOST = 'localhost'
PORT = 9999
BUFSIZ = 1024
ADDR = (HOST, PORT)
tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
data = input('<<:')
if not data:
print('输入为空!')
continue
tcpCliSock.send(data.encode('utf-8'))
data = tcpCliSock.recv(BUFSIZ)
if not data:
print('服务器端异常')
break
print(data.decode())
tcpCliSock.close()
socketserver模块
socketserver是标准库的一个高级模块,它的目标是简化很多样板代码,隐藏的实现细节之外,另一个不同之处就是,我们现在使用累来编写应用程序。因为以面向对象的方式处理事务有助于组织数据,以及逻辑性的将功能放在正确的地方,你还会注意到,应用程序现在是事件驱动的,这以为着只有在系统中的事件发生时,它们才会工作。
socketserver模块类
类 | 描述 |
---|---|
BaseServer | 包含核心服务器功能和 mix-in类的钩子;仅用于推导,这样不会创建这个类的实例;可以用TCPServer 或 UDPServer 创建类的实例 |
TCPServer/UDPServer | 基础的网络同步TCP/UDP服务器 |
UnixStreamServer/UnixDatagramServer | 基于文件的基础同步TCp/UDP服务器 |
ForkingMixIn/ThreadingMixIn | 核心派出或线程功能;只用做mix-in类与一个服务器类配合实现一些异步性;不能直接实例化这个类 |
ThreadingTCPServer/ThreadingUDPServer | ThreadingMixIn 和 TCPServer/UDPServer的组合 |
BaseRequestHandler | 包含处理服务器请求的核心功能;仅仅用于推导,这样无法创建这个类的实例;可以用StreamRequestHandler 或 DatagramRequestHandler 创建类的实例 |
StreamRequestHandler/DatagramRequestHandler | 实现Tcp/UDP服务器的服务处理器 |
用socketserver实现tsTserv.py
from socketserver import (TCPServer as TCP,
StreamRequestHandler as SRH)
from time import ctime
HOST = ''
PORT = 9999
ADDR = (HOST, PORT)
BUFSIZ = 1024
class MyRequestHandler(SRH):
def handle(self):
print('..connected from:', self.client_address)
self.request.send(('[%s] %s' %(ctime(), self.request.recv(BUFSIZ).decode())).encode('utf-8'))
if __name__ == '__main__':
tcpServ = TCP(ADDR, MyRequestHandler)
print('waiting for connection..')
tcpServ.serve_forever()
我们得到了请求处理程序MyRequestHandler,作为socketserver中StreamRequestHandler的一个子类,重写了handle()方法,该方法在基类Request中默认情况下没有任何行为
def handle(self):
pass
当接收到一个来自客户端的消息时,他就会调用handle()方法。
在socket中用recv()和send()来表示接收/发送 而在socketserver中self.request.recv()和self.request.send()来表示接收/发送
最后利用给定的主机欣喜和请求处理创建了TCP服务器。然后,无限循环的等待并服务于客户端请求
用socketserver实现tsTcln.py
from socket import *
HOST ='localhost'
PORT = 9999
BUFSIZ = 1024
ADDR = (HOST, PORT)
tcp = socket(AF_INET, SOCK_STREAM)
tcp.connect(ADDR)
while True:
data = input('>>:')
if not data:
continue
tcp.send(bytes(data, 'utf-8'))
data = tcp.recv(BUFSIZ)
if not data:
break
print(data.decode().strip())
tcp.close()
socketserver请求处理程序的默认行为是接收连接、获取请求,然后关闭连接。由于这个原因,我们不能在应用程序整个执行过程中都保持连接,因此每次向服务器发送信息时,都需要创建一个新的套接字
运行时可以发现,我们还是只能连接一个客户端,当第一个连接没有关闭时,第二个连接一直在等待,如何实现同时连接呢?
很简单,我们将服务端稍作修改;
# from socketserver import (TCPServer as TCT,
# StreamRequestHandler as SRH)
import socketserver #这里只导入socketser并不进行导入具体方法
from time import ctime
HOST = ''
PORT = 9999
ADDR = (HOST, PORT)
BUFSIZ = 1024
class MyRequestHandler(socketserver.StreamRequestHandler):#这里和以前一样 只是换了一个写法
def handle(self):
print('..connected from:', self.client_address)
self.request.send(('[%s] %s' %(ctime(), self.request.recv(BUFSIZ).decode())).encode('utf-8'))
if __name__ == '__main__':
tcpServ = socketserver.ThreadingTCPServer(ADDR, MyRequestHandler)# tcpServ 并不是socketserver.TCPServer 而是socketserver.ThreadingTCPServer 就是字面意思 ThreadingTCPServer 使用的多线程
print('waiting for connection..')
tcpServ.serve_forever()
练习:(以下答案个人理解不保证正确)
1.套接字。面向连接的套接字和无连接套接字之间的区别是什么?
面向连接
是指进行通讯之前必须建立一个连接,又称为虚拟电路或流套接字。
通讯提供序列化的、可靠的和不重复的数据交付。二没有记录边界
主要协议是传输控制协议(TCP)
无连接
是指通讯之前不需要建立连接。数据报类型套接字
传输过程中无法保证他的顺序性、可靠性或重复性。然而数据报确实保存了记录边界
主要协议是用户数据报协议(UDP)
2.客户端/服务器架构.用自己的话描述这个术语的意思,并给出几个例子
人们通过客户端从服务器端获取想要的内容
消费者和24小时商店的关系,消费者可以随时去进行消费,商店就相当于服务器,一直在等待着人们去消费。
3.套接字。TCP和UDP之中,哪种类型的服务器接受连接,并将他们转换到独立的套接字进行客户端通信?
UDP
4.客户端。更新TCP(tsTclnt.py)以使得服务器名称无需硬编码到应用程序中。此外,应该允许用户指定主机名和端口号,且如果二者中任何一个或者全部参数丢失,那么应该使用默认值
from socket import *
def sock(HOST, PORT):
BUFSIZ = 1024
ADDR = (HOST, PORT)
print(ADDR)
tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
data = input('<<:')
if not data:
print('输入为空!')
continue
tcpCliSock.send(data.encode('utf-8'))
data = tcpCliSock.recv(BUFSIZ)
if not data:
print('服务器端异常')
break
print(data.decode())
tcpCliSock.close()
if __name__ == '__main__':
HOST = input('主机名<<:')
PORT = input('端口号<<:')
if not HOST :
HOST = 'localhost'
if not PORT:
PORT = 9999
sock(HOST,PORT)
5.半双工聊天。创建一个简单的半双工聊天程序。指定半双工,我们呢的意思就是,当建立一个连接且服务开始后,只有一个人能打字,而另一个参与者在得到输入消息提示之前必须等待消息。并且,一旦发送者发送了一条消息,在他能够再次发送消息之前,必须等待对方回复,其中,一个参与者将在服务器一侧,而另一位在客户端一侧。
'''服务器端'''
import socket
HOST = ''
PORT = 9999
BUFSIZ = 1024
ADDR = (HOST, PORT)
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(ADDR)
server.listen()
while True:
tcpClientSock, addr = server.accept()
print('...connected from:', addr)
while True:
data = tcpClientSock.recv(BUFSIZ)
if not data:
print('not data')
break
print('recv:', data.decode())
sends = input('<<:')
if not sends:
continue
tcpClientSock.send(sends.encode('utf-8'))
tcpClientSock.close()
server.close()
'''客户端'''
import socket
HOST = 'localhost'
PORT = 9999
BUFSIZ = 1024
ADDR = (HOST, PORT)
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(ADDR)
while True:
data = input('<<:')
if not data:
continue
client.send(data.encode('utf-8'))
serverdata = client.recv(BUFSIZ)
if not serverdata:
print('服务器端无返回')
break
print(serverdata.decode())
client.close()