01: socket模块
网络编程其他篇
目录:
1.1 socket理论部分 返回顶部
1、socket起源
1. socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。
2. socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
3. 基本上,Socket 是任何一种计算机网络通讯中最基础的内容
4. 例如当你在浏览器地址栏中输入 http://www.cnblogs.com/ 时,你会打开一个套接字,然后连接到 http://www.cnblogs.com/ 并读取响应的页面然后然后显示出来
5. socket和file的区别:
1) file模块是针对某个指定文件进行【打开】【读写】【关闭】
2) socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
2、socket.socket( family, type ) 实例化一个socket类
1. 实例化需要3个参数
1) 第一个是地址簇(默认是socket.AF_INET)
2) 第二个是流(socket.SOCK_STREAM, 默认值)或数据报(socket.SOCK_DGRAM)套接字
3) 第三个是实用的协议(默认是0 使用默认即可),对于一个普通的套接字不需要提供任何参数
2. 第一个参数: Socket Families(地址簇) 类似于OSI七层的网络层
1)socket.AF_UNIX unix本机进程间通信(本机没有网卡时本机件进程自己起socket通信)
2)socket.AF_INET IPV4
3)socket.AF_INET6 IPV6
3. 第二个参数: Socket Families(地址簇) 类似于OSI七层的传输层
1)socket.SOCK_STREAM #for tcp
2)socket.SOCK_DGRAM #for udp
3)socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;
其次,SOCK_RAW也可以处理特殊的IPv4报文;此外利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头
4)socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议低级访问,
在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用
3、socket对象可以使用的所有方法
# 1、 sk.bind(address) s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。 # 2、 sk.listen(backlog) 开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。 backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5 这个值不能无限大,因为要在内核中维护连接队列 # 3、 sk.setblocking(bool) 是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。 # 4、 sk.accept() 接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。 接收TCP 客户的连接(阻塞式)等待连接的到来 # 5、 sk.connect(address) 连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 # 6、 sk.connect_ex(address) 同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061 # 7、 sk.close() 关闭套接字 # 8、 sk.recv(bufsize[,flag]) 接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。 # 9、 sk.recvfrom(bufsize[.flag]) 与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 # 10、 sk.send(string[,flag]) 将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。 # 11、 sk.sendall(string[,flag]) 将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 内部通过递归调用send,将所有内容发送出去。 # 12、 sk.sendto(string[,flag],address) 将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 # 13、 sk.settimeout(timeout) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s ) # 14、 sk.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 # 15、 sk.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port) # 16、 sk.fileno() 套接字的文件描述符
4、TCP三层握手
####1、第一次握手 # 建立连接时,客户端发送SYN包到服务器,其中包含客户端的初始序号seq=x,并进入SYN_SENT状态,等待服务器确认。 ####2、第二次握手 # 服务器收到请求后,必须确认客户的数据包。同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN_RECV状态。 ####3、第三次握手 # 客户端收到服务器的SYN+ACK包,向服务器发送一个序列号(seq=x+1),确认号为ack(客户端)=y+1,此包发送完毕, # 客户端和服务器进入ESTAB_LISHED(TCP连接成功)状态,完成三次握手。
#### 1、第一次挥手 # 首先,客户端发送一个FIN,用来关闭客户端到服务器的数据传送,然后等待服务器的确认。其中终止标志位FIN=1,序列号seq=u。 #### 2、第二次挥手 # 服务器收到这个FIN,它发送一个ACK,确认ack为收到的序号加一。 #### 3、第三次挥手 # 关闭服务器到客户端的连接,发送一个FIN给客户端。 #### 4、第四次挥手 # 客户端收到FIN后,并发回一个ACK报文确认,并将确认序号seq设置为收到序号加一。 # 首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
1.2 socket处理单个连接 和 同时接受多个连接 返回顶部
1、socket通信原理图解
2、简单的socket列子:处理单个连接
import socket #服务器端 server = socket.socket() # 创建一个socket对象 server.bind(('',6969)) # host=’ ‘或者‘0.0.0.0’都表示所有计算机都可以访问服务器 server.listen() # server.listen(5) 表示同时可以有五个客户连接到服务器 conn,addr = server.accept() # c, addr = s.accept() 这是一种赋变量的方法,会组成一个元组。 # 代表只要客户端提供地址(addr)就可以接受外部的链接 data = conn.recv(1024) # 收到客户端数据:recv默认是阻塞的,收到数据为空则死循环 conn.send(data.upper()) # 服务器返回数据给client端 server.close()
import socket #客户端 client = socket.socket() # 创建socket对象 client.connect(('localhost',6969)) # 连接到服务端 client.send("在python3中socket仅可以传送byte类型".encode("utf-8")) # 发送数据到服务端 data = client.recv(1024) # 接收服务端返回数据 print('客户端收到返回数据为:',data.decode()) client.close()
3、socket实现同时连接多个客户端
1. 这里仅能实现,同时连接多个客户端,但同一时刻仅能有一个客户端与服务的通信,其他客户端阻塞状态
2. 只有当前一个客户端断开连接后,后面的客户端才能与服务端发消息
3. 经测试,只能在Linux服务器中实现,在Windows中,断开客户端后服务端就会应发异常
#-*- coding:utf-8 -*- import socket #服务器端python3.5 server = socket.socket() server.bind(('',6969)) #绑定要监听的端口 server.listen(5) #我要监听这个端口,最大连接数为:5 print("我要开始等电话了") while True: #当一个客户端断开后程序就回到这里,等待下一个连接 conn,addr = server.accept() #等电话打进来 conn是打电话实例,addr电话号码 #conn就是客户端连过来而在服务器端为其生成的连接实例,这里其实就是为每个客户端生成一个实例区分 while True: data = conn.recv(1024) #data是接收到的从客户端发送过来的数据 print("rect:",data) #将客户端发送过来的数据在服务器端打印 if not data: #如果客户端断开连接,data就为空,就跳出内层while循环,到达外层循环等待下一个客户端连接 print("host has lost!!!") break conn.send(data.upper()) #将客户端发送来的数据变成大写,然后再发送给客户端 server.close()
#-*- coding:utf-8 -*- import socket #客户端python3.5 client = socket.socket() #声明socket类型,同时生成socket连接对象 client.connect(('1.1.1.3',6969)) #指定要连接的服务器地址和端口号 while True: msg = input(">>:").strip() #输入要发送给服务器的消息 if len(msg) == 0:continue #如果客户端输入空格,让客户端继续输入 client.send(msg.encode("utf-8")) #将输入的消息发送到服务器端 data = client.recv(1024) #接收服务器端发送过来的数据,每次1024字节 print('client_recv:',data) #将从服务器端接收的数据在客户端打印出来 client.close()
1.3 socket实现远程执行命令,下载文件 返回顶部
1、使用socket实现远程执行命令
#-*- coding:utf-8 -*- import socket,os #服务器端python3.5 server = socket.socket() server.bind(('1.1.1.3',6969)) #绑定要监听的端口 server.listen(5) #我要监听这个端口 print("我要开始等电话了") while True: conn,addr = server.accept() #等电话打进来 conn是打电话实例,addr电话号码 while True: #conn就是客户端连过来而在服务器端为其生成的连接实例 data = conn.recv(1024) data = data.decode() print("接收的命令:",data) if not data: print("host has lost!!!") break res = os.popen(data).read() conn.send(res.encode("utf-8")) server.close()
#-*- coding:utf-8 -*- import socket #客户端python3.5 client = socket.socket() #声明socket类型,同时生成socket连接对象 client.connect(('1.1.1.3',6969)) while True: msg = input(">>:").strip() client.send(msg.encode("utf-8")) data = client.recv(1024) print('客户端接收到服务端命令结果:',data.decode()) client.close()
2、 socket实现下载文件
1)先在服务器上把要传送的视屏文件打开,用send发送给客户端
2)在客户端上打开一个新文件,将收到数据写入到文件中
3)测试结果是:在客户端每次输入任意符号服务器端就会发送一个1024字节的文件到客户端
4)可以看到的结果是,文件以1024的倍数增加
import socket,os #socket服务器端 server = socket.socket() server.bind(('0.0.0.0',2222)) server.listen(5) while True: conn,addr = server.accept() while True: file_name = conn.recv(1024).decode() file_size = os.stat(file_name).st_size conn.send(str(file_size).encode("utf-8")) with open(file_name,'rb') as f: for line in f: conn.send(line) server.close()
import socket #socket客户端 client = socket.socket() client.connect(('1.1.1.3',2222)) while True: file_name = input("please input you need to download file:") client.send(file_name.encode("utf-8")) file_size = client.recv(1024).decode() print(type(file_size),file_size) file_size = int(file_size) recv_size = 0 with open(file_name + '.new','wb') as f: while recv_size < file_size: data = client.recv(1024) f.write(data) recv_size += 1024 client.close()
1.4 通过socket实现简单的ssh 和 处理连包问题 返回顶部
1、说明
1. 先运行服务器端,再运行客户端,客户端连接后发送数据给服务器端,服务器端将结果返回给客户端
2. 这段代码在windows python3.5中运行和 centos6.5系统中运行都正常
3. 下面用Windows作为客户端,centos6.5作为服务器端演示操作结果
4. 运行发现如果客户端服务端都在centos中运行时,客户端断开服务器端会继续监听下一个连接
5. 但是如果客户端是Windows无论服务器端是什么系统客户端断开服务器端就会自动断开
2、socket实现ssh远程执行命令 及 处理连包问题
import socket,os #ssh服务器端代码 server = socket.socket() server.bind(('0.0.0.0',9999)) #0.0.0.0表示允许所有主机连接 server.listen() print("服务器端ssh开始监听客户端的链接啦!!") while True: conn,addr = server.accept() #每次客户端断开,就会到这里等待新的连接 while True: print("等待新指令!!") client_cmd = conn.recv(1024).decode() #服务器端接收客户端发送过来的命令 if not client_cmd: #1 如果客户端断开连接收到的数据为空就会卡在这里 print("客户端已断开连接") break print("执行命令:",client_cmd) cmd_res = os.popen(client_cmd).read() #接收字符串,执行结果也是字符串 print("before send:",len(cmd_res)) #打印出命令执行结果的长度 if len(cmd_res) == 0: #2 如果命令错误则执行结果长度为 0 cmd_res = "The command is wrong!!" conn.send( str(len(cmd_res.encode())).encode("utf-8") ) #发送命令结果长度 client_ack = conn.recv(1024) #3 为了解决连包问题可以在两个send中间加一个recv conn.send(cmd_res.encode("utf-8")) #将命令执行结果发送给客户端 print("send done") server.close()
import socket #ssh 客户端程序代码 client = socket.socket() client.connect(('localhost',9999)) while True: cmd = input("请输入要执行的命令:") if len(cmd) == 0:continue #1 客户端输入命令为空就不发送让他继续输入命令 client.send(cmd.encode("utf-8")) #将要执行命令发送到服务器 cmd_res_size = client.recv(1024).decode() #接收命令结果的长度 print("执行命令结果的长度:",cmd_res_size) #打印要接收结果的数据长度 client.send('ok'.encode('utf-8')) #2 为解决连包问题可以在连续两次send中间加一个recv received_size = 0 #接收数据的长度初始值为 0 received_data = b'' #接收到的数据初始值为 空字符串 while received_size < int(cmd_res_size): data = client.recv(1024) #每次从服务器端接收1024的数据 received_size += len(data) #接收到的数据长度累加 received_data += data #每次接收的数据累加到 received_data变量中 else: print("The command receive done:",received_size) #打印最终接收的长度 print(received_data.decode()) #将接收到的命令执行结果打印出来 client.close() #1 在centos6.5 10.1.0.50服务器端运行结果: [root@localhost python]# python35 ssh_server.sh 服务器端ssh开始监听客户端的链接啦!! 等待新指令!! 执行命令: df before send: 429 send done 等待新指令!! #2 在Windows 7中运行客户端的结果: 请输入要执行的命令:df 执行命令结果的长度: 429 The command receive done: 429 Filesystem 1K-blocks Used Available Use% Mounted on /dev/mapper/VolGroup-lv_root 38744716 4118012 32658576 12% / tmpfs 510148 224 509924 1% /dev/shm /dev/sda1 495844 34846 435398 8% /boot /dev/sr0 4363088 4363088 0 100% /media/CentOS_6.5_Final /dev/sr0 4363088 4363088 0 100% /mnt
作者:学无止境
出处:https://www.cnblogs.com/xiaonq
生活不只是眼前的苟且,还有诗和远方。