socket概念
socket层
理解socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议
简单来说
其实站在你的角度上看,socket就是一个模块。我们通过调用模块中已经实现的方法建立两个进程之间的连接和通信。
也有人将socket说成ip+port,因为ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序。
所以我们只要确立了ip和port就能找到一个应用程序,并且使用socket模块来与之通信。
套接字(socket)的发展史
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
tcp协议和udp协议
.TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)
套接字(socket)初使用
基于TCP协议的socket
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
windows如果不通,注意关闭防火墙
server端
import socket
sk = socket.socket() # 实例化一个对象sk
sk.bind(('192.168.11.224', 8989)) # 把地址绑定到套接字
sk.listen() # 监听链接
conn, addr = sk.accept() # 接收客户端链接
print(addr) # 打印出客户端的地址+端口
conn.send('hello'.encode('utf-8')) # 向客户端发送信息
ret = conn.recv(1024) # 接收客户端信息(1024字节)
print(ret.decode('utf-8')) # 打印客户端信息(bytes类型需要decode)
conn.close() # 关闭客户端套接字
sk.close() # 关闭服务器套接字
client端
import socket
sk = socket.socket() # 创建客户端套接字
sk.connect(('192.168.11.224', 8989)) # 连接服务器(ip地址+端口)
ret = sk.recv(1024) # 接收消息
print(ret.decode('utf-8')) # 打印接收的消息
sk.send('SB!'.encode('utf-8')) # 向服务器发送消息(bytes)
sk.close() # 关闭客户端套接字
选运行server端,在运行client端(顺序不能搞反了!)
server端执行结果如下
('192.168.11.224', 58040)
SB!
client端执行结果如下
hello
上面例子,只是互相发送了一次消息,就关闭了,如何互相一直发送消息?
server代码:
import socket
while True:
sk = socket.socket() # 实例化一个对象sk
sk.bind(('192.168.11.224', 8989)) # 把地址绑定到套接字
sk.listen(10) # 监听链接
conn, addr = sk.accept() # 接收客户端连接信息
print(addr) # 打印出客户端的地址
ret = conn.recv(1024) # 接收客户端信息(1024字节)
if ret.decode('utf-8').lower() == 'q': # (一定要是client先退出,读取内容为q,则退出)
conn.close() # 关闭客户端套接字
sk.close() # 关闭服务器套接字
break
print(ret.decode('utf-8')) # 打印客户端发送信息
content = input('input>>>').strip().encode('utf-8') # 输入需要发送的内容
conn.send(content) # 向客户端发送信息
client代码:
import socket
while True:
sk = socket.socket() # 创建客户端套接字
sk.connect(('192.168.11.224', 8989)) # 连接服务器(ip地址+端口)
content = input('input>>>').strip().encode('utf-8') # 输入内容,需要encode为bytes类型
if content.decode('utf-8').lower() == 'q': # 当输入q的时候,退出聊天
sk.send(content) # 关闭前发送退出信息给server端
sk.close() # 关闭客户端套接字
break
sk.send(content) # 向服务器发送消息(bytes)
ret = sk.recv(1024) # 接收消息
print(ret.decode('utf-8')) # 打印接收的消息
运行效果--(先执行server,在执行client)
socket(tcp协议)编程,多人聊天
对于一个tcp连接,客户端和server对话没有结束,那么其它客户端全部都不能与服务器发消息对话
示例
server端代码
import socket
sk = socket.socket() # 实例化一个对象sk
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 重用ip和端口
sk.bind(('127.0.0.1', 9595)) # 把地址,端口绑定到套接字(socket)
sk.listen() # 监听连接
# 网络传输只能是二进制(bytes)类型的数据
while True:
conn, addr = sk.accept() # 接收客户端连接(conn为内容,addr为ip地址和端口)
while True:
ret = conn.recv(1024).decode('utf-8') # 接收内容1024个字节并解码为utf-8
if ret == 'q':
break
print(ret)
inp = input('>>>').strip()
conn.send(inp.encode('utf-8')) # conn.send发送信息(并编码成utf-8)
if inp == 'q':
break
conn.close() # 关闭客户端连接
sk.close() # 关闭socket连接
client端代码
import socket
sk = socket.socket() # 实例化一个对象sk
sk.connect(('127.0.0.1', 9595)) #连接服务器(ip地址+端口)
while True:
inp = input('>>>').strip()
sk.send(inp.encode('utf-8')) # 向服务器发送消息(编程成utf-8)
if inp == 'q':
break
ret = sk.recv(1024).decode('utf-8') # 接收消息(1024字节,解码成utf-8)
if ret == 'q':
break
print(ret)
sk.close() # 关闭socket连接
运行结果
可以很明显的看到,如果第一个连接不退出的话,那么第二个连接就不能与服务器对话
总结:
tcp协议适用于 文件的上传和下载 发送邮件 发送重要文件
每和一个客户端建立连接,都会在自己的操作系统上占用一个资源
同一时间,只能和一个客户端通信