Python网络编程

阅读:333 2019-03-19 15:02:33 来源:新网

例:点分十进ip地址(192.168.5.6),实际上是32位二进制数(01100100.00000100.00000101.00000110)

socket是应用层与tcp/ip协议簇通信的中间软件抽象层,它是一组接口。在设计模式中,socket其实就是一个门面模式,它把复杂的tcp/ip协议族隐藏在socket接口后面,对用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。

unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

(还有af_inet6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,af_inet是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用af_inet)

tcp(transmissioncontrolprotocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用tcp的应用:web浏览器;电子邮件、文件传输程序。

udp(userdatagramprotocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用udp的应用:域名系统(dns);视频流;ip语音(voip)

importsocketsk=socket.socket()sk.bind(('127.0.0.1',8088))sk.listen()conn,addr=sk.accept()whiletrue:res=conn.recv(1024).decode('utf-8')ifres=='bye':breakelse:print(res)info=input('请输入:n>>>').encode('utf-8')conn.send(info)conn.close()sk.close()client端

importsocketsk=socket.socket()sk.connect(('127.0.0.1',8088))whiletrue:info=input('请输入:n>>>').encode('utf-8')sk.send(info)res=sk.recv(1024).decode('utf-8')ifres=='bye':breakelse:print(res)sk.close()对于addressalreadyinuse的情况,可以加入socket配置重用ip和端口

importsocketfromsocketimportsol_socket,so_reuseaddrsk=socket.socket()sk.setsockopt(sol_socket,so_reuseaddr,1)#就是它,在bind前加sk.bind(('127.0.0.1',8898))#把地址绑定到套接字sk.listen()#监听链接conn,addr=sk.accept()#接受客户端链接ret=conn.recv(1024)#接收客户端信息print(ret)#打印客户端信息conn.send(b'hi')#向客户端发送信息conn.close()#关闭客户端套接字sk.close()#关闭服务器套接字(可选)一个服务端与多个客户端交互示例

服务端

importsocketsk=socket.socket()sk.bind(('127.0.0.1',8088))#listen([backlog])中的[backlog]参数代表服务端允许多少客户端连接到服务端,即阻塞队列长度,所以一共能与服务器连接的客户端共有backlog+1个sk.listen(3)whiletrue:conn,addr=sk.accept()#sk.accept()放在循环内部是为了每次与同一个客户端交互结束后会重新建立conn连接,以便下一个客户端连入res=conn.recv(1024).decode('utf-8')ifres=='bye':breakelse:print('收到',res)info=input('请输入>>>').encode('utf-8')conn.send(info)conn.close()#同上面sk.accept()的作用sk.close()

客户端(可以多个客户端逐个与服务端进行通讯)

importsocketsk=socket.socket()sk.connect(('127.0.0.1',8088))whiletrue:info=input('请输入>>>').encode('utf-8')ifinfo=='bye':breakelse:sk.send(info)res=sk.recv(1024).decode('utf-8')print(res)sk.close()基于udp协议的socket:不可靠无连接,效率高server端

importsocketip_port=('127.0.0.1',9000)bufsize=1024udp_server_client=socket.socket(socket.af_inet,socket.sock_dgram)udp_server_client.bind(ip_port)whiletrue:msg,addr=udp_server_client.recvfrom(bufsize)print(msg,addr)udp_server_client.sendto(msg.upper(),addr)client端

importsocketip_port=('127.0.0.1',9000)bufsize=1024udp_server_client=socket.socket(socket.af_inet,socket.sock_dgram)whiletrue:msg=input('>>:').strip()ifnotmsg:continueudp_server_client.sendto(msg.encode('utf-8'),ip_port)back_msg,addr=udp_server_client.recvfrom(bufsize)print(back_msg.decode('utf-8'),addr)黏包

产生原因:粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

特点:只有tcp有粘包现象,udp永远不会粘包

服务端

fromsocketimport*ip_port=('127.0.0.1',8080)tcp_socket_server=socket(af_inet,sock_stream)tcp_socket_server.bind(ip_port)tcp_socket_server.listen(5)conn,addr=tcp_socket_server.accept()data1=conn.recv(10)data2=conn.recv(10)print('----->',data1.decode('utf-8'))print('----->',data2.decode('utf-8'))conn.close()

客户端

importsocketbufsize=1024ip_port=('127.0.0.1',8080)s=socket.socket(socket.af_inet,socket.sock_stream)res=s.connect_ex(ip_port)s.send('hello'.encode('utf-8'))s.send('feng'.encode('utf-8'))接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

服务端

fromsocketimport*ip_port=('127.0.0.1',8080)tcp_socket_server=socket(af_inet,sock_stream)tcp_socket_server.bind(ip_port)tcp_socket_server.listen(5)conn,addr=tcp_socket_server.accept()data1=conn.recv(2)#一次没有收完整data2=conn.recv(10)#下次收的时候,会先取旧的数据,然后取新的print('----->',data1.decode('utf-8'))print('----->',data2.decode('utf-8'))

客户端

importsocketbufsize=1024ip_port=('127.0.0.1',8080)s=socket.socket(socket.af_inet,socket.sock_stream)res=s.connect_ex(ip_port)s.send('hellofeng'.encode('utf-8'))黏包的解决方案原理:问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

直接告知客户端发送数据的长度(server端)

importsocket,subprocesssk=socket.socket()sk.setsockopt(socket.sol_socket,socket.so_reuseaddr,1)sk.bind(('localhost',8070))sk.listen()whiletrue:conn,addr=sk.accept()print('客户端:',addr)whiletrue:msg=conn.recv(1024)ifnotmsg:breakres_temp=subprocess.popen(msg.decode('utf-8'),shell=true,stdin=subprocess.pipe,stdout=subprocess.pipe,stderr=subprocess.pipe)error=res_temp.stderr.read()iferror:res=errorelse:res=res_temp.stdout.read()date_len=len(res)conn.send(str(date_len).encode('utf-8'))data=conn.recv(1024).decode('utf-8')ifdata=='recv_ready':conn.sendall()conn.close()

直接告知客户端发送数据的长度(client端)

importsockets=socket.socket(socket.af_inet,socket.sock_stream)res=s.connect_ex(('127.0.0.1',8080))whiletrue:msg=input('>>:').strip()iflen(msg)==0:continueifmsg=='quit':breaks.send(msg.encode('utf-8'))length=int(s.recv(1024).decode('utf-8'))s.send('recv_ready'.encode('utf-8'))send_size=0recv_size=0data=b''whilerecv_size

借助struct模块打包定制报头(server端)

importsocket,struct,jsonimportsubprocesssk=socket.socket(socket.af_inet,socket.sock_stream)sk.setsockopt(socket.sol_socket,socket.so_reuseaddr,1)#就是它,在bind前加sk.bind(('127.0.0.1',8080))sk.listen(5)whiletrue:conn,addr=sk.accept()whiletrue:cmd=conn.recv(1024)ifnotcmd:breakprint('cmd:%s'%cmd)res=subprocess.popen(cmd.decode('utf-8'),shell=true,stdin=subprocess.pipe,stdout=subprocess.pipe,stderr=subprocess.pipe)err=res.stderr.read()print(err)iferr:back_msg=errelse:back_msg=res.stdout.read()conn.send(struct.pack('i',len(back_msg)))#先发back_msg的长度conn.sendall(back_msg)#在发真实的内容conn.close()

借助struct模块打包定制报头(server端)

importsocket,time,structs=socket.socket(socket.af_inet,socket.sock_stream)res=s.connect_ex(('127.0.0.1',8080))whiletrue:msg=input('>>:').strip()iflen(msg)==0:continueifmsg=='quit':breaks.send(msg.encode('utf-8'))l=s.recv(4)x=struct.unpack('i',l)[0]print(type(x),x)r_s=0data=b''whiler_s

server端

importjsonimportsocketimportstructsk=socket.socket()sk.bind(('127.0.0.1',8090))sk.listen()buffer=1024conn,addr=sk.accept()#接收head_len=conn.recv(4)head_len=struct.unpack('i',head_len)[0]json_head=conn.recv(head_len).decode('utf-8')head=json.loads(json_head)filesize=head['filesize']withopen(head['filename'],'wb')asf:whilefilesize:print(filesize)iffilesize>=buffer:content=conn.recv(buffer)f.write(content)filesize-=len(content)else:content=conn.recv(filesize)f.write(content)breakconn.close()sk.close()

client端

importosimportjsonimportstructimportsocketsk=socket.socket()sk.connect(('127.0.0.1',8090))buffer=2048#发送文件head={'filepath':r'c:usersygdesktop','filename':r'02pythonfullstacks9day32基于udp的socket服务.mp4','filesize':none}file_path=os.path.join(head['filepath'],head['filename'])filesize=os.path.getsize(file_path)head['filesize']=filesizejson_head=json.dumps(head)#字典转成了字符串bytes_head=json_head.encode('utf-8')#字符串转bytes#计算head的长度head_len=len(bytes_head)#报头的长度pack_len=struct.pack('i',head_len)sk.send(pack_len)#先发报头的长度sk.send(bytes_head)#再发送bytes类型的报头withopen(file_path,'rb')asf:whilefilesize:print(filesize)iffilesize>=buffer:content=f.read(buffer)#每次读出来的内容sk.send(content)filesize-=len(content)else:content=f.read(filesize)sk.send(content)breaksk.close()socket方法

服务端套接字函数

s.bind()绑定(主机,端口号)到套接字s.listen()开始tcp监听s.accept()被动接受tcp客户的连接,(阻塞式)等待连接的到来

客户端套接字函数

s.connect()主动初始化tcp服务器连接s.connect_ex()connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数

s.recv()接收tcp数据s.send()发送tcp数据s.sendall()发送tcp数据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()创建一个与该套接字相关的文件hmac模块—客户端链接合法性验证hmac模块—客户端链接合法性验证

链接合法性验证—举例

服务端

#server端importos,socket,hmacsecret_key=b'secretkey'sk=socket.socket(family=socket.af_inet,type=socket.sock_stream)sk.bind(('localhost',8889))sk.listen()defcheck_client(conn):send_msg=os.urandom(32)conn.send(send_msg)h=hmac.new(secret_key,send_msg)digest=h.digest()client_digest=conn.recv(1024)returnhmac.compare_digest(digest,client_digest)conn,addr=sk.accept()is_legal=check_client(conn)ifis_legal:print('合法客户端!')conn.send(b'legal')whiletrue:recv_msg=conn.recv(1024).decode('utf-8')ifrecv_msg.lower()=='q':conn.send(b'q')conn.close()breakelse:print(recv_msg)info=input('服务端>>>').strip()ifinfo:conn.send(info.encode('utf-8'))else:print('非法客户端!')conn.close()sk.close()

客户端

#client端importsocket,hmacsecret_key=b'secretkey'sk=socket.socket(family=socket.af_inet,type=socket.sock_stream)sk.connect(('localhost',8889))msg=sk.recv(1024)h=hmac.new(secret_key,msg)digest=h.digest()sk.send(digest)is_legal=sk.recv(1024)ifis_legal==b'legal':whiletrue:info=input('客户端>>>').strip()ifinfo:sk.send(info.encode('utf-8'))recv_msg=sk.recv(1024).decode('utf-8')ifrecv_msg=='q':breakelse:print(recv_msg)sk.close()利用socketserver模块处理服务端与多客户端交互问题

服务端

#server端importsocketserverclassmyserver(socketserver.baserequesthandler):defhandle(self):whiletrue:recv_msg=self.request.recv(1024).decode('utf-8')ifrecv_msg=='q':self.request.send(b'q')breakelse:print(recv_msg)info=input('服务端>>>').strip()ifinfo:self.request.send(info.encode('utf-8'))if__name__=='__main__':server=socketserver.threadingtcpserver(('localhost',8889),myserver)server.allow_reuse_address=trueserver.serve_forever()

客户端

下一篇: GCD
相关文章
{{ v.title }}
{{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
你可能感兴趣
推荐阅读 更多>
推荐商标

{{ v.name }}

{{ v.cls }}类

立即购买 联系客服