python【第七篇】网络编程
为什么需要socket
不同的机器需要通信,必须通过socket,常见的需要通信的软件游戏比如英雄联盟,服务器运行服务端软件即LOL服务端,客户端运行客户端软件即LOL客户端(TGP)等,服务器端软件封装socket服务端、文件、SQL解析指令等,客户端软件封装socket客户端、SQL解析指令等。逛淘宝也是一样的道理。
socket是什么
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序 而程序的pid是同一台机器上不同进程或者线程的标识
socket工作原理
套接字发展史及分类
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
socket工作流程
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
··
socket()模块函数用法
import socket socket.socket(socket_family,socket_type,protocal=0) socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。 获取tcp/ip套接字 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 获取udp/ip套接字 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。 例如tcpSock = socket(AF_INET, SOCK_STREAM)
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字
面向锁的套接字方法
s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间
面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件
基于TCP的套接字
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
tcp服务端
ss = socket() #创建服务器套接字 ss.bind() #把地址绑定到套接字 ss.listen() #监听链接 inf_loop: #服务器无限循环 cs = ss.accept() #接受客户端链接 comm_loop: #通讯循环 cs.recv()/cs.send() #对话(接收与发送) cs.close() #关闭客户端套接字 ss.close() #关闭服务器套接字(可选)
tcp客户端
cs = socket() # 创建客户套接字 cs.connect() # 尝试连接服务器 comm_loop: # 通讯循环 cs.send()/cs.recv() # 对话(发送/接收) cs.close() # 关闭客户套接字
一次通话
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 phone.bind(('127.0.0.1',8000)) #绑定手机卡 phone.listen(5) #开机 print('---->') conn,addr=phone.accept() #等电话 (阻塞) data=conn.recv(1024) #收消息(阻塞) print('客户端发来的消息是: ',data) conn.send(data.upper())#发消息 conn.close() phone.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1',8000)) #拨通电话 phone.send('hello地方'.encode('utf-8')) #发消息 data=phone.recv(1024) # (阻塞) print('收到服务端的发来的消息:',data)
循环通话
对于windows,client程序退出后server异常,对于linux,client程序退出后server一直接收空数据进入死循环
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买手机 phone.bind(('127.0.0.1', 8000)) # 绑定手机卡 phone.listen(5) # 开机 print('---->') conn, addr = phone.accept() # 等电话 (阻塞) while True: # 通信循环 data = conn.recv(1024) # 收消息(阻塞) print('客户端发来的消息是: ', data) conn.send(data.upper()) # 发消息 conn.close() phone.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1',8000)) #拨通电话 while True: # 通信循环 msg=input('>>: ').strip() phone.send(msg.encode('utf-8')) #发消息,发送空给server,server接收空一直阻塞,不会进一步发送数据给client(客户端打通电话不说话,服务端等待客户端说话,然后大家都不说话。。。) data=phone.recv(1024) print(data) phone.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1',8000)) #拨通电话 while True: # 通信循环 msg=input('>>: ').strip() if not msg:continue phone.send(msg.encode('utf-8')) #发消息,发送空给server,server接收空一直阻塞,不会进一步发送数据给client(客户端打通电话不说话,服务端等待客户端说话,然后大家都不说话。。。) data=phone.recv(1024) print(data) phone.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买手机 phone.bind(('127.0.0.1', 8000)) # 绑定手机卡 phone.listen(5) # 开机 print('---->') while True: conn, addr = phone.accept() # 等电话 (阻塞) while True: # 通信循环 try: # 应对windows系统:如果不捕获异常,客户端程序停止后,服务端程序异常停止,ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。 msg = conn.recv(1024) # 收消息(阻塞) if not msg: break # linux系统 print('客户端发来的消息是: ', msg) # linux系统 conn.send(msg.upper()) # 发消息 except Exception: break conn.close() phone.close()
基于UDP的套接字
udp是无链接的,先启动哪一端都不会报错
udp服务端
ss = socket() #创建一个服务器的套接字 ss.bind() #绑定服务器套接字 inf_loop: #服务器无限循环 cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送) ss.close() # 关闭服务器套接字
udp客户端
cs = socket() # 创建客户套接字 comm_loop: # 通讯循环 cs.sendto()/cs.recvfrom() # 对话(发送/接收) cs.close() # 关闭客户套接字
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket udpserver=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udpserver.bind(('127.0.0.1',8080)) while True: #通讯循环 data,client_addr=udpserver.recvfrom(1024) print('======>',data.decode('utf-8')) print(client_addr) # msg=input('>>: ') udpserver.sendto(data.upper(),client_addr)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket udpclient=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) server_ip_port=('127.0.0.1',8080) while True: inp=input(">>: ") udpclient.sendto(inp.encode('utf-8'),server_ip_port) data,server_addr=udpclient.recvfrom(1024) print(data.decode('utf-8'))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket udpclient=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) server_ip_port=('127.0.0.1',8080) while True: inp=input(">>: ") udpclient.sendto(inp.encode('utf-8'),server_ip_port) data,server_addr=udpclient.recvfrom(1024) print(data.decode('utf-8'))
粘包现象
粘包原因:接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
在python语法里,socket.send(x)的含义是把x当作一个包发送到缓存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。 例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束 所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。 此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。 TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。 UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。 tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略 udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠 tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。
两种情况下会发生粘包。
1.发送方发送数据时间间隔很短,且数据量很小,会合到一起,产生粘包
1.发送时间间隔短,数据量小
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#买手机 import socket,time import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port=('127.0.0.1',8080) phone.bind(ip_port) phone.listen(5) conn,addr=phone.accept() # data1=conn.recv(1024) # data2=conn.recv(1024) data1=conn.recv(1024) #b'helloworldSB' data2=conn.recv(1024) #b'' # print('第一个包',data1) print('第二个包',data2) server
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#买手机 import socket,time import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port=('127.0.0.1',8080) phone.connect(ip_port) # phone.send('helloworld'.encode('utf-8')) phone.send('SB'.encode('utf-8'))
第一个包 b'helloworldSB' 第二个包 b''
补充:发送间隔时间长,则不粘包
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#买手机 import socket,time import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port=('127.0.0.1',8080) phone.bind(ip_port) phone.listen(5) conn,addr=phone.accept() # data1=conn.recv(1024) # data2=conn.recv(1024) data1=conn.recv(1024) #b'helloworld' data2=conn.recv(1024) #b'SB' # print('第一个包',data1) print('第二个包',data2)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#买手机 import socket,time import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port=('127.0.0.1',8080) phone.connect(ip_port) # phone.send('helloworld'.encode('utf-8')) time.sleep(3) phone.send('SB'.encode('utf-8'))
第一个包 b'helloworld' 第二个包 b'SB'
2.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#买手机 import socket,time import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port=('127.0.0.1',8080) phone.bind(ip_port) phone.listen(5) conn,addr=phone.accept() time.sleep(3) data1=conn.recv(1) #b'h' 一次没有收完整 data2=conn.recv(1024) #b'elloworldSB' 下次收的时候,会先取旧的数据,然后取新的 # print('第一个包',data1) print('第二个包',data2)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#买手机 import socket,time import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port=('127.0.0.1',8080) phone.connect(ip_port) phone.send('hello'.encode('utf-8')) phone.send('SB'.encode('utf-8'))
第一个包 b'h' 第二个包 b'elloSB'
解决粘包的处理方法
LOW
得到包的大小再获取包(即自定制报头的长度信息),当然比这更low的是time.sleep
远程执行命令
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#coding:utf-8 #买手机 import socket import struct import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port = ('127.0.0.1', 8080) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(ip_port) #开机 phone.listen(5) #等待电话 #链接循环 while True: conn,addr=phone.accept() print('client addr',addr) #通讯循环 while True: try: cmd=conn.recv(1024) res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out_res=res.stdout.read() err_res=res.stderr.read() data_size=len(out_res)+len(err_res) #发送报头 conn.send(struct.pack('i',data_size)) # i表示的范围有限,但是数据长度可能无限 #发送数据部分 conn.send(out_res) conn.send(err_res) except Exception: break conn.close() phone.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#买手机 import socket import struct phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #拨通电话 ip_port = ('127.0.0.1', 8080) phone.connect(ip_port) # 通信循环 while True: # 发消息 cmd = input('>>: ').strip() if not cmd: continue phone.send(bytes(cmd, encoding='utf-8')) #收报头 baotou=phone.recv(4) data_size=struct.unpack('i',baotou)[0] #字节数据格式转换为python数据类型 # 收数据 recv_size=0 recv_data=b'' while recv_size < data_size: data=phone.recv(1024) recv_size+=len(data) recv_data+=data print(recv_data.decode('gbk')) phone.close()
HIGH
远程执行命令
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#coding:utf-8 #买手机 import socket import struct import json import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定电话卡 ip_port = ('127.0.0.1', 8080) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(ip_port) #开机 phone.listen(5) #等待电话 #链接循环 while True: conn,addr=phone.accept() print('client addr',addr) #通讯循环 while True: try: cmd=conn.recv(1024) res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out_res=res.stdout.read() err_res=res.stderr.read() data_size=len(out_res)+len(err_res) head_dic={'data_size':data_size} head_json=json.dumps(head_dic) # head_json='{'data_size':data_size}' head_bytes=head_json.encode('utf-8') # head_json=b'{'data_size':data_size}' #part1:先发报头的长度 head_len=len(head_bytes) conn.send(struct.pack('i',head_len)) #part2:再发送报头 conn.send(head_bytes) #part3:最后发送数据部分 conn.send(out_res) conn.send(err_res) except Exception: break conn.close() phone.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 买手机 import socket import struct import json phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 拨通电话 ip_port = ('127.0.0.1', 8080) phone.connect(ip_port) # 通信循环 while True: # 发消息 cmd = input('>>: ').strip() if not cmd: continue phone.send(bytes(cmd, encoding='utf-8')) # part1:先收报头的长度 head_struct=phone.recv(4) head_len=struct.unpack('i',head_struct)[0] # part2:再收报头 head_bytes=phone.recv(head_len) head_json=head_bytes.decode('utf-8') head_dic=json.loads(head_json) print(head_dic) data_size = head_dic['data_size'] #part3:收数据 recv_size = 0 recv_data = b'' while recv_size < data_size: data = phone.recv(1024) recv_size += len(data) recv_data += data print(recv_data.decode('gbk')) # 在windows上测试使用gbk,linux需使用utf-8 phone.close()
单线程Socket
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ('112.74.97.66',9999) #只接受ip为112.74.97.66的请求,开放9999端口 sk = socket.socket() sk.bind(ip_port) #绑定ip和端口 sk.listen(5) #最大允许连接5个 while True: print 'server waiting...' conn,addr = sk.accept() #阻塞,接收到连接后,生成一个实例conn,该实例conn只为该client服务,还有其他连接时会另外生成 一个实例conn client_data = conn.recv(1024) print client_data conn.sendall('不要回答,不要回答,不要回答') conn.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!-*- coding:utf-8 -*- import socket ip_port = ('139.129.119.104',9999) #连接到139.129.119.104:9999 sk = socket.socket() sk.connect(ip_port) sk.sendall('请求占领地球') server_reply = sk.recv(1024) print server_reply sk.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Server # python server.py server waiting... 请求占领地球 server waiting... Client # python client.py 不要回答,不要回答,不要回答 #
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ # Echo server program import socket HOST = '' # 表示接受所有IP PORT = 50007 # 任意非特权端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #打开IPV4,TCP类型的socket s.bind((HOST, PORT)) #绑定到一个地址和端口 s.listen(1) #侦听进来的连接 conn, addr = s.accept() #接受连接,addr为客户端IP,conn为建立的连接 print 'Connected by', addr while 1: #不断接受数据 data = conn.recv(1024) #接受1024个bit数据,即Client发送的数据 if not data: break #客户端无数据就停止接收数据 print "data from %s %s:" % addr,data conn.sendall(data) #接受了数据就返回数据给客户端 conn.close() #关闭连接
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ # Echo client program import socket HOST = '192.168.11.7' # 服务端IP PORT = 50007 # 和服务端相同的端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #打开IPV4,TCP类型的socket s.connect((HOST, PORT)) #向服务端发送一个连接 s.sendall('Hello, world') #发送数据 data = s.recv(1024) #接受数据 s.close() #关闭socket print 'Received', repr(data)
Socket连续交互通信:加层循环
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ # Echo server program import socket HOST = '' # 表示接受所有IP PORT = 50007 # 任意非特权端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #打开IPV4,TCP类型的socket s.bind((HOST, PORT)) #绑定到一个地址和端口 s.listen(1) #侦听进来的连接 while 1: #不断接受连接 conn, addr = s.accept() #虽然在循环下,但是该线程一直占据该连接,故只接收一个连接 print 'Conneted by:',addr while 1: data = conn.recv(1024) #处于阻塞状态,接受1024个bit数据,即Client发送的数据后,向下执行 if not data: break #客户端无数据就停止接收数据 print "client:%s" % data print "server:", server_data = raw_input() conn.sendall(server_data) #接受了数据就返回数据给客户端 conn.close() #关闭连接
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ # Echo client program import socket,time HOST = '127.0.0.1' # 服务端IP PORT = 50007 # 和服务端相同的端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #打开IPV4,TCP类型的socket s.connect((HOST, PORT)) #向服务端发送一个连接 while 1: print "client:", client_data = raw_input() s.sendall(client_data) #发送数据 data = s.recv(1024) #接受数据,处于阻塞状态,接受1024个bit数据,即Client发送的数据后,向下执行 if not data: break #客户端没有接收到反馈数据就断开 print "server : %s" % data s.close() #关闭socket
模拟ssh协议一
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ # Echo server program import socket,os HOST = '' # 表示接受所有IP PORT = 50007 # 任意非特权端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #打开IPV4,TCP类型的socket s.bind((HOST, PORT)) #绑定到一个地址和端口 s.listen(1) #侦听进来的连接 while 1: #不断接受数据 conn, addr = s.accept() #虽然在循环下,但是该线程一直占据该连接,故只接收一 个连接 print 'Conneted by:',addr while 1: data = conn.recv(1024) #接受1024个bit数据,即Client发送的数据 if not data: break #客户端无数据就停止接收数据 print "data from %s %s:" % addr,data cmd_result = os.popen(data).read() conn.sendall(cmd_result) #接受了数据就返回数据给客户端 conn.close() #关闭连接
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ # Echo client program import socket,time HOST = '192.168.11.7' # 服务端IP PORT = 50007 # 和服务端相同的端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #打开IPV4,TCP类型的socket s.connect((HOST, PORT)) #向服务端发送一个连接 while 1: cmd = raw_input("Your Command:").strip() s.sendall(cmd) #发送数据 data = s.recv(1024) #接受数据 if not data: break #客户端没有接收到反馈数据就断开 time.sleep(2) print 'Received', data s.close() #关闭socket
模拟ssh协议二
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_* # Echo server program import socket,os s.bind((HOST, PORT)) #绑定到一个地址和端口 s.listen(1) #侦听进来的连接 while 1: #不断接受数据 conn, addr = s.accept() #虽然在循环下,但是该线程一直占据该连接,故只接收个 连接 print 'Conneted by:',addr while 1: data = conn.recv(1024) #接受1024个bit数据,即Client发送的数据 if not data: break #客户端无数据就停止接收数据 print "data from %s %s:" % addr,data cmd_status,cmd_result =commands.getstatusoutput(data) if len(cmd_result.strip()) !=0: print cmd_status,cmd_result conn.sendall(cmd_result) else: conn.sendall("DONE") #接受了数据就返回数据给客户端 conn.close() #关闭连接
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ # Echo client program import socket,time HOST = '192.168.11.7' # 服务端IP PORT = 50007 # 和服务端相同的端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #打开IPV4,TCP类型的socket s.connect((HOST, PORT)) #向服务端发送一个连接 while 1: cmd = raw_input("Your Command:").strip() s.sendall(cmd) #发送数据 data = s.recv(1024) #接受数据 if not data: break #客户端没有接收到反馈数据就断开 time.sleep(2) print data s.close() #关闭socket
多线程Socket
(centos自带的python2.6不支持,需升级到2.7以上)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import SocketServer,commands class MyTCPHandler(SocketServer.BaseRequestHandler): def handle(self): while 1: self.data = self.request.recv(1024).strip() print "{} wrote:".format(self.client_address[0]) if not self.data: print "client %s is dead!" % self.client_address[0] break cmd_status,cmd_result = commands.getstatusoutput(self.data) if len(cmd_result.strip()) != 0: self.request.sendall(cmd_result) else: self.request.sendall('DONE') if __name__ == "__main__": HOST, PORT = "localhost", 9999 server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler) server.serve_forever()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ # Echo client program import socket,time,tab HOST = 'localhost' # 服务端IP PORT = 9999 # 和服务端相同的端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #打开IPV4,TCP类型的socket s.connect((HOST, PORT)) #向服务端发送一个连接 while 1: cmd = raw_input("Your Command:").strip() if len(cmd) == 0:continue s.sendall(cmd) #发送数据 data = s.recv(8096) #接受数据 print data s.close() #关闭socket
ftp
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_* import SocketServer,commands,time class MyTCPHandler(SocketServer.BaseRequestHandler): def handle(self): while 1: self.data = self.request.recv(1024).strip() #接收数据 print "{} wrote:".format(self.client_address[0]) #打印client地址 if not self.data: #没有收到数据就打印并停止接收 print "client %s is dead!" % self.client_address[0] break user_input = self.data.split() #分割client命令 if user_input[0] == 'get': #若命令第一个字段为get print "start down file......" with open(user_input[1],'rb') as f: #以读的方式打开目的此文件 self.request.sendall(f.read()) #把读取的文件内容发给client time.sleep(0.5) self.request.send('FileTransferDone') continue cmd_status,cmd_result = commands.getstatusoutput(self.data) if len(cmd_result.strip()) != 0: self.request.sendall(cmd_result) else: self.request.sendall('DONE') if __name__ == "__main__": HOST, PORT = "localhost", 9999 server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler) server.serve_forever()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#_*_coding:utf-8_*_ # Echo client program import socket,time HOST = 'localhost' # 服务端IP PORT = 9999 # 和服务端相同的端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #打开IPV4,TCP类型的socket s.connect((HOST, PORT)) #向服务端发送一个连接 while 1: cmd = raw_input("Your Command:").strip() #接受输入 if len(cmd) == 0:continue #判断输入是否为空 s.sendall(cmd) #发送输入的数据 if cmd.split()[0] == 'get': #命令第一个字段为get一直下载文件的内容 with open(cmd.split()[1],'wb') as f: while 1: data = s.recv(1024) if data == "FileTransferDone":break#没有收到数据时停止接收 f.write(s.recv(1024)) #把收到的数据写入文件f print "getting file......" continue else: print s.recv(1024) s.close() #关闭socket
异步非阻塞Socket
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import sys import socket import time import gevent from gevent import socket, monkey monkey.patch_all() def server(port): s = socket.socket() s.bind(('0.0.0.0', port)) s.listen(500) while True: cli, addr = s.accept() gevent.spawn(handle_request, cli) def handle_request(conn): try: while True: data = conn.recv(1024) print("recv:", data) conn.send(data) if not data: conn.shutdown(socket.SHUT_WR) except Exception as ex: print(ex) finally: conn.close() if __name__ == '__main__': server(8001) server
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket HOST = 'localhost' # The remote host PORT = 8001 # The same port as used by the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) while True: msg = bytes(input(">>:"), encoding="utf8") s.sendall(msg) data = s.recv(1024) # print(data) print('Received', repr(data)) s.close()