Python之路PythonNet,第二篇,网络2
pythonnet 网络2
问题:
什么是七层模型
tcp 和udp区别
三次握手和四次挥手
**************************************************
tcp 数据传输:
recv会不断的取出缓冲区中内容,如果一次没有拿完,那么下次会继续收取没拿完的消息;
tcp 粘包
tcp粘包指的是<发送方> 发送若干次数据的时候,因为是数据流的传输方式,导致数据粘在一起,<接收方>一次将多次发送的数据一起接收,传输接收数据的粘连;
粘包是tcp传输特有的现象,因为tcp传输没有消息边界。 如果是发送连续的内容,比如文件等,则粘包没有影响。如果是每次发送为单独需要处理内容则需要处理粘包;
如何处理粘包?
1,将消息格式化;
2,发送消息的同时发送一个消息长度标识;
3,让消息的发送延迟,使接收端每次都能够有时间接收一个消息;
UDP数据表套接字服务端:SERVER
(面向无连接的不可靠的传输服务)
1,创建数据报套接字;
2,绑定本地IP和端口;
3,收发消息;
recvfrom(BUFFERSIZE)
功能:在udp中接收消息;
参数: buffersize 表示一次最多可以接收多少字节的消息;
返回值:data:接收到的消息;
addr :表示从哪个客户端接收到的消息;
sendto(data, addr)
功能: 向一个网络终端发送消息;
参数:data要发送的消息(bytes)
addr 发送对象的地址;
4,关闭套接字
import sys
sys.argv : 将命令行内容收集为一个列表,每个元素是命令行中的一项;
(命令行传入的内容均为str格式; 命令行内容以空格作为分隔,引号可以合成一个整体;)
#!/usr/bin/python3 import sys print(sys.argv[1]) print(sys.argv[2]) #### # python3 sys_argv.py 192.168.1.10 8888 192.168.1.10 8888
UDP客户端:CLIENT
1,创建数据报套接字
2,消息收发;
3,关闭套接字;
(recvfrom每次只能接收一个数据包,如果数据包的大小超过recvfrom的设置大小,则会出现数据丢失;)
########udp_server########## from socket import * import sys from time import ctime #从命令行传入IP和端口 HOST = sys.argv[1] PORT = int(sys.argv[2]) ADDR = (HOST,PORT) BUFFERSIZE = 5 #创建数据报套接字 sockfd = socket(AF_INET,SOCK_DGRAM) #绑定本地IP和端口 sockfd.bind(ADDR) #收发消息 while True: data,addr = sockfd.recvfrom(BUFFERSIZE) print("recv from ",addr,':',data.decode()) sockfd.sendto\ (("在 %s 接受到你的消息"%ctime()).encode(),addr) #关闭套接字 sockfd.close() ############udp_client############## from socket import * import sys #从命令行传入服务器的IP和端口 HOST = sys.argv[1] PORT = int(sys.argv[2]) ADDR = (HOST,PORT) BUFFERSIZE = 1024 #创建数据报套接字 sockfd = socket(AF_INET,SOCK_DGRAM) #消息收发 while True: data = input("消息>>") #输入空客户端退出 if not data: break #此处发送消息给服务器 sockfd.sendto(data.encode(),ADDR) data,addr = sockfd.recvfrom(BUFFERSIZE) print("从服务器接收:",data.decode()) #关闭套接字 sockfd.close() ############### python3 udp_client.py 192.168.1.112 8888 message>> a SERVER recvfrom...: In Sun Jul 1 17:50:05 2018 recvfrom your messages #### # python3 udp_server.py 192.168.1.112 8888 recvfrom: ('192.168.1.112', 56956) : a
总结tcp和udp的区别:
1,tcp是有连接的,udp是无连接的
2,tcp有三次握手四次挥手的过程;而udp没有;
3,tcp是以数据流传输数据,会有粘包;udp是数据报的形式传输数据,没有粘包;
4,tcp的连接需要消耗一定的资源,相比之下udp资源消耗少;
5,tcp保证数据的可靠性,udp不保证;
6,tcp需要listen accept connect; 而 udp 不需要这些操作;
socket模块
套接字属性
getpeername()
功能:用做服务器连接套接字,查看连接的客户端地址;
getsockname()
功能: 获取套接字对应的绑定的地址和端口;
s.type 套接字类型
fileno()
功能:获取套接字的文件描述符号码;
文件描述符:系统会给进程中的每个IO操作对象匹配一个 >=0 的正整数作为标号,我们称之为该IO操作的文件描述符。一个进程中的所有IO的文件描述符不会重复;
setsockopt(level, optname, value)
功能: 设置套接字选项,可以增加或改变套接字的功能;
参数:level 要定义的选项类型;
例如: SOL_SOCKET 、 IPPROTO_IP、 IPPROTO_TCP
optname 每种类型都有具体的选项,根据具体需求选择选项 , 进行设置;
例如: SOL_SOCKET ---> SO_REUSERADDR (#将端口号设置为立即重用(SOL_SOCKET,SO_REUSEADDR,1) )
value 将选择的现象设置为什么值;
getsockopt(level, optname)
功能:获取相应选项的值
参数:level 要获取的选项类型
optname 每种类型都有具体的选项,根据具体需求选择要获取的选项;
返回值:获取到的值;
######tcp_server##### from socket import * import time HOST = '192.168.1.112' PORT = 8888 ADDR = (HOST,PORT) BUFFERSIZE = 1024 sockfd = socket(AF_INET,SOCK_STREAM) print("您的套接字是:",sockfd.type) print("sockfd 的 file num:",sockfd.fileno()) #将端口号设置为立即重用 sockfd.setsockopt\ (SOL_SOCKET,SO_REUSEADDR,1) print("获取选项值:",sockfd.\ getsockopt(SOL_SOCKET,SO_REUSEADDR)) sockfd.bind(ADDR) sockfd.listen(5) print("您的套接字地址是:",sockfd.getsockname()) while True: print("wait for connect......") conn,addr = sockfd.accept() #使用getpeername获取链接的客户端的地址 print("connect from ",conn.getpeername()) while True: data = conn.recv(BUFFERSIZE) if not data: break print("接受到:",data.decode()) n = conn.send(b"Recv your message\n") print("发送了 %d 字节的数据"%n) conn.close() # 表示和客户端断开连接 sockfd.close() # 不能再使用sockfd ######tcp_client###### cat tcp_client.py #!/usr/local/bin/python3 from socket import * import time HOST = '192.168.1.112' PORT = 8888 ADDR = (HOST,PORT) connfd = socket(AF_INET,SOCK_STREAM) connfd.connect(ADDR) while True: data = input('send>>>') if not data: break connfd.sendall(data.encode()) data = connfd.recv(1024) print('client recv:',data.decode()) connfd.close()
socket服务器模型:
硬件服务器 : 计算机主机 IBM HP
集成 分布式
软件服务器 : 网络服务器,提供后端逻辑服务和请求处理的程序集合及架构
例如 web服务器等
服务器架构 c/s b/s 服务器的组织形式
服务器追求 : 更快速, 更安全,并发量更大
fork
1, 创建套接字 绑定 监听;
2,接收客户端连接请求 创建新的进程;
3,主进程继续接收下一个客户端连接请求,子进程处理客户端事件;
4, 有客户端断开,则关闭响应的子进程;
##########fork_tcp_server############ from socket import * import os import signal #有客户端断开则关闭相应的子进程 def handler(c): while True: data = c.recv(BUFFERSIZE).decode() if not data: break print("服务器收到:",data) c.send(b'receive your message') c.close() os._exit(0) #创建套接字 绑定 监听 HOST = '192.168.1.112' PORT = 8888 ADDR = (HOST,PORT) BUFFERSIZE = 1024 #创建tcp套接字 s = socket() s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(ADDR) s.listen(5) #做僵尸进程的处理 signal.signal(signal.SIGCHLD,signal.SIG_IGN) #接收客户端连接请求 创建新的进程 while True: try: c,addr = s.accept() except KeyboardInterrupt: print("服务器结束") s.close() os._exit(0) except Exception: continue print("接收到客户端链接 >",c.getpeername()) pid = os.fork() if pid < 0: print("创建子进程失败") continue #子进程处理客户端事件 elif pid == 0: s.close() print('处理客户端请求事件') handler(c) # 处理客户端的函数 #主进程继续接收下一个客户端连接请求 else: c.close() continue #########tcp_client######## # cat tcp_server.py #!/usr/local/bin/python3 from socket import * HOST = '127.0.0.1' PORT = 8888 ADDR = (HOST,PORT) BUFFERSIZE = 1024 sockfd = socket(AF_INET, SOCK_STREAM) sockfd.bind(ADDR) sockfd.listen(5) while True: print('wait for connect....') conn,addr = sockfd.accept() print('connect from',addr) while True: data = conn.recv(BUFFERSIZE) if not data: break print('connect:',data.decode()) n = conn.send(b'Recv your message.\n') print('send.. %d'%n) conn.close() sockfd.close()
threading
1, 创建套接字 , 绑定, 监听;
2,接收客户端连接请求, 创建新的线程;
3,主线程继续接收下一个客户端连接请求;分支线程处理客户端事件;
练习
########threading_tcp_server####### from socket import * import threading import os #有客户端断开则关闭相应的子线程 def handler(c): while True: data = c.recv(BUFFERSIZE).decode() if not data: break print("服务器收到:",data) c.send(b'receive your message') c.close() #创建套接字 绑定 监听 HOST = '127.0.0.1' PORT = 8888 ADDR = (HOST,PORT) BUFFERSIZE = 1024 #创建tcp套接字 s = socket() s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(ADDR) s.listen(5) #接收客户端连接请求 创建新的线程 while True: try: c,addr = s.accept() except KeyboardInterrupt: print("服务器结束") s.close() os._exit(0) except Exception: continue print("接收到客户端链接 >",c.getpeername()) #分支线程处理客户端事件 t = threading.Thread\ (target = handler,args = (c,)) t.setDaemon(True) t.start() #主线程继续接收下一个客户端连接请求 ##########tcp_client################# # cat tcp_client.py #!/usr/local/bin/python3 from socket import * import time HOST = '127.0.0.1' PORT = 8888 ADDR = (HOST,PORT) connfd = socket(AF_INET,SOCK_STREAM) connfd.connect(ADDR) while True: data = input('send>>>') if not data: break connfd.sendall(data.encode()) data = connfd.recv(1024) print('client recv:',data.decode()) connfd.close()
socketserver模块 (python2 SocketServer)
'DatagramRequestHandler',
'ForkingMixIn',
'ForkingTCPServer',
'ForkingUDPServer',
'StreamRequestHandler',
'TCPServer',
'ThreadingMixIn',
'ThreadingTCPServer',
'ThreadingUDPServer',
'UDPServer',
三部分:
多进程/多线程 TCP/UDP streamhandler/datagramhandler
ForkingMixIn TCPServer StreamRequestHandler
ThreadingMixIn UDPServer DatagramRequestHandler
组合如下:
‘ThreadingTCPServer’ = ThreadingMixIn + TCPServer
‘ThreadingUDPServer’ = ThreadingMixIn + UDPServer
ForkingTCPServer = ForkingMixIn + TCPServer
ForkingUDPServer = ForkingMixIn + UDPServer
步骤:
1, 创建服务器类;
2,创建处理类;
3,使用创建的服务器类来生产服务器;
# fork + tcp 并发
########socket_server######### # fork + tcp 并发 from socketserver import * #创建服务器类 class Server(ThreadingMixIn,TCPServer): pass # class Server(ForkingTCPServer): # pass #创建处理类 class Handler(StreamRequestHandler): #当有客户端链接时候调用该函数自动处理 #客户段请求事件 def handle(self): print("connect from ",self.client_address) while True: #self.request 为tcp中为我们自动生成的 #和客户端交互的套接字 data = self.request.recv(1024).decode() if not data: break print("服务器收到:",data) self.request.send(b'receive your message') #使用创建的服务器类来生产服务器 server = Server(('172.60.50.218',9999),Handler) #运行服务器 server.serve_forever() #########tcp_client######## # cat tcp_client.py #!/usr/local/bin/python3 from socket import * import time HOST = '127.0.0.1' PORT = 8889 ADDR = (HOST,PORT) connfd = socket(AF_INET,SOCK_STREAM) connfd.connect(ADDR) while True: data = input('send>>>') if not data: break connfd.sendall(data.encode()) data = connfd.recv(1024) print('client recv:',data.decode()) connfd.close()
# fork + udp
##########fork_udp_server############# # fork + udp from socketserver import * class Server(ForkingUDPServer): pass class Handler(DatagramRequestHandler): #udp无连接所以request的含义不同 def handle(self): data = self.rfile.readline() print("接受到了:",data.decode()) self.wfile.write(b"receive message") server = Server(('0.0.0.0',8888),Handler) server.serve_forever() ###########udp_client############## # cat udp_client.py #!/usr/local/bin/python3 from socket import * import sys HOST = sys.argv[1] PORT = int(sys.argv[2]) ADDR = (HOST,PORT) BUFFERSIZE = 1024 sockfd = socket(AF_INET, SOCK_DGRAM) while True: data = input("message>> ") if not data: break sockfd.sendto(data.encode(),ADDR) data,addr = sockfd.recvfrom(BUFFERSIZE) print('SERVER recvfrom...: ', data.decode()) sockfd.close()
思考:ftp传输