一、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__":