1、socket就是将下边的TCP/IP协议进行封装,留出来的接口用户通信
1.1、Socket Families
socket.AF_UNIX unix本机进程间通信
socket.AF_INET IPV4
socket.AF_INET6 IPV6
1.2、Socket Types
socket.SOCK_STREAM #for tcp
socket.SOCK_DGRAM #for udp
2、Socket 参数
2.1、socket.
socket
(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
声明socket类型,同时生成socket链接对象
2.2、s.bind(address)
s.bind(address) 将套接字绑定到地址
2.3、s.listen(backlog)
开始监听传入连接
2.4、sk.setblocking(bool)
是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。
2.5、sk.accept()
接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
2.6、sk.connect(address)
连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误
2.7、sk.close()
关闭套接字
2.8、sk.recv(bufsize[,flag])
接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。
2.9、sk.send(string[,flag])
将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
2.10、sk.sendall(string[,flag])
将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
2.11、sk.settimeout(timeout)
设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
2.12、sk.getpeername()
返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)
3、基本Socket实例
socket_server.py:
import socketserver = socket.socket()server.bind(('localhost',9999)) #绑定要监听的接口server.listen() #监听print("我要开始等电话了")while True: conn, addr = server.accept() # 接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来 print("电话来了") while True: data = conn.recv(1024) #等着收数据 #为了防止收到是数据是Ctrl + C,是Ctrl + C的话会出现异常,其实就是收到的数据一直是空,判断如果是空,就重新监听 if not data: print("lost...") break conn.send(data.upper())
server.close()
socket_client.py:import socketclient = socket.socket() #声明socket类型,同时生成socket链接对象client.connect(('localhost',9999))while True: msg = input(">>:").strip() #不能Send空 if len(msg) == 0: continue client.send(msg.encode("utf-8"))
"""在 Python3 中,bytes 和 str 的互相转换方式是 str.encode('utf-8') 转成byte bytes.decode('utf-8') 转成str""" data = client.recv(1024) print(data.decode('utf-8')) #decode默认就解成unicodeclient.close() 4、通过socket实现简单得SSHsocket ssh server:server.bind('localhost',9999)server .listen()while True: conn,addr = server.accept() print("new conn",addr) whilt True: print("等待客户指令") data = conn.recv(1024) if not data: print("客户端已断开") break print("执行指令:"data) cmd_res = os.popen(data.decode()).read #接收字符串的命令,数目相关执行结果 conn.send(cmd_res.encode('utf-8')) #如果服务端没有任何执行结果,是不会客户端发送数据的 print('send done') socket ssh client:import socketclient = socket.socket()client.connect(('localhost',9999))while True: cmd = input(">>:").strip() if len(cmd) == 0: continue client.send(cmd.encode('utf-8')) cmd_res_size = client.recv(1024) print("命令结果大小:",cmd_res_size.decode())client.close()以上会出现,加入服务端对输入的命令无响应,客户端就会进入等待收命令的假死状态,而且由于接收缓存区的问题,会出现粘包的问题。解决方法粘包的方法,就是先发数据的大小,客户端收到数据大小之后,进行比较,一直不断的接受,直到收到的完整的数据5、socket优化版import socketsocket ssh server:
import socket,osserver = socket.socket()server.bind(('localhost',9999))server.listen()while True: conn,addr = server.accept() print("new conn:",addr) while True: print("等待客户指令") data = conn.recv(1024) if not data: print("客户端已断开") break print("执行指令:",data) cmd_res = os.popen(data.decode()).read() #接受字符串,执行结果也是字符串 print("before send",len(cmd_res.encode())) if len(cmd_res) == 0: cmd_res = "cmd has no output..." conn.send(str(len(cmd_res.encode())).encode('utf-8')) #先发大小给客户端 aaa = conn.recv(1024) #等待确认 print(aaa.decode()) conn.send(cmd_res.encode('utf-8')) #客户端没有收到数据,因为没有数据,服务器是不会发的 print("send done")server.close()
socket ssh client:client = socket.socket()client.connect(('localhost',9999))while True: cmd = input(">>:").strip() # print(cmd) if len(cmd) == 0: continue client.send(cmd.encode('utf-8')) cmd_res_size = client.recv(1024) #收到要传数据的大小 client.send(b"len is ok") #为了防止和下边即将收到的数据粘包,对收到的数据进行回应,此种方法只能针对小于size大小的数据包 print("命令结果大小:",cmd_res_size.decode()) received_size = 0 total_size = int(cmd_res_size.decode()) received_data = b'' while received_size < total_size: #为了防止粘包,对收到的数据进行处理 if total_size - received_size > 1024: #只要未收到的大于1024就赋值1024,要不然赋值剩余的收取的数据 size = 1024 else: size = total_size - received_size print("last receive:", size) data = client.recv(size) received_size += len(data) received_data += data else: print("cmd res recevie done...",received_size) print(received_data.decode())#后边就可以继续加要收取的数据了,这样子和上边的数据就不会粘包了client.close()6、socket简单的ftp服务器import socket,os,hashlibserver = socket.socket()server.bind(('localhost',9999))server.listen()while True: conn,addr = server.accpect() print("new conn:",addr) while True: print("等待指令”) data = conn.recv(1024) if not data: print("客户端已经断开") break cmd,filename = data.decode().split() #默认已空格进行分割 print(filename) if os.path.isfile(filename): f = open(filename,'rb') m = hashlib.md5() #stat 系统调用时用来返回相关文件的系统状态信息,后边很多属性,这次只用了st_size的属性 file_size = os.stat(filename).st_size #文件的大小 conn.send(str(file_size).encode()) #send file size conn.recv(1024) #wait for ack for line in f: #文件都是可以按照行读取的,这种不是一次性读取,这种方式是迭代器 conn.send(line) m.update(line) f.close() conn.send(m.hexdigest().encode()) print(‘send done’)server.close() ftp client: import socket,hashlibclient = socket.socket()client.connect(('localhost',9999))while True: cmd = input(">>:").strip() if len(cmd) == 0:continue # python startswith() 方法用于检查字符串是否是以指定子字符串开头,如果是则返回 True,否则返回 False if cmd.startswith("get"): client.send(cmd.encode()) server_response = client.recv(1024) print("server respone:",server_response) client.send(b"ready to recv file") m = hashlib.md5() file_total_size = int(server_response.decode()) received_size = 0 filename = cmd.split()[1] print(filename) f = open(filename + ".new","wb") while received_size < file_total_size: if file_total_size - received_size > 1024: size = 1024 else: size = file_total_size - received_size print("last receive:",size) data = client.recv(size) received_size += len(data) m.update(data) f.write(data) else: print("file recv done",received_size,file_total_size) new_file_md5 = m.hexdigest() f.close() server_file_md5 = client.recv(1024) print("server file md5:",server_file_md5.decode()) print("client file md5:",new_file_md5)client.close() 7、socket 用类来写1、导入socketserver 模块2. 继承socketserver.BaseRequestHandler类3.重写handle方法,在handle方法中实现所有的socket内容 4.实例化socket server:import socketserverclass MyTCPHandler(socketserver.BaseRequestHandler): def handle(self): while True: try: self.data = self.request.recv(1024).strip() #request方法就是接收或者发送数据 if not self.data: print("lost.....") else: # str.format() 其实是将括号中的文字放到前面 print("{} write:".format(self.client_address[0])) #这些client_address都是继承父类的属性
print(self.client_address) print(self.data) self.request.send(self.data.upper()) except Exception as e: print("err",e) breakif __name__ == "__main__": HOST, PORT = "localhost", 9999 server = socketserver.ThreadingTCPServer((HOST, PORT),MyTCPHandler) #执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ... server.serve_forever()
socket client:
import socket,sysHOST, PORT = 'localhost',9999#sys.argv[1:] 就是提取传入的参数1包括后面的变量# data = " ".join(sys.argv[1:])# Create a socket (SOCK_STREAM means a TCP socket)sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #这是默认参数,可以直接写成 socket.socket()sock.connect((HOST,PORT))while True: data = input(">>:").strip() sock.sendall(data.encode('utf-8')) received = str(sock.recv(1024).decode()) print("Sent: {}".format(data)) print("Received: {}".format(received))sock.close()
来源:https://www.cnblogs.com/qianyuyu/p/10120046.html