socket套接字.是一种通信机制,用于描述IP地址和端口,是一个通信链的句柄.操作socket就像操作Unix一样,一切皆是文件,.
0.0.0.0有多少个网卡,都可以连接到本机.
程序之间进行内存数据交换(通信)比较麻烦:每个程序的内存空间都是被保护的,不能被别的程序直接访问,所以要通过某种介质,管道或第三方工具,去通信.
nosql(第三方):将内存的数据缓存进来,供其它的程序调用,所有的程序都可以往里存数据,所有的程序都可以取数据.相当于实现了一个共享的内存空间.
而所谓的网络编程就是,让在不同的电脑上的软件能够进行数据传递,即进程之间的通信(网络socket)
socket.AF_UNIX;只能够用于单一的Unix系统进程间通信
socket.AF_INET:服务器之间网络通信
socket.AF_INET6 IPv6
socket 处于第4层(传输层) ICMP(网络层)
socket通信的数据格式
socket.SOCK_STREAM :PCP三次握手
socket.SOCKDGRAM:UDP
socket.SOCK_RAW 原始套接字 可以处理普通套接字不能处理的ICMP,IGMP等网络报文 可以可以伪造IP
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
| from socket import * socketobj = socket(AF_INET,SOCK_STREAM) socketobj.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1) socketobj.bind(('localhost',8080)) socketobj.listen(128) while True: connection,address=socketobj.accept() try: while True: data = connection.recv(1024) if len(data) > 0: print('server端收到client端的信息:'+ str(data,encoding = "utf-8")) connection.sendall(bytes("hello client","utf-8")) else: print('[%s]客户端已经关闭'%str(address)) break finally: connection.close() socketobj.close()
|
accept():等待客户端连接时阻塞与非阻塞
recv():等待客户端发送信息时阻塞与非阻塞,两者才是只能服务一个客户端的关键
client
1 2 3 4 5 6 7 8 9 10 11 12
| from socket import * socketobj = socket(AF_INET,SOCK_STREAM) socketobj.connect(('localhost',20000)) while True: user_input =input("msg to server:").strip() socketobj.send(bytes(user_input,'utf-8')) data = socketobj.recv(1024) print("client端收到server端的信息 :"+str(data,encoding = "utf-8")) socketobj.close()
|
多进程与多线程的Socket
利用进程来完成多任务,其实就是启动一个子进程(线程)来完成client.start()
也就是说: 来一个客户端连接分配一个进程或线程为其服务
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
| from socket import * from multiprocessing import * from threading import Thread from time import sleep def dealWithClient(newSocket,destAddr): while True: recvData = newSocket.recv(1024) if len(recvData)>0: print('recv[%s]:%s'%(str(destAddr), recvData)) else: print('[%s]客户端已经关闭'%str(destAddr)) break newSocket.close() def main(): serSocket=socket(AF_INET, SOCK_STREAM) serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1) localAddr=('', 8080) serSocket.bind(localAddr) serSocket.listen(5) try: while True: print('-----主进程,,等待新客户端的到来------') newSocket,destAddr = serSocket.accept() print('-----主进程,,接下来创建一个新的进程(线程)负责数据处理-----') client = Process(target=dealWithClient, args=(newSocket, destAddr)) client.start() newSocket.close() finally: serSocket.close() if __name__ == '__main__': main()
|
区别在于:进程的cow(写时copy):能共用的资源尽量共用,实在不行才copy
单进程-非堵塞socket
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
| from socket import * serSocket = socket(AF_INET, SOCK_STREAM) localAddr = ('', 8080) serSocket.bind(localAddr) serSocket.setblocking(False) serSocket.listen(100) clientAddrList = [] while True: try: clientSocket,clientAddr = serSocket.accept() except: pass else: print("一个新的客户端到来:%s"%str(clientAddr)) clientSocket.setblocking(False) clientAddrList.append((clientSocket,clientAddr)) for clientSocket,clientAddr in clientAddrList: try: recvData = clientSocket.recv(1024) except: pass else: if len(recvData)>0: print("%s:%s"%(str(clientAddr), recvData)) else: clientSocket.close() clientAddrList.remove((clientSocket, clientAddr)) print("%s 已经下线"%str(clientAddr))
|
但如果当有一个Socket使用到耗时的代码时,其它的Socket也将被阻塞
同时使用轮询机制,所以效率不高
epoll版-TCP服务器
Linux平台下的通过epoll来高效管理socket(server_socket,client_socket)
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
| import socket import select s = socket.socket(socket.AF_INET,socket.SOCK_ STREAM) s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) s.bind(("",8080)) s.listen(10) epoll = select.epoll() epoll.register(s.fileno(),select.EPOLLIN|select.EPOLLET) connections= {} addresses ==={} while True: epoll_list=epoll.poll() forfd,events in epoll_list: iffd==s.fileno(): conn,addr=s.accept() print('有新的客户端到来%s'%str(addr)) connections[conn.fileno()] = conn addresses[conn.fileno()] = addr epoll.register(conn .fileno(),select.EPOLLIN|select.EPOLLET) elif events == select.EPOLLIN: recvData = connections[fd].recv(1024) if len(recvData)>0: print('recv:%s'%recvData) else: epoll.unregister(fd) connections[fd].close() print("%s---offline---"%str(addresses[fd]))
|
epoll的优点:socket通过事件通知机制(由socket本身的状态来告诉epoll,哪个socket可用,就不需要去轮询所有的socket了,更加高效的得到可用的Socket)
不是轮询的方式,epoll它只管你”活跃”的连接,而跟连接总数无关
asy_server
参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| from socketserver import BaseRequestHandler, TCPServer, ThreadingTCPServer import os class EchoHandler(BaseRequestHandler): def handle(self): print('Got connection from', self.client_address) while True: msg = self.request.recv(8192) if not msg: print('connection close by', self.client_address) break print(msg) self.request.send(msg) if __name__ == '__main__': serv = ThreadingTCPServer(('0.0.0.0', 20000), EchoHandler) serv.serve_forever()
|