一、socket
1、socket简介
socket(简称 套接字
) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的
2、创建socket
在python中,使用socket模块的函数可以完成:
import socket
"""
函数:socket.socket(AddressFamily, Type)
创建一个socket,该函数有两个参数:
AddressFamily:可以选择AF_INET(用于Internet进程间通信)或者AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
Type:套接字类型,可以是SOCKET_STREAM(流式套接字,主要用于TCP协议)或者SOCK_DGRAM(数据报套接字,主要用于UDP协议)
"""
3、udp发送数据
(1)、ubuntu桌面编写python代码
(2)、使用命令运行代码
(3)、在虚拟机xp系统中查看是否接收到数据
注意:xp中用到的接收软件在本博客资料中
http://download.csdn.net/download/wingzhezhe/10226876
4、练习:发送带有退出功能的可多次发送的udp数据
5、接收udp数据
(1)、ubuntu中使用udp接收数据的代码
import socket
def main():
# 1.创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2.给当前套接字对象绑定一个ip和端口号
# 使用的函数是 :bind((ip, port)) bind的参数是一个元组
# ip :当前本机的ip,一般不用写,用""代替,代表本机ip
# port : 给当前套接字对象指定端口号
local_info = ("", 8888)
udp_socket.bind(local_info)
# 3.接收数据 : 使用函数recvfrom(args) 参数args表示本次接收的最大字节数
"""
接收的数据是一个元组类型的 : (字节类型的接收到的内容, (发送方的ip, 发送方的port))
需要使用 decode(args) 方法将接收的字节类型的数据进行转码
"""
recv_data = udp_socket.recvfrom(1024)
print(type(recv_data))
# 对接收的数据进行处理
recv_content = recv_data[0].decode("gbk") # window中默认编码时gbk,因此转码也要使用gbk
recv_info = recv_data[1]
# 4.打印数据
print("接收到的数据内容是:%s \n 发送方的信息是:%s" % (recv_content, recv_info))
# 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
(2)、运行程序,演示操作
6、使用同一个套接字首发upd数据
(1)、在ubuntu系统中安装网路调试助手
(2)、编写代码
def main():
# 1.创建一个套接字对象
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2.获取对方的ip/port
dest_ip = input("请输入对方的ip:")
dest_port = int(input("请输入对方的port:"))
# 3.从键盘获取数据
content = input("请输入要发送的内容:")
# 4.使用套接字发送数据
udp_socket.sendto(content.encode("utf-8"), (dest_ip, dest_port))
# 接收对方发送的套接字数据
recv = udp_socket.recvfrom(1024)
print(recv)
# 5.关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
(3)、测试运行
7、使用udp实现简易聊天室
import socket
def send_msg(udp_socket):
"""发送数据"""
dest_ip = input("请输入目的地ip:")
dest_port = int(input("请输入目的地port:"))
dest_content = input("请输入要发送的内容:")
# 发送数据
udp_socket.sendto(dest_content.encode("utf-8"), (dest_ip, dest_port))
def recv_msg(udp_socket):
"""接收数据"""
recv_data = udp_socket.recvfrom(1024)
print("端口号和ip为:%s 发送的数据是:%s " % (recv_data[1], recv_data[0].decode("utf-8")))
def main():
# 1.创建套接字对象
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2.给套接字绑定ip和端口号
udp_socket.bind(("",8888))
# 3.使用循环来实现收发功能
while True:
print("---------------XXX聊天室---------------")
opt = input("请输入您要进行的操作:1(发送消息) 2(接收消息) 3(退出系统)")
if opt == "1":
send_msg(udp_socket)
elif opt == "2":
recv_msg(udp_socket)
elif opt == "3":
break
else:
print("输入有误")
# 4.关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
测试运行:
二、tcp
1、tcp协议介绍
TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
TCP通信需要经过创建连接、数据传送、终止连接三个步骤。、
TCP通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据
2、Tcp的特点
(1)、面向连接
通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
双方间的数据传输都可以通过这一个连接进行。
完成数据交换后,双方必须断开此连接,以释放系统资源。
这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。
(2)、可靠传输
1)TCP采用发送应答机制
TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功
2)超时重传
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。
TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
(3)、错误校验
TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
4) 流量控制和阻塞管理
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
TCP与UDP的不同点
面向连接(确认有创建三方交握,连接已创建才作传输。)
有序数据传输
重发丢失的数据包
舍弃重复的数据包
无差错的数据传输
阻塞/流量控制
3、tcp通信模型
4、使用tcp客户端发送数据
import socket
def main():
# 1.创建tcp套接字
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.链接服务器
server_ip = input("请输入要链接的服务器的ip:")
server_port = int(input("请输入服务器的ip:"))
server_info = (server_ip, server_port)
tcp_socket.connect(server_info)
# 3.发送数据
send_data = input("请输入要发送的内容:")
tcp_socket.send(send_data.encode("utf-8"))
# 4.关闭套接字
tcp_socket.close()
if __name__ == "__main__":
main()
5、tcp服务器
(1)、创建服务器流程
socket创建一个套接字
bind绑定ip和port
listen使套接字变为可以被动链接
accept等待客户端的链接
recv/send接收发送数据
(2)、创建tcp服务器demo
def main():
# 1.创建tcp套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.绑定本地信息
tcp_server_socket.bind(("", 8888))
# 3.让默认的套接字由主动变为被动
tcp_server_socket.listen(128)
print("----------程序启动,等待客户端链接------------")
"""
4.等待客户端的链接,返回值是一个元组(client_socket, client_addr)
步骤1创建的套接字对象只负责等待客户端进行链接,如果有链接,服务端就创建一个套接字,用来为客户端服务
client_socket : 服务器创建的套接字对象,用来为客户端服务
client_addr : 存放的是客户端的信息,是一个元组(client_ip, client_port)
"""
client_socket, client_addr = tcp_server_socket.accept()
print("-----------接收到客户端链接-----------")
print(client_addr)
# 5.接收客户端发送过来的数据,返回值就是一个字节数据
recv_data = client_socket.recv(1024)
print(recv_data)
# 6.服务器接收到数据后,给客户端返回数据
client_socket.send("数据已经收到".encode("utf-8"))
# 7.关闭套接字
client_socket.close()
tcp_server_socket.close()
if __name__ == "__main__":
main()
(3)、实现tcp服务端循环为多个客户端服务
(4)、实现tcp服务器循环为多个客户服务,并且多次为一个客户提供服务
6、模拟下载文件
(1)、tcp服务端代码
import socket
def send_file_2_client(client_socket, client_addr):
# 1.接收客户端发过来的信息(要下载的文件名)
file_name = client_socket.recv(1024).decode("utf-8")
print("客户端要下载的文件名为:%s" % file_name)
# 2. 打开文件,读取文件内容
file_content = None
try:
f = open(file_name, "rb")
file_content = f.read()
f.close()
except Exception as e:
print("没有要下载的文件:%s" % file_name)
# 3.发送数据给客户端
if file_content:
client_socket.send(file_content)
def main():
# 1.创建服务端的套接字对象
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.绑定服务端信息
tcp_server_socket.bind(("", 8888))
# 3.让默认的套接字由主动变为被动(listen)
tcp_server_socket.listen(128)
# 让下载程序为多个客户端服务
while True:
# 4.等待客户端的链接
client_socket, client_addr = tcp_server_socket.accept()
# 5.调用方法,读取文件内容,并发送给客户端
send_file_2_client(client_socket, client_addr)
# 关闭套接字
client_socket.close()
# 6.关闭套接字
tcp_server_socket.close()
if __name__ == "__main__":
main() main()
(2)、tcp客户端代码
import socket
def main():
# 1.创建tcp套接字对象
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.获取服务器的ip和port
server_ip = input("请输入服务器的ip:")
server_port = int(input("请输入服务器的port:"))
# 3.链接服务器
tcp_socket.connect((server_ip, server_port))
# 4.获取要下载的文件的名字
download_file_name = input("请输入要下载的文件名:")
# 5.将文件名发送到服务器
tcp_socket.send(download_file_name.encode("utf-8"))
# 6.接收从服务器中发送多来的文件中的数据,参数是最大接收的字节数,1024代表1K
recv_data = tcp_socket.recv(1024)
"""
7.保存数据到一个文件中
with open(fileName, option) as f:
以上代码的作用 :在文件能打开的前提下,替代了使用try...except的时候,
如果抛出异常,需要手动关闭文件流的操作,
即,使用以上写法,如果在操作过程中,出现异常,系统自动会关闭文件流
如果没有出现异常,也会在操作结束的时候自动关闭文件流
"""
if recv_data:
# 如果文件内容不是空,才进行下载
with open("[下载]" + download_file_name, "wb") as f:
f.write(recv_data)
# 8.关闭套接字
tcp_socket.close()
if __name__ == "__main__":