一、socket基础 socket通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过”套接字”向网络发出请求或者应答网络请求。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
socket和file的区别:
file模块是针对某个指定文件进行【打开】【读写】【关闭】
socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
tcp_server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 -*- coding: utf-8 -*- """ @Datetime: 2018/9/3 @Author: Zhang Yafei """ from socket import *ip_port = ('127.0.0.1' ,8000 ) buffer_size = 1024 backlog = 5 tcp_server = socket(AF_INET,SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(backlog) print('服务端开始运行...' ) while True : print('开始接收客户端消息...' ) conn,address = tcp_server.accept() print('双向链接是:' ,conn) print('客户端地址是:' ,address) while True : try : data = conn.recv(buffer_size) print('客户端发来消息:' ,data.decode('utf-8' )) conn.send(data.upper()) except : break conn.close() tcp_server.close()
tcp_client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 """ @Datetime: 2018/9/3 @Author: Zhang Yafei """ from socket import *ip_port = ('127.0.0.1' ,8000 ) buffer_size = 1024 backlog = 5 tcp_client = socket(AF_INET,SOCK_STREAM) tcp_client.connect(ip_port) while True : msg = input('>>:' ) if not msg: continue tcp_client.send(msg.encode('utf-8' )) print('客户端已经发送消息' ) data = tcp_client.recv(buffer_size) print('服务端发来消息:' ,data.decode('utf-8' )) tcp_client.close()
Web服务应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import socket def handle_request (client) : buf = client.recv(1024 ) client.send("HTTP/1.1 200 OK\r\n\r\n" ) client.send("Hello, World" ) def main () : sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost' ,8080 )) sock.listen(5 ) while True : connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__' : main()
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 参数一:地址簇 socket .AF_INET IPv4(默认) socket .AF_INET6 IPv6 socket .AF_UNIX 只能够用于单一的Unix系统进程间通信 参数二:类型 socket .SOCK_STREAM 流式socket , for TCP (默认) socket .SOCK_DGRAM 数据报式socket , for UDP socket .SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。 socket .SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。 socket .SOCK_SEQPACKET 可靠的连续数据包服务 参数三:协议 0 (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议
udp_client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 """ @Datetime: 2018/9/3 @Author: Zhang Yafei """ from socket import *ip_port = ('127.0.0.1' , 8000 ) buffer_size = 1024 udp_client = socket(AF_INET, SOCK_DGRAM) while True :msg = input('>>:' ) udp_client.sendto(msg.encode('utf-8' ), ip_port) print('客户端已发送消息...' ) data,addr = udp_client.recvfrom(buffer_size) print('接收到服务端发送的消息:' ,data.decode('utf-8' ))
udp_server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 """ @Datetime: 2018/9/3 @Author: Zhang Yafei """ from socket import *ip_port = ('127.0.0.1' ,8000 ) buffer_size = 1024 udp_server = socket(AF_INET,SOCK_DGRAM) udp_server.bind(ip_port) print('客户端服务开启...' ) while True :data,addr = udp_server.recvfrom(buffer_size) print('客户端发送消息:' ,data.decode('utf-8' )) udp_server.sendto(data.upper(),addr)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 sk .bind (address ) s .bind (address ) 将套接字绑定到地址。address 地址的格式取决于地址族。在AF_INET 下,以元组(host ,port )的形式表示地址。 sk .listen (backlog ) 开始监听传入连接。backlog 指定在拒绝连接之前,可以挂起的最大连接数量。 backlog 等于5,表示内核已经接到了连接请求,但服务器还没有调用accept 进行处理的连接个数最大为5 这个值不能无限大,因为要在内核中维护连接队列 sk .setblocking (bool ) 是否阻塞(默认True ),如果设置False ,那么accept 和recv 时一旦无数据,则报错。 sk .accept () 接受连接并返回(conn ,address ),其中conn 是新的套接字对象,可以用来接收和发送数据。address 是连接客户端的地址。 接收TCP 客户的连接(阻塞式)等待连接的到来 sk .connect (address ) 连接到address 处的套接字。一般,address 的格式为元组(hostname ,port ),如果连接出错,返回socket .error 错误。 sk .connect_ex (address ) 同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061 sk .close () 关闭套接字 sk .recv (bufsize [,flag] ) 接受套接字的数据。数据以字符串形式返回,bufsize 指定最多可以接收的数量。flag 提供有关消息的其他信息,通常可以忽略。 sk .recvfrom (bufsize [.flag] ) 与recv ()类似,但返回值是(data ,address )。其中data 是包含接收数据的字符串,address 是发送数据的套接字地址。 sk .send (string [,flag] ) 将string 中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string 的字节大小。即:可能未将指定内容全部发送。 sk .sendall (string [,flag] ) 将string 中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None ,失败则抛出异常。 内部通过递归调用send ,将所有内容发送出去。 sk .sendto (string [,flag] ,address ) 将数据发送到套接字,address 是形式为(ipaddr ,port )的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP 协议。 sk .settimeout (timeout ) 设置套接字操作的超时期,timeout 是一个浮点数,单位是秒。值为None 表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s ) sk .getpeername () 返回连接套接字的远程地址。返回值通常是元组(ipaddr ,port )。 sk .getsockname () 返回套接字自己的地址。通常是一个元组(ipaddr ,port ) sk .fileno () 套接字的文件描述符
实例:智能机器人
server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import socketip_port = ('127.0.0.1' ,8888 ) sk = socket.socket() sk.bind(ip_port) sk.listen(5 ) while True : conn,address = sk.accept() conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.' ) Flag = True while Flag: data = conn.recv(1024 ) if data == 'exit' : Flag = False elif data == '0' : conn.sendall('通过可能会被录音.balabala一大推' ) else : conn.sendall('请重新输入.' ) conn.close() 服务端
client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import socketip_port = ('127.0.0.1' ,8005 ) sk = socket.socket() sk.connect(ip_port) sk.settimeout(5 ) while True : data = sk.recv(1024 ) print('receive:' ,data) inp = raw_input('please input:' ) sk.sendall(inp) if inp == 'exit' : break sk.close() 客户端
二、利用socket实现简单命令行交互 客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 """ tcp协议会产生粘包现象,tcp是面向连接,可靠地的协议,基于数据流,所以不能为空,客户端和服务端都要添加空消息处理机制。由于服务端不知道客户端发了多少数据,上一次未传输完数据,会接着上一次传输。 udp不会产生粘包现象,是由于udp传输的是数据报,是面向消息,无连接的协议,udp基于的数据报,即使输入的是空,那也不是空消息,udp会帮你封装上消息头。每个数据报中有消息头(消息来源地址,端口等 """ import timefrom socket import *ip_port = ('127.0.0.1' ,8000 ) buffer_size = 1024 backlog = 5 tcp_client = socket(AF_INET,SOCK_STREAM) tcp_client.connect(ip_port) tcp_client.send('hello' .encode('utf-8' )) tcp_client.send('world' .encode('utf-8' )) tcp_client.send('egon' .encode('utf-8' )) time.sleep(1000 )
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 """ 服务端 发送端发送的数据量较小,多次send相隔时间较短,tcp接收端有一个算法会合并,不知道有几个包 """ from socket import *import subprocessip_port = ('127.0.0.1' , 8000 ) buffer_size = 5 backlog = 5 tcp_server = socket(AF_INET, SOCK_STREAM) tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1 ) tcp_server.bind(ip_port) tcp_server.listen(backlog) conn, addr = tcp_server.accept() data = conn.recv(buffer_size) print('第一次数据:' ,data) data = conn.recv(buffer_size) print('第二次数据:' ,data) data = conn.recv(buffer_size) print('第三次数据:' ,data) conn.close()
粘包现象: TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。
解决粘包
tcp_client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from socket import *ip_port = ('127.0.0.1' ,8000 ) buffer_size = 1024 backlog = 5 tcp_client = socket (AF_INET,SOCK_STREAM) tcp_client.connect(ip_port) while True: cmd = input('>>:' ).strip() if not cmd: continue if cmd == 'quit' : break tcp_client.send (cmd.encode('utf-8' )) length = tcp_client.recv(4 ) length = struct.unpack('i' ,length )[0 ] recv_size = 0 recv_msg = b'' while recv_size < length : recv_msg += tcp_client.recv(buffer_size) recv_size = len (recv_msg) print(recv_msg.decode('gbk' ))
tcp_server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 from socket import *import subprocess import struct ip_port = ('127.0.0.1' , 8000 ) buffer_size = 1024 backlog = 5 tcp_server = socket (AF_INET, SOCK_STREAM) tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1 ) tcp_server.bind(ip_port) tcp_server.listen(backlog) while True: conn, addr = tcp_server.accept() print('新的客户端链接:' , addr) while True: try : cmd = conn.recv(buffer_size) print('收到客户端命令:' , cmd.decode('utf-8' )) res = subprocess.Popen(cmd.decode('utf-8' ),shell =True, stderr =subprocess.PIPE, stdout =subprocess.PIPE, stdin =subprocess.PIPE, ) err = res.stderr .read () if err: cmd_res = err else : cmd_res = res.stdout .read () if not cmd_res: cmd_res = '执行成功' .encode('gbk' ) length = len (cmd_res) data_length = struct.pack('i' ,length ) conn.send (data_length) conn.send (cmd_res) except Exception as e: print(e) break conn.close ()
udp_client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 """ @Datetime: 2018/9/3 @Author: Zhang Yafei """ from socket import *ip_port = ('127.0.0.1' ,8000 ) buffer_size = 8096 backlog = 5 udp_client = socket(AF_INET,SOCK_DGRAM) while True : cmd = input('>>:' ).strip() if not cmd: continue if cmd == 'quit' : break udp_client.sendto(cmd.encode('utf-8' ),ip_port) cmd_res,addr = udp_client.recvfrom(buffer_size) print(cmd_res.decode('gbk' ))
udp_server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 """ @Datetime: 2018/9/3 @Author: Zhang Yafei """ from socket import *import subprocessip_port = ('127.0.0.1' , 8000 ) buffer_size = 1024 backlog = 5 udp_server = socket(AF_INET, SOCK_DGRAM) udp_server.bind(ip_port) while True : try : cmd,addr = udp_server.recvfrom(buffer_size) print('收到客户端命令:' , cmd.decode('utf-8' )) res = subprocess.Popen(cmd.decode('utf-8' ),shell=True , stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, ) err = res.stderr.read() if err: cmd_res = err else : cmd_res = res.stdout.read() if not cmd_res: cmd_res = '执行成功' .encode('gbk' ) udp_server.sendto(cmd_res,addr) except Exception as e: print(e) break udp_server.close()
三、socketserver实现并发 socketserver模块解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 server类:处理链接 | BaseServer | +------------+ | v +-----------+ +------------------+ | TCPServer |-------> | UnixStreamServer | +-----------+ +------------------+ | v +-----------+ +--------------------+ | UDPServer |-------> | UnixDatagramServer | request类:处理通信
socketserver_client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 """ @Datetime: 2018/9/3 @Author: Zhang Yafei """ from socket import *ip_port = ('127.0.0.1' ,8000 ) buffer_size = 1024 backlog = 5 tcp_client = socket(AF_INET,SOCK_STREAM) tcp_client.connect(ip_port) while True : msg = input('>>:' ).strip() if not msg:continue if msg == 'quit' :break tcp_client.send(msg.encode('utf-8' )) data = tcp_client.recv(buffer_size) print(data.decode('utf-8' )) tcp_client.close()
socketserver_server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 """ @Datetime: 2018/9/4 @Author: Zhang Yafei """ import socketserverclass Myserver (socketserver.BaseRequestHandler) : """socketserver内置的通信方法""" def handle (self) : print('conn is:' ,self.request) print('addr is:' ,self.client_address) while True : try : data = self.request.recv(1024 ) if not data:break print('收到的客户端消息是:' ,data.decode('utf-8' ),self.client_address) self.request.sendall(data.upper()) except Exception as e: print(e) break if __name__ == '__main__' : s = socketserver.ThreadingTCPServer(('127.0.0.1' ,8000 ), Myserver) print(s.server_address) print(s.RequestHandlerClass) print(Myserver) print(s.socket) s.serve_forever()
udp_clinet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 """ @Datetime: 2018/9/4 @Author: Zhang Yafei """ from socket import *ip_port = ('127.0.0.1' ,8000 ) buffer_size = 1024 backlog = 5 tcp_client = socket(AF_INET,SOCK_DGRAM) while True : msg = input('>>:' ).strip() if not msg:continue if msg == 'quit' :break tcp_client.sendto(msg.encode('utf-8' ),ip_port) data,addr = tcp_client.recvfrom(buffer_size) print(data.decode('utf-8' )) tcp_client.close()
udp_server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 """ @Datetime: 2018/9/4 @Author: Zhang Yafei """ import socketserverclass Myserver (socketserver.BaseRequestHandler) : """socketserver内置的通信方法""" def handle (self) : print('收到来自客户端的消息:' ,self.request[0 ].decode('utf-8' ),self.client_address) self.request[1 ].sendto(self.request[0 ].upper(),self.client_address) if __name__ == '__main__' : s = socketserver.ThreadingUDPServer(('127.0.0.1' ,8000 ), Myserver) s.serve_forever()
四、认证客户端链接的合法性 client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 """ @Datetime: 2018/9/4 @Author: Zhang Yafei """ from socket import *import hmac,ossecret_key=b'linhaifeng bang bang bang' def conn_auth (conn) : ''' 验证客户端到服务器的链接 :param conn: :return: ''' msg=conn.recv(32 ) h=hmac.new(secret_key,msg) digest=h.digest() conn.sendall(digest) def client_handler (ip_port,bufsize=1024 ) : tcp_socket_client=socket(AF_INET,SOCK_STREAM) tcp_socket_client.connect(ip_port) conn_auth(tcp_socket_client) while True : data=input('>>: ' ).strip() if not data:continue if data == 'quit' :break tcp_socket_client.sendall(data.encode('utf-8' )) respone=tcp_socket_client.recv(bufsize) print(respone.decode('utf-8' )) tcp_socket_client.close() if __name__ == '__main__' : ip_port=('127.0.0.1' ,9999 ) bufsize=1024 client_handler(ip_port,bufsize)
client(不知道key)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 """ @Datetime: 2018/9/4 @Author: Zhang Yafei """ from socket import *import hmac,ossecret_key=b'linhaifeng bang bang bang1111' def conn_auth (conn) : ''' 验证客户端到服务器的链接 :param conn: :return: ''' msg=conn.recv(32 ) h=hmac.new(secret_key,msg) digest=h.digest() conn.sendall(digest) def client_handler (ip_port,bufsize=1024 ) : tcp_socket_client=socket(AF_INET,SOCK_STREAM) tcp_socket_client.connect(ip_port) conn_auth(tcp_socket_client) while True : data=input('>>: ' ).strip() if not data:continue if data == 'quit' :break tcp_socket_client.sendall(data.encode('utf-8' )) respone=tcp_socket_client.recv(bufsize) print(respone.decode('utf-8' )) tcp_socket_client.close() if __name__ == '__main__' : ip_port=('127.0.0.1' ,9999 ) bufsize=1024 client_handler(ip_port,bufsize)
client(不知道加密方式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 """ @Datetime: 2018/9/4 @Author: Zhang Yafei """ from socket import *def client_handler (ip_port,bufsize=1024 ) : tcp_socket_client=socket(AF_INET,SOCK_STREAM) tcp_socket_client.connect(ip_port) while True : data=input('>>: ' ).strip() if not data:continue if data == 'quit' :break tcp_socket_client.sendall(data.encode('utf-8' )) respone=tcp_socket_client.recv(bufsize) print(respone.decode('utf-8' )) tcp_socket_client.close() if __name__ == '__main__' : ip_port=('127.0.0.1' ,9999 ) bufsize=1024 client_handler(ip_port,bufsize)
server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 """ @Datetime: 2018/9/4 @Author: Zhang Yafei """ from socket import *import hmac,ossecret_key=b'linhaifeng bang bang bang' def conn_auth (conn) : ''' 认证客户端链接 :param conn: :return: ''' print('开始验证新链接的合法性' ) msg=os.urandom(32 ) conn.sendall(msg) h=hmac.new(secret_key,msg) digest=h.digest() respone=conn.recv(len(digest)) return hmac.compare_digest(respone,digest) def data_handler (conn,bufsize=1024 ) : if not conn_auth(conn): print('该链接不合法,关闭' ) conn.close() return print('链接合法,开始通信' ) while True : data=conn.recv(bufsize) if not data:break conn.sendall(data.upper()) def server_handler (ip_port,bufsize,backlog=5 ) : ''' 只处理链接 :param ip_port: :return: ''' tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(backlog) while True : conn,addr=tcp_socket_server.accept() print('新连接[%s:%s]' %(addr[0 ],addr[1 ])) data_handler(conn,bufsize) if __name__ == '__main__' : ip_port=('127.0.0.1' ,9999 ) bufsize=1024 server_handler(ip_port,bufsize)
五、多用户在线的FTP程序 此程序我已上传至百度云,感兴趣的同学可以下载学习
链接 :https://pan.baidu.com/s/1noIPV2q-QJ-1m1jCoEu5Ew 提取码 :bjn8
网络编程常见问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 1. OSI 7 层模型 OSI(Open System Interconnect),即开放式系统互联。 OSI定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层)。 应用层:OSI参考模型中最靠近用户的一层,是为计算机用户提供应用接口,也为用户直接提供各种网络服务。我们常见应用层的网络服务协议有:HTTP,HTTPS,FTP,POP3、SMTP等。 表示层:数据压缩和加密也是表示层可提供的转换功能之一 会话层:会话层就是负责建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。 传输层:传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括处理差错控制和流量控制等问题。TCP和UDP 网络层:本层通过IP寻址来建立两个节点之间的连接,为源端的运输层送来的分组,选择合适的路由和交换节点,正确无误地按照地址传送给目的端的运输层。就是通常说的IP层。这一层就是我们经常说的IP协议层。IP协议是Internet的基础。 数据链路层:将比特组合成字节,再将字节组合成帧,使用链路层地址 (以太网使用MAC地址)来访问介质,并进行差错检测。 物理层: 实际最终信号的传输是通过物理层实现的。通过物理介质传输比特流。规定了电平、速度和电缆针脚。常用设备有(各种物理设备)集线器、中继器、调制解调器、网线、双绞线、同轴电缆。这些都是物理层的传输介质。 osi七层模型发送的时候即每一层都是对上一层的封装,上一层的请求头和请求体作为请求体,这一层的请求头作为请求头。 接收的时候按照顺序依次解包取出最后的数据。 2. 三次握手、四次挥手? 所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3 个包。在socket编程中,客户端执行connect()时。将触发三次握手。 客户端说我要连接了,服务端说好的,我知道了,你连吧,客户端说好的,我开始连了。 TCP 四次挥手 TCP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。 客户端说我要断开了,服务器收到后发送一个数据包ACK说我允许你断开,但我现在可能还有点数据没发完,我还需要一些操作。。。等服务端发完发送一个数据包FIN说我发完了,可以断开了,客户端断开连接。 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢? 这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。 但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可能未必会马上 会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文 多数情况下都是分开发送的。 3. TCP和UDP区别? 用的http请求比较多,udp没怎么用过,http是基于tcp的,udp的话可能会用在视频流的时候会用到 1 、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接 2 、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付 3 、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的 UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等) 4 、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信 5 、TCP首部开销20 字节;UDP的首部开销小,只有8 个字节 6 、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道 TCP相当于双方要先建立连接,发送数据和接收数据要确认,而UDP建立连接之后直接发,发送数据后不确认,我发出去了,收到没收到我就不管了。 4. 路由器和交换机的区别? 路由器可以实现局域网之间的互联,交换机一般是在内网组建局域网。 路由器在网络层,路由器根据ip地址寻址,交换机工作在数据链路层,数据帧,根据MAC地址寻址 路由器可以把一个IP分配给很多个主机使用,这些主机对外只表现出一个IP。交换机可以把很多主机连起来,这些主机对外各有各的IP。 多台主机可以通过网线连接到交换机,这时就组建好了局域网,就可以将数据发送给局域网中的其他主机, 通过交换机组建的局域网是不能访问外网的(即是Internet),这时需要路由器来为我们”打开外面精彩世界的大门“,局域网的所有主机使用的都是私网的IP,所以必须通过路由器转化为公网的IP之后才能访问外网。 5. ARP协议?ip --> MAC地址 介于网络层和数据链路层之间 地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。 6. DNS解析?域名:www.pythonav.vom ---> ip: 23.43 .54 .12 应用层协议 DNS 是域名系统 (Domain Name System) 的缩写,它是由解析器和域名服务器组成的。域名服务器是指保存有该网络中所有主机的域名和对应IP地址, 并具有将域名转换为IP地址功能的解析器。 7. Http和Https? 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密, 如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此,HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。 为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议, SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。 8. 进程、线程、协程区别? 进程是资源分配的最小单位,进程是独立的内存空间,一个进程里面多个线程 线程是cpu工作的最小单位,多个线程可以共享进程的内存空间, 协程实际不存在,可以称为微线程,多个协程属于同一个线程。线程发送请求,一遇到IO就切换到其他的事情,实现线程之间的来回切换。但计算密集型任务时协程的效果往往不好, 原因是IO操作不占用cpu IO操作:网络请求,文件操作,终端操作 greenlet是协程模块。 gevent =》 greenlet + IO切换 9. GIL锁 全局解释器锁(Global Interpreter Lock)是计算机程序设计语言解释器用于同步线程的工具,使得任何时刻一个进程仅有一个线程占用cpu 数据安全,自己加锁 可以创建独立的进程来实现并行化 10. 进程如何进程共享? 数据交互:queue,pipe 数据共享:Manager