socket是操做系統中I/O系統延伸部分,支持TCP和UDP等網絡通訊協議,它使計算機之間(或其自己)的進程通訊稱爲可能。socket中的socket()函數、recv()函數和send()函數,至關於文件操做中的open()函數、read()函數、write()函數。所以,soket使得操做系統可以以文件描述符的方式對網絡數據進行操做。python
服務端:服務器
import socket udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # ''表示本身電腦的任何一個ip(無線和有限同時鏈接或者電腦有不一樣的網卡(橋接),會有多個ip). # 綁定端口:寫的是本身的ip和固定的端口,通常是寫在sever端 bindAddr = ('', 9001) udpSocket.bind(bindAddr) recvData = udpSocket.recvfrom(1024) # print(recvData) print(recvData[0].decode('gbk')) udpSocket.close() # recvData的格式:(data, ('ip', 端口)).它是一個元組,前面是數據,後面是一個包含ip和端口的元組.
客戶端:網絡
import socket # upd連接 # SOCK_DGRAM:數據報套接字,主要用於UDP協議 udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 關閉防火牆 # 同一網段(局域網)下,主機的ip地址和端口號. sendAddr = ('127.0.0.1', 9001) # sendData = bytes(input('請輸入要發送的數據:'), 'gbk') sendData = input('請輸入要發送的數據:').encode('gbk') # 使用udp發送數據,每一次發送都須要寫上接收方的ip地址和端口號 udpSocket.sendto(sendData, sendAddr) # udpSocket.sendto(b'hahahaha', ('192.168.10.247', 8080)) udpSocket.close()
服務端:多線程
import socket tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcpServer.bind(('', 8899)) tcpServer.listen(5) # tcp的三次握手,寫進了這一句話當中 tcpClient, addr = tcpServer.accept() # tcpServer.accept(),不須要寫ip,能夠接收多個客戶端的。但事先要綁定端口和接入的客戶端的數量 # client 表示接入的新的客戶端 # clientInfo 表示接入的新的客戶端的ip和端口port recvData = tcpClient.recv(1024) print('%s: %s' % (str(addr), recvData.decode('gbk'))) sendData = input("請輸入返回數據: ") tcpClient.send(sendData.encode("gbk")) # tcp的四次握手,寫進了這一句話 tcpClient.close() tcpServer.close() # tcpServer.accept():等待客戶端的接入,自帶堵塞功能:即必須接入客戶端,而後往下執行 # tcpClient.recv(1024): 也是堵塞,不輸入數據就一直等待,不往下執行. # tcpServer建立了兩個套接字,一個是Server,另外一個是tcpClient.Server負責監聽接入的Client,再爲其建立專門的tcpClient進行通訊.
客戶端:socket
import socket tcpClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverAddr = ('127.0.0.1', 8899) # tcp的三次握手,寫進了這一句話 tcpClient.connect(serverAddr) sendData = input('請輸入要發送的數據:') tcpClient.send(sendData.encode('gbk')) recvData = tcpClient.recv(1024) print('接收到的數據爲:%s' % recvData.decode('gbk')) tcpClient.close() # 爲何用send而不是sendto?由於tcp鏈接是事先連接好(只需鏈接一次),之後只管發數據就ok了。 # 而udp必須用sendto,是發一次數據,鏈接一次。每次發送必需要指定對方的ip和port。 # 相同的道理,在tcpServer端,要寫recv,而不是recvfrom來接收數據
服務端:tcp
import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 9003)) server.listen(5) while True: serverThisClient, addr = server.accept() while True: recvData = serverThisClient.recv(1024) if len(recvData) > 1: print('recv: %s' % recvData.decode('utf-8')) sendData = input('send: ') serverThisClient.send(sendData.encode('utf-8')) else: print('再見!') break serverThisClient.close() server.close()
客戶端:函數
import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('127.0.0.1', 9003)) while True: data = input("send:") client.send(data.encode("utf-8")) recv = client.recv(1024) print("recv: %s" % recv.decode("utf-8")) client.close()
socket是自帶阻塞的。一次只能接收和處理一個連接。若是一次接收多個鏈接,能夠用多線程的方式實現。改寫上面服務器的寫法,客戶端保持不變(能夠啓動多個client看看效果)。spa
from threading import Thread import socket class Connect(object):
"""connect就是一個連接""" def __init__(self, conn, addr): self.conn = conn # 本次連接的conn self.addr = addr # 本次連接的ip地址和端口 self.bsize = 1024 self.before() try: self.handle() except: raise socket.SO_ERROR self.after() def handle(self): try: while True: recvData = self.conn.recv(self.bsize) print("recv: %s." % recvData.decode("utf-8")) sendData = input("send: ") self.conn.send(sendData.encode("utf-8")) except: self.conn.close() def before(self): print("Connected from: %s." % self.addr[0]) def after(self): print("Closed from %s." % self.addr[0]) def main(): tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcpSocket.bind(('', 9003)) tcpSocket.listen(5) while True: conn, addr = tcpSocket.accept() Thread(target=Connect, args=(conn, addr)).start() tcpSocket.close() if __name__ == '__main__': main()
上面已經實現了多線程的socket鏈接。python提供了socketServer來實現功能更全面的封裝。對上面的服務端用socketServer進行重寫。操作系統
import socketserver class Server(socketserver.BaseRequestHandler): def handle(self): while True: recvData = self.request.recv(1024) print("recv: %s." % recvData.decode("utf-8")) sendData = input("send: ") self.request.send(sendData.encode("utf-8")) if __name__ == '__main__': server = socketserver.ThreadingTCPServer(("", 9003), Server) server.serve_forever()
服務端:線程
(文件地址:https://files.cnblogs.com/files/kuaizifeng/Python%E9%87%8D%E8%A6%81%E7%9A%84%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93.pdf.zip)
import socketserver, struct class MyServer(socketserver.BaseRequestHandler): def handle(self): f = open("Python重要的第三方庫.pdf.zip", mode="ab") size = self.request.recv(4) # 只接收4個字節 size = struct.unpack('i', size)[0] # struct模塊將python中的數據類型轉換成C中的字節。第一次發數據大小,是int類型(struct用i表示),它在C中佔4個字節 print(size) # 若是recv接收不是4個字節,則會報錯(粘包) length = 0 while True: data = self.request.recv(1024) f.write(data) length += len(data) if len(data) < 1024: f.close() break if length == size: print("over.") else: print("error.") if __name__ == '__main__': server = socketserver.ThreadingTCPServer(("", 9009), MyServer) server.serve_forever()
客戶端:
import socket, struct, os client = socket.socket() client.connect(("localhost", 9009)) filepath= os.path.dirname(os.path.dirname(__file__)) # 定位這個文件路徑 file = os.path.join(filepath, "Python重要的第三方庫.pdf.zip") f = open(file, mode="rb") count = 0 size = os.path.getsize(file) size = struct.pack('i', size) client.send(size) print("size: {}.".format(size)) while True: data = f.read(1024) client.send(data) count += 1 if len(data) < 1024: break print("send: {}.".format(count)) client.close()