首页 文章 精选 留言 我的

精选列表

搜索[python],共10000篇文章
优秀的个人博客,低调大师

初学Python——Socket网络编程

认识socket socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递。我们知道网络 通信 都 是基于 ip+port(端口) 方能定位到目标的具体机器上的具体服务,操作系统有0-65535个端口,每个端口都可以独立对外提供服务,如果 把一个公司比做一台电脑 ,那公司的总机号码就相当于ip地址, 每个员工的分机号就相当于端口, 你想找公司某个人,必须 先打电话到总机,然后再转分机 。 建立一个socket必须至少有2端, 一个服务端,一个客户端, 服务端被动等待并接收请求,客户端主动发起请求, 连接建立之后,双方可以互发数据。 基本参数 Socket Families(地址簇) socket.AF_UNIX 本机进程间通信 socket.AF_INET IPV4(默认) socket.AF_INET6 IPV6 Socket Types(类型) socket.SOCK_STREAM 流式socket,代表TCP协议(默认) socket.SOCK_DGRAM 数据报式socket,代表UDP协议 socket方法 sk = socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None) 建立socket连接对象 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,将所有内容发送出去。 服务端步骤: 步骤:1.server = socket.socket() 声明实例,生成连接对象2.server.bind() 绑定要监听的端口3.server.listen() 开始监听4.conn,addr = server.accept()等待客户端发起连接,阻塞5.接收数据(发送数据)6.当客户端断开连接后,继续监听等待下一个客户端建立连接……关闭连接对象 代码示例: import socket "服务器端" server = socket.socket() # 生成连接对象 server.bind(("localhost",6000)) # 绑定要监听的端口 server.listen(5) # 开始监听(最大允许挂起的连接) while True: print("\n服务器在等待...") conn,addr = server.accept() # 等待客户端发起建立连接,起到阻塞作用 # conn 是客户端链接过来而在服务器端为其生成的连接实例,addr是IP地址+端口 print("已成功连接") print("连接对象:{0},地址:{1}\n".format(conn,addr)) while True: try: data = conn.recv(1024) # 接收数据 # (如果客服端断开连接,此步骤将会被无限循环操作,所以一定要有检查机制) if data != b"000001": # 接收到的信号不是"000001"的话就正常执行 print("接收客户端信息:", data.decode()) msg = input(">>输入返回客户端的数据:") conn.send(msg.encode(encoding="utf-8")) # 向客户端发送数据 else: print("该客户已主动断开连接") break except ConnectionResetError as e: print("该客户机异常!已被强迫断开连接",e) break else: print("It's OK !") server.close() 客户端步骤: 步骤:1.client = socket.socket() 声明实例,生成连接对象2.client.connect() 与服务器建立连接3.与服务器交互(发送接收数据)4.client.close() 断开连接 代码示例: import socket "通信案例客户端消息接收与发送" client = socket.socket() # 声明socket类型,并生成socket连接对象 try: client.connect(("localhost",6000)) # 与服务器建立连接 while True: msg = input(">>输入要向服务器发送的信息:") client.send(msg.encode(encoding="utf-8")) # 向服务器发送信息(只能发送bytes字节类型,不能是str字符类型) data = client.recv(1024) # 接收来自服务器的1024个字节 print("接收来自服务器的数据:",data.decode()) # 打印服务器发送的数据 chioce = input("按任意键继续,按0退出客户端") if chioce == "0": client.send(b"000001") # 发送此信号表明客户端要断开连接 break client.close() except ConnectionRefusedError as e: print("服务器还没开机!请静候") 需要注意的是: 1.客户端再发送数据时,要主要服务器接收的大小限制。如果超过了这个限制,超出的部分暂时存在系统缓冲区,第二次接收的时候再输出剩下的部分。例如服务器端的recv(1024),而客户端一次发了2024字节,那么剩下的1000字节存在缓冲区,第二次接收的时候会接收缓冲区的内容,将不会接收新发来的数据,会造成数据错误。官方建议一次性不超过8192字节 2.双发收发数据只能是bytes类型 3.粘包问题,下面讲 socket粘包问题 什么是粘包呢?我们知道发送数据,并且数据量比较大时,并不会一次性发送,即使能一次发送,客户端也不一定能一次性接收,所以服务器有个缓冲区,等客户端下次再接收数据的时候再发送给客户端,所以,就需要将数据分成几次发送,客户端分成几次接收。那又出来问题了,客户端知道数据(文件)有多大么?它怎么知道要接收几次?当然是要服务器告诉他啦! 于是,我们设计服务器首先发送数据大小(数据),再开始分批发送数据,客户端先接收文件大小(数据),再开始分批接收。问题就有可能在这里出现了。如果连续2次send数据,很有可能将两次的数据黏在一起发送出去,在客户端也无法将其分开,怎么办呢? 我们可以让服务器每次发送数据后,接收来自客户端的确认,这样会强制清空缓冲区,就不会造成粘包。当然,基于上面讲的方法,只需要在发送正式数据之前接收确认就好。 最后,如果希望100%确认双发收发数据是否一致,可以采用MD5校验。 服务器端步骤: 1.读取文件名2.检测文件是否存在3.打开文件4.检测文件大小5.将文件大小发给客户端6.确认7.开始边读边发(循环发送)8.发送MD5 代码: import socket,os,hashlib ser = socket.socket() ser.bind(("localhost",5000)) ser.listen() while True: try: print("正在等待客户端连接...") conn,addr = ser.accept() print("已连接,new conn:",addr) while True: data = conn.recv(8192) filename = data.decode() print("寻找文件",filename) if os.path.isfile(filename): conn.send(b"01") conn.recv(1024) f = open(filename,"rb") m = hashlib.md5() file_size = os.stat(filename).st_size conn.send(str(file_size).encode(encoding="utf-8")) client_ack = conn.recv(1024) # 接收确认信息 if client_ack == b"1": print("开始发送数据") for line in f: m.update(line) conn.send(line) f.close() conn.send(m.hexdigest().encode(encoding="utf-8")) # 发送MD5值 else: conn.send(b"00") #表示文件不存在 print("该文件不存在!") except ConnectionResetError: print("该客户端已断开连接") ser.close() print("服务器已关闭") 客户端步骤: 1.发送接收文件请求,同时将文件名发送给服务器 2.接收文件长度 3.本地新建同名文件,循环接收数据,并将其写入文件 4.同时更新本地MD5值 5.接收数据完毕后,再接收服务器的MD5值,与本地MD5值进行比较 代码: import socket,hashlib def receive1(client): "真正的数据接收" while True: res = b"" res = res + client.recv(1024) return res def receive(client,filename): "接收处理" m = hashlib.md5() rece_res_size = int(client.recv(1024).decode()) # 接收的结果长度,转成int型 client.send(b"1") rece_size = 0 res = b"" filename = filename.decode() f = open(filename + ".new","wb") while rece_size < rece_res_size: if rece_res_size - rece_size >1024: # 如果不是最后一次接收数据 size = 1024 else: # 最后一次接收数据 size = rece_res_size - rece_size a = client.recv(size) # 循环接收数据 res = res + a rece_size = len(res) m.update(a) f.write(a) print("发送数据量:{0},接收数据量:{1}".format(rece_res_size,rece_size)) else: serves_md5 = client.recv(1024).decode() print("服务器MD5:",serves_md5) print("客户端MD5:",m.hexdigest()) if serves_md5 == m.hexdigest(): print("文件接收并校验完毕!") res.decode() f.close() def main(): client = socket.socket() try: client.connect(("localhost", 5000)) while True: filename = input("请输入需要的文件名").strip().encode(encoding="utf-8") if len(filename) == 0: print("输入为空,重新输入") continue client.send(filename) is_file = client.recv(1024) if is_file == b"01": client.send(b"OK") receive(client,filename) # 调用函数接收数据,返回结果res(bytes) else: print(" {0} 文件不存在!".format(filename.decode())) except ConnectionRefusedError: print("等待服务器开机") client.close() main() socketserver 什么是socketserver?为什么需要它呢? 我们在前面的文章中普通的socket并不能同时处理多个客户端,当一个客户端在与服务器连接时,其它客户端只能排队。而sockerserver则不同,它可以并发地处理多个客户端请求。 import socketserver ''' 每一个客户端请求过来,都会实例化 MyTCPHandler ''' class MyTCPHandler(socketserver.BaseRequestHandler): def handle(self): "跟客户端所有的交互都是在handle里完成的" while True: try: self.data = self.request.recv(1024).strip() print("{0} wrote:".format(self.client_address[0])) print(self.data) if not self.data: print("输入为空") self.request.send(bytes("输入为空", "utf-8")) else: self.request.send(self.data.upper()) except ConnectionResetError : print("客户已断开连接") break if __name__ == "__main__": HOST, PORT = "localhost", 9999 #server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) # 实例化一对一的连接对象 server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) # 实例化多并发的连接对象(多线程) server.serve_forever() 客户端并没有什么区别: import socket HOST, PORT = "localhost", 9999 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((HOST, PORT)) while True: data = input("输入字符") try: sock.sendall(bytes(data + "\n", "utf-8")) received = str(sock.recv(1024), "utf-8") finally: print("Sent: {0}".format(data)) print("Received: {0}".format(received)) sock.close()

优秀的个人博客,低调大师

初学Python——RabbitMQ的安装

记录踩坑之路,本篇文章主要摘抄自CSDN博客https://blog.csdn.net/weixin_39735923/article/details/79288578 Windows10环境下安装RabbtiMQ的步骤 第一步:下载并安装Erlang 原因:RabbitMQ服务器端代码是使用Erlang语言编写的,它依赖于Erlang运行。 下载地址:http://www.erlang.org/downloads 根据本机系统的位数选择合适的版本。 双击下载好的安装包,开始安装 安装完成后要配置环境变量 在系统环境变量中新建 变量名:ERLANG_HOME 变量值就是刚才erlang的安装地址,点击确定。 然后双击系统变量path 点击“新建”,将%ERLANG_HOME%\bin加入到path中。 最后windows键+R键,输入cmd,再输入erl,看到版本号就说明erlang安装成功了。 第二步:下载并安装RabbitMQ 下载地址:http://www.rabbitmq.com/download.html 双击下载后的.exe文件,安装过程与erlang的安装过程相同。 RabbitMQ安装好后接下来安装RabbitMQ-Plugins插件。打开命令行cd,输入RabbitMQ的sbin目录。 我的目录是:D:\Program Files\RabbitMQ Server\rabbitmq_server-3.7.3\sbin 然后在后面输入rabbitmq-plugins enable rabbitmq_management命令进行安装 打开sbin目录,双击rabbitmq-server.bat 等几秒钟看到这个界面后,访问http://127.0.0.1:15672 然后可以看到如下界面 默认用户名和密码都是guest 登陆即可。 安装步骤暂时就写这么多。如果一切顺利,不会出现什么问题,但是我的电脑上可没有这么顺利! 在安装RabbitMQ-Plugins插件时出了问题,无论如何也安装不成功,Pycharm中的代码也直接运行出错。同时双击rabbitmq-server.bat也会自动窗口消失。查找了许久,发现windows服务中RabbitMQ没有开启 (当然这是已经解决问题之后的截图,当时的情况是停止状态,无论如何点击启动按钮,都会立马自动停止运行,并提示错误1067) 查找许久才发现这是由于计算机名称是中文导致的,遂改成英文,重启。发现RabbitMQ服务已经自动启动,然后继续上次出错的步骤,配置成功!

优秀的个人博客,低调大师

Python类的内置方法

目录 1、new、init 2、str、repr 3、call 4、del 5、iter、next 6、getitem、setitem、delitem 7、getattr、setattr、delattr 8、getatrribute 9、enter、exit 10、get、set、delete、描述符(研究中,待补充) **(为了方便和美观,省略了各内置方法前后的__双下划线)** 1、new、init __new__方法是真正的类构造方法,用于产生实例化对象(空属性)。重写__new__方法可以控制对象的产生过程。__init__方法是初始化方法,负责对实例化对象进行属性值初始化,此方法必须返回None,__new__方法必须返回一个对象。重写__init__方法可以控制对象的初始化过程。 # 使用new来处理单例模式 class Student: __instance = None def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance = object.__new__(cls) return cls.__instance def sleep(self): print('sleeping...') stu1 = Student() stu2 = Student() print(id(stu1), id(stu2)) # 两者输出相同 print(stu1 is stu2) # True 个人感觉,__new__一般很少用于普通的业务场景,更多的用于元类之中,因为可以更底层的处理对象的产生过程。而__init__的使用场景更多。 2、str、repr 两者的目的都是为了显式的显示对象的一些必要信息,方便查看和调试。__str__被print默认调用,__repr__被控制台输出时默认调用。即,使用__str__控制用户展示,使用__repr__控制调试展示。 # 默认所有类继承object类,object类应该有一个默认的str和repr方法,打印的是对象的来源以及对应的内存地址 class Student: def __init__(self, name, age): self.name = name self.age = age stu = Student('zlw', 26) print(stu) # <__main__.Student object at 0x0000016ED4BABA90> # 自定义str来控制print的显示内容,str函数必须return一个字符串对象 # 使用repr = str来偷懒控制台和print的显示一致 class Student: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f'{self.__class__}, {self.name}, {self.age}' __repr__ = __str__ stu = Student('zlw', 26) print(stu) # <class '__main__.Student'>, zlw, 26 3、call __call__方法提供给对象可以被执行的能力,就像函数那样,而本质上,函数就是对象,函数就是一个拥有__call__方法的对象。拥有__call__方法的对象,使用callable可以得到True的结果,可以使用()执行,执行时,可以传入参数,也可以返回值。所以我们可以使用__call__方法来实现实例化对象作为装饰器: # 检查一个函数的输入参数个数, 如果调用此函数时提供的参数个数不符合预定义,则无法调用。 # 单纯函数版本装饰器 def args_num_require(require_num): def outer(func): def inner(*args, **kw): if len(args) != require_num: print('函数参数个数不符合预定义,无法执行函数') return None return func(*args, **kw) return inner return outer @args_num_require(2) def show(*args): print('show函数成功执行!') show(1) # 函数参数个数不符合预定义,无法执行函数 show(1,2) # show函数成功执行! show(1,2,3) # 函数参数个数不符合预定义,无法执行函数 # 检查一个函数的输入参数个数, # 如果调用此函数时提供的参数个数不符合预定义,则无法调用。 # 实例对象版本装饰器 class Checker: def __init__(self, require_num): self.require_num = require_num def __call__(self, func): self.func = func def inner(*args, **kw): if len(args) != self.require_num: print('函数参数个数不符合预定义,无法执行函数') return None return self.func(*args, **kw) return inner @Checker(2) def show(*args): print('show函数成功执行!') show(1) # 函数参数个数不符合预定义,无法执行函数 show(1,2) # show函数成功执行! show(1,2,3) # 函数参数个数不符合预定义,无法执行函数 4、del __del__用于当对象的引用计数为0时自动调用。__del__一般出现在两个地方:1、手工使用del减少对象引用计数至0,被垃圾回收处理时调用。2、程序结束时调用。__del__一般用于需要声明在对象被删除前需要处理的资源回收操作 # 手工调用del 可以将对象引用计数减一,如果减到0,将会触发垃圾回收 class Student: def __del__(self): print('调用对象的del方法,此方法将会回收此对象内存地址') stu = Student() # 调用对象的__del__方法回收此对象内存地址 del stu print('下面还有程序其他代码') class Student: def __del__(self): print('调用对象的del方法,此方法将会回收此对象内存地址') stu = Student() # 程序直接结束,也会调用对象的__del__方法回收地址 5、iter、next 这2个方法用于将一个对象模拟成序列。内置类型如列表、元组都可以被迭代,文件对象也可以被迭代获取每一行内容。重写这两个方法就可以实现自定义的迭代对象。 # 定义一个指定范围的自然数类,并可以提供迭代 class Num: def __init__(self, max_num): self.max_num = max_num self.count = 0 def __iter__(self): return self def __next__(self): if self.count < self.max_num: self.count += 1 return self.count else: raise StopIteration('已经到达临界') num = Num(10) for i in num: print(i) # 循环打印1---10 6、getitem、setitem、delitem 重写此系列方法可以模拟对象成列表或者是字典,即可以使用key-value的类型。 class StudentManager: li = [] dic = {} def add(self, obj): self.li.append(obj) self.dic[obj.name] = obj def __getitem__(self, item): if isinstance(item, int): # 通过下标得到对象 return self.li[item] elif isinstance(item, slice): # 通过切片得到一串对象 start = item.start stop = item.stop return [student for student in self.li[start:stop]] elif isinstance(item, str): # 通过名字得到对象 return self.dic.get(item, None) else: # 给定的key类型错误 raise TypeError('你输入的key类型错误!') class Student: manager = StudentManager() def __init__(self, name): self.name = name self.manager.add(self) def __str__(self): return f'学生: {self.name}' __repr__ = __str__ stu1 = Student('小明') stu2 = Student('大白') stu3 = Student('小红') stu4 = Student('胖虎') # 当做列表使用 print(Student.manager[0]) # 学生: 小明 print(Student.manager[-1]) # 学生: 胖虎 print(Student.manager[1:3]) # [学生: 大白, 学生: 小红] # 当做字典使用 print(Student.manager['胖虎']) # 学生: 胖虎 7、getattr、setattr、delattr 当使用obj.x = y的时候触发对象的setattr方法,当del obj.x的时候触发对象的delattr方法。 当尝试访问对象的一个不存在的属性时 obj.noexist 会触发getattr方法,getattr方法是属性查找中优先级最低的。 可以重写这3个方法来控制对象属性的访问、设置和删除。 **特别注意:如果定义了getattr,而没有任何代码(即只有pass),则所有不存在的属性值都是None而不会报错,可以使用super().__getattr__()方法来处理** class Student: def __getattr__(self, item): print('访问一个不存在的属性时候触发') return '不存在' def __setattr__(self, key, value): print('设置一个属性值的时候触发') # self.key = value # 这样会无限循环 self.__dict__[key] = value def __delattr__(self, item): print('删除一个属性的时候触发') if self.__dict__.get(item, None): del self.__dict__[item] stu = Student() stu.name = 'zlw' # 设置一个属性值的时候触发 print(stu.noexit) # 访问一个不存在的属性时候触发 , 返回'不存在' del stu.name # 删除一个属性的时候触发 8、getatrribute 这是一个属性访问截断器,即,在你访问属性时,这个方法会把你的访问行为截断,并优先执行此方法中的代码,此方法应该是属性查找顺序中优先级最高的。属性查找顺序: 实例的getattribute-->实例对象字典-->实例所在类字典-->实例所在类的父类(MRO顺序)字典-->实例所在类的getattr-->报错 class People: a = 200 class Student(People): a = 100 def __init__(self, a): self.a = a def __getattr__(self, item): print('没有找到:', item) def __getattribute__(self, item): print('属性访问截断器') if item == 'a': return 1 return super().__getattribute__(item) stu = Student(1) print(stu.a) # 1 9、enter、exit 这两个方法的重写可以让我们对一个对象使用with方法来处理工作前的准备,以及工作之后的清扫行为。 class MySQL: def connect(self): print('启动数据库连接,申请系统资源') def execute(self): print('执行sql命令,操作数据') def finish(self): print('数据库连接关闭,清理系统资源') def __enter__(self): # with的时候触发,并赋给as变量 self.connect() return self def __exit__(self, exc_type, exc_val, exc_tb): # 离开with语句块时触发 self.finish() with MySQL() as mysql: mysql.execute() # 结果: # 启动数据库连接,申请系统资源 # 执行sql命令,操作数据 # 数据库连接关闭,清理系统资源 10、get、set、delete、描述符(研究中,待补充)

优秀的个人博客,低调大师

使用python绘出常见函数

'''''' ''' mpl.rcParams['font.sans-serif'] = ['SimHei'] mpl.rcParams['axes.unicode_minus'] = False用来解决不能使用汉字问题,需要导入matplotlib ''' import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl mpl.rcParams['font.sans-serif'] = ['SimHei'] mpl.rcParams['axes.unicode_minus'] = False import math x=np.arange(0.05,3,0.05) #设置X坐标轴 y1=[5 for i in x] plt.plot(x,y1,linewidth=2,label=u'常函数:y=5') #常函数 y2=[2*i+1 for i in x] plt.plot(x,y2,linewidth=2,label=u'一次函数:y=2x+1') #二次函数,在$内的内容能正确显示x^2 y3=[1.5*i*i-3*i+1 for i in x] plt.plot(x,y3,linewidth=2,label=u'二次函数:y=1.5$x^2$-3x+1') #幂函数,math,pow(x,y) x是底数 y是指数 y4=[math.pow(i,2) for i in x] plt.plot(x,y4,linewidth=2,label=u'幂函数:y=$x^2$') #指数函数 y5=[math.pow(2,i) for i in x] plt.plot(x,y5,linewidth=2,label=u'指数函数:y=$2^x$') #对数函数,math.log(x,y) y是可以设置的底数 y6=[math.log(i,2) for i in x] plt.plot(x,y6,linewidth=2,label=u'对数函数:y=logx2(x)') #-4pi到4pi之间产生一百个等差值 x1=np.linspace(-4*np.pi,4*np.pi,100) y7=[np.sin(i) for i in x1] y8=[np.cos(i) for i in x1] plt.plot(x1,y7,label='y=sin(x)',c='g',linewidth=2) plt.plot(x1,y8,label='y=cos(x)',c='r',linewidth=2) #突出某条具体的线 # plt.plot([1,1],[-3],5,'--',color='#999999',linestyle=2) #plt.grid(True)是否显示网格线 plt.legend(loc='lower right') plt.grid(True) plt.show()

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册