python(十三):网络编程之socket与socketserver

  socket是操做系统中I/O系统延伸部分,支持TCP和UDP等网络通讯协议,它使计算机之间(或其自己)的进程通讯称为可能。socket中的socket()函数、recv()函数和send()函数,至关于文件操做中的open()函数、read()函数、write()函数。所以,soket使得操做系统可以以文件描述符的方式对网络数据进行操做。python

一、socket中udp通讯

  服务端:服务器

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()

二、socket中的TCP通讯

  服务端:多线程

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来接收数据

三、socket开启循环

  服务端: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多线程

  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()

五、使用自带的socketServer模块实现socket多线程

  上面已经实现了多线程的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()

六、socketserver实现文件传输

  服务端:线程

  (文件地址: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()
相关文章
相关标签/搜索