zoukankan      html  css  js  c++  java
  • ConsoleWebsocketServer服务端和ConsoleWebsocketClient客户端

    本文是简述了Websocket的服务端和客户端的实时通讯过程,Websocket的服务端和客户端的具体使用使用了2种Websocket的服务端和2种客户端。

    以下代码使用的是Visual Studio 2019 Enterprise版本,控制台项目使用的是 .NETFramework,Version=v4.7.2,同时为了调试方便,做了log4net日志处理,log4net日志的使用可以百度

    Websocket服务端

    1、新建项目ConsoleWebsocketServer

    2、项目右键 管理Nuget程序包,,搜索  SuperWebSocketNETServer,添加即可

    3、新建文件夹ServerEntities,然后再该文件夹下添加HttpAndWebsocket和ConsoleAppWebsocketServer这两个类,代码如下

    HttpAndWebsocket

    using System;
    using System.Net;
    using System.Net.WebSockets;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;

    namespace ConsoleWebsocketServer.ServerEntities
    {
    public class HttpAndWebsocket
    {
    public async void Start(string url)
    {
    string url1 = "http://+:80/";
    int Port = 1234;
    HttpListener httpListener = new HttpListener();
    httpListener.Prefixes.Add("http://+:" + Port + "/");
    httpListener.Start();
    while (true)
    {
    try
    {
    HttpListenerContext httpListenerContext = await httpListener.GetContextAsync();
    try
    {
    if (httpListenerContext.Request.IsWebSocketRequest)
    {
    ProcessRequest(httpListenerContext);
    }
    }
    catch (Exception)
    {
    httpListenerContext.Response.StatusCode = 400;
    httpListenerContext.Response.Close();
    }
    }
    catch (Exception)
    {
    throw;
    }
    }
    }

    private async void ProcessRequest(HttpListenerContext listenerContext)
    {
    HttpListenerWebSocketContext httpListenerWebSocket;

    try
    {
    httpListenerWebSocket = await listenerContext.AcceptWebSocketAsync(null);
    }
    catch (Exception)
    {
    listenerContext.Response.StatusCode = 500;
    listenerContext.Response.Close();
    return;
    }
    WebSocket webSocket = httpListenerWebSocket.WebSocket;
    try
    {

    while (webSocket.State == WebSocketState.Open)
    {
    byte[] returnBytes = new byte[10240];
    WebSocketReceiveResult webSocketReceiveResult = await webSocket.ReceiveAsync(new ArraySegment<byte>(returnBytes), CancellationToken.None);
    string ReceivesData = Encoding.UTF8.GetString(returnBytes, 0, webSocketReceiveResult.Count);
    ReceivesData = $"我已经收到数据:{ReceivesData}";
    returnBytes = Encoding.UTF8.GetBytes(ReceivesData);
    await webSocket.SendAsync(new ArraySegment<byte>(returnBytes), WebSocketMessageType.Text, true, CancellationToken.None);
    await Task.Delay(TimeSpan.FromSeconds(3));
    }
    }
    catch (Exception)
    {

    throw;
    }
    finally
    {
    if (webSocket != null)
    {
    webSocket.Dispose();
    }
    }
    }
    }
    }

    ConsoleAppWebsocketServer

    using SuperWebSocket;
    using System;
    using System.Web;

    namespace ConsoleWebsocketServer.ServerEntities
    {
    /// <summary>
    /// SuperWebSocket服务端
    /// </summary>
    public class ConsoleAppWebsocketServer
    {
    public static WebSocketServer ws = null;

    public void Start()
    {
    ws = new WebSocketServer();
    ws.NewSessionConnected += Ws_NewSessionConnected;
    ws.NewMessageReceived += Ws_NewMessageReceived;
    ws.SessionClosed += Ws_SessionClosed;
    if (!ws.Setup("127.0.0.1", 1234))
    {
    Console.WriteLine("ChatWebSocket 设置WebSocket服务侦听地址失败");
    return;
    }

    if (!ws.Start())
    {
    Console.WriteLine("ChatWebSocket 启动WebSocket服务侦听失败");
    return;
    }
    while (true)
    {

    }
    }

    public void Ws_NewSessionConnected(WebSocketSession session)
    {
    Console.WriteLine("{0:HH:MM:ss} 与客户端:{1}创建新会话", DateTime.Now, GetSessionName(session));
    var msg = string.Format("{0:HH:MM:ss} {1} 进入聊天室", DateTime.Now, GetSessionName(session));

    SendToAll(session, msg);
    }

    private void Ws_NewMessageReceived(WebSocketSession session, string value)
    {
    var msg = string.Format("{0:HH:MM:ss} {1} 说: {2}", DateTime.Now, GetSessionName(session), value);
    Console.WriteLine($"{msg}");
    SendToAll(session, msg);
    }

    public void Ws_SessionClosed(WebSocketSession session, SuperSocket.SocketBase.CloseReason value)
    {
    Console.WriteLine("{0:HH:MM:ss} 与客户端:{1}的会话被关闭 原因:{2}", DateTime.Now, GetSessionName(session), value);
    var msg = string.Format("{0:HH:MM:ss} {1} 离开聊天室", DateTime.Now, GetSessionName(session));
    SendToAll(session, msg);
    }

    /// <summary>
    /// 启动服务
    /// </summary>
    /// <returns></returns>
    public void StartWebsocket()
    {
    if (!ws.Setup("127.0.0.1", 1234))
    {
    Console.WriteLine("ChatWebSocket 设置WebSocket服务侦听地址失败");
    return;
    }

    if (!ws.Start())
    {
    Console.WriteLine("ChatWebSocket 启动WebSocket服务侦听失败");
    return;
    }

    Console.WriteLine("ChatWebSocket 启动服务成功");
    }

    /// <summary>
    /// 停止侦听服务
    /// </summary>
    public void Stop()
    {

    if (ws != null)
    {
    ws.Stop();
    }
    }

    public string GetSessionName(WebSocketSession session)
    {
    return HttpUtility.UrlDecode(session.Path.TrimStart('/'));
    }

    public void SendToAll(WebSocketSession session, string msg)
    {
    foreach (var sendSession in session.AppServer.GetAllSessions())
    {
    sendSession.Send(msg);
    }
    }
    }
    }

    4、ConsoleWebsocketServer的Program.cs 代码如下

    using ConsoleWebsocketServer.ServerEntities;
    using SuperWebSocket;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Web;

    namespace ConsoleWebsocketServer
    {
    class Program
    {
    public static WebSocketServer ws = null;
    static void Main(string[] args)
    {
    #region 启动http服务接受websocket请求
    Console.WriteLine("启动http服务的WebSocket服务");

    HttpAndWebsocket httpAndWebsocket = new HttpAndWebsocket();
    httpAndWebsocket.Start("ws://127.0.0.1:1234");
    Console.WriteLine("ChatWebSocket 启动服务成功");
    Console.WriteLine("按 Enter 退出...");
    Console.ReadKey();
    #endregion

    #region 启动websocket服务接受websocket请求
    //Console.WriteLine("WebSocket服务");
    //ws = new WebSocketServer();
    //ws.NewSessionConnected += Ws_NewSessionConnected;
    //ws.NewMessageReceived += Ws_NewMessageReceived;
    //ws.SessionClosed += Ws_SessionClosed;
    //if (!ws.Setup("127.0.0.1", 1234))
    //{
    // Console.WriteLine("ChatWebSocket 设置WebSocket服务侦听地址失败");
    // return;
    //}

    //if (!ws.Start())
    //{
    // Console.WriteLine("ChatWebSocket 启动WebSocket服务侦听失败");
    // return;
    //}

    //Console.WriteLine("ChatWebSocket 启动服务成功");
    //Console.WriteLine("按 Enter 推出...");
    //Console.ReadKey();
    //ws.Stop();
    #endregion
    }

    public static void Ws_NewSessionConnected(WebSocketSession session)
    {
    Console.WriteLine("{0:HH:MM:ss} 与客户端:{1}创建新会话", DateTime.Now, GetSessionName(session));
    var msg = string.Format("{0:HH:MM:ss} {1} 进入聊天室", DateTime.Now, GetSessionName(session));

    SendToAll(session, msg);
    }

    private static void Ws_NewMessageReceived(WebSocketSession session, string value)
    {
    var msg = string.Format("{0:HH:MM:ss} {1} 说: {2}", DateTime.Now, GetSessionName(session), value);
    Console.WriteLine($"{msg}");
    SendToAll(session, msg);
    }

    public static void Ws_SessionClosed(WebSocketSession session, SuperSocket.SocketBase.CloseReason value)
    {
    Console.WriteLine("{0:HH:MM:ss} 与客户端:{1}的会话被关闭 原因:{2}", DateTime.Now, GetSessionName(session), value);
    var msg = string.Format("{0:HH:MM:ss} {1} 离开聊天室", DateTime.Now, GetSessionName(session));
    SendToAll(session, msg);
    }

    /// <summary>
    /// 启动服务
    /// </summary>
    /// <returns></returns>
    public static void Start()
    {
    if (!ws.Setup("127.0.0.1", 1234))
    {
    Console.WriteLine("ChatWebSocket 设置WebSocket服务侦听地址失败");
    return;
    }

    if (!ws.Start())
    {
    Console.WriteLine("ChatWebSocket 启动WebSocket服务侦听失败");
    return;
    }

    Console.WriteLine("ChatWebSocket 启动服务成功");

    }

    /// <summary>
    /// 停止侦听服务
    /// </summary>
    public static void Stop()
    {

    if (ws != null)
    {
    ws.Stop();
    }
    }

    public static string GetSessionName(WebSocketSession session)
    {
    return HttpUtility.UrlDecode(session.Path.TrimStart('/'));
    }

    public static void SendToAll(WebSocketSession session, string msg)
    {
    foreach (var sendSession in session.AppServer.GetAllSessions())
    {
    sendSession.Send(msg);
    }
    }
    }
    }

    Websocket客户端

    1、新建项目ConsoleWebsocketClient

    2、项目右键 管理Nuget程序包,,搜索  log4net,WebSocket4NET,添加即可

    3、新建文件夹ClientEntities,然后再该文件夹下添加ClientWebsocketEntity和ConsoleAppWebsocketClient这两个类,代码如下

    ClientWebsocketEntity

    using ConsoleWebsocketClient.CommonUntils;
    using System;
    using System.IO;
    using System.Net.WebSockets;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;

    namespace ConsoleWebsocketClient.ClientEntities
    {
    /// <summary>
    /// ClientWebsocket客户端实体类
    /// </summary>
    public class ClientWebsocketEntity
    {
    #region Fields And Propertities

    /// <summary>
    /// ClientWebsocket客户端Url
    /// </summary>
    public string SocketUrl { get; set; } = "ws://127.0.0.1:1234/";
    //public string SocketUrl { get; set; } = "ws://192.168.1.198:1234/";

    /// <summary>
    /// ClientWebsocket客户端对象
    /// </summary>
    public ClientWebSocket ClientWebSocket { get; set; }

    /// <summary>
    /// 消息回传的委托定义
    /// </summary>
    /// <param name="retValue"></param>
    public delegate void ShowMessage(string retValue);

    /// <summary>
    /// 消息回传
    /// </summary>
    public ShowMessage ReturnMessage;

    #endregion

    #region Methods
    /// <summary>
    /// 构造函数初始化
    /// </summary>
    public ClientWebsocketEntity()
    {
    ClientWebSocket = new ClientWebSocket();
    //ClientWebsocketConnect();
    }

    public void StartClient()
    {
    ClientWebsocketConnect();
    }

    /// <summary>
    /// ClientWebsocket客户端连接Websocket服务端
    /// </summary>
    public async void ClientWebsocketConnect()
    {
    try
    {
    await ClientWebSocket.ConnectAsync(new Uri(SocketUrl), CancellationToken.None);
    ClientWebsocketSendMessage();
    ClientWebsocketReceivedMessage();
    }
    catch (Exception)
    {

    throw;
    }
    }

    /// <summary>
    /// ClientWebsocket客户端向Websocket服务端发送消息
    /// </summary>
    public async void ClientWebsocketSendMessage()
    {
    try
    {
    while (true)
    {
    byte[] bytess = Encoding.Default.GetBytes($"login#22222");
    await ClientWebSocket.SendAsync(new ArraySegment<byte>(bytess), WebSocketMessageType.Text, true, new CancellationToken());
    await Task.Delay(1000 * 3);
    }
    //while (ClientWebSocket.State == WebSocketState.Open)
    //{
    // byte[] buffer = new byte[10240];
    // await ClientWebSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
    // await Task.Delay(TimeSpan.FromSeconds(3));
    //}
    }
    catch (Exception ex)
    {
    LogHelper.Instance.Error($"{ex.Message}");
    throw;
    }
    }

    /// <summary>
    /// ClientWebsocket客户端从Websocket服务端接收消息
    /// </summary>
    public async void ClientWebsocketReceivedMessage()
    {
    try
    {
    //while (true)
    //{
    // //byte[] bytess = Encoding.Default.GetBytes($"TestWebsocket发送数据 {s} ");
    // //cln.SendAsync(new ArraySegment<byte>(bytess), WebSocketMessageType.Text, true, new CancellationToken()).Wait();
    // //s++;
    // string returnValue = await GetAsyncValue(ClientWebSocket);//异步方法
    // ReturnMessage?.Invoke(returnValue);
    //}

    while (ClientWebSocket.State == WebSocketState.Open)
    {
    ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024 * 4]);
    WebSocketReceiveResult result = await ClientWebSocket.ReceiveAsync(buffer, CancellationToken.None);
    if (result.EndOfMessage)
    {
    if (result.MessageType == WebSocketMessageType.Text)
    {
    //string retValue = Encoding.UTF8.GetString(buffer.Array).Replace("", "").Trim(); // Encoding.UTF8.GetString(buffer.Array)会多出很多空余的字符,添加Replace("", "").Trim()处理掉就可以了
    string retValue = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
    ReturnMessage?.Invoke(retValue);
    }
    }
    }

    //WebSocketReceiveResult result;
    //ArraySegment<byte> buffer;
    //string retValue;
    //while (ClientWebSocket.State == WebSocketState.Open)
    //{
    // using (var ms = new MemoryStream())
    // {
    // buffer = new ArraySegment<byte>(new byte[1024]);
    // do
    // {
    // result = await ClientWebSocket.ReceiveAsync(buffer, CancellationToken.None);
    // ms.Write(buffer.Array, buffer.Offset, result.Count);
    // }
    // while (!result.EndOfMessage);
    // ms.Seek(0, SeekOrigin.Begin);
    // if (result.MessageType == WebSocketMessageType.Text)
    // {
    // using (var reader = new StreamReader(ms, Encoding.UTF8))
    // {
    // retValue = reader.ReadToEnd();
    // ReturnMessage?.Invoke(retValue);
    // }
    // }
    // }
    //}

    }
    catch (Exception ex) when (!string.IsNullOrEmpty(ex.Message))
    {
    LogHelper.Instance.Error($"{ex.Message}");
    throw;
    }
    }

    public static async Task<string> GetAsyncValue(ClientWebSocket clientWebSocket)
    {
    string returnValue = null;
    ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[8192]);
    WebSocketReceiveResult result = null;
    using (var ms = new MemoryStream())
    {
    do
    {
    result = await clientWebSocket.ReceiveAsync(buffer, CancellationToken.None);
    ms.Write(buffer.Array, buffer.Offset, result.Count);
    }
    while (!result.EndOfMessage);
    ms.Seek(0, SeekOrigin.Begin);
    if (result.MessageType == WebSocketMessageType.Text)
    {
    using (var reader = new StreamReader(ms, Encoding.UTF8))
    {
    returnValue = reader.ReadToEnd();
    //Console.WriteLine(returnValue);
    }
    }
    }
    return returnValue;
    }
    #endregion
    }
    }

    ConsoleAppWebsocketClient

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;

    namespace ConsoleWebsocketClient.ClientEntities
    {
    class ConsoleAppWebsocketClient
    {
    static WebSocket4Net.WebSocket webSocket4NetFaceValidate = null;
    static void Start(string[] args)
    {
    Console.WriteLine("WebSocket客户端");
    Thread.Sleep(TimeSpan.FromSeconds(8));
    webSocket4NetFaceValidate = new WebSocket4Net.WebSocket("ws://127.0.0.1:1234");
    webSocket4NetFaceValidate.Opened += WebSocket4NetFaceValidate_Opened;
    webSocket4NetFaceValidate.MessageReceived += WebSocket4NetFaceValidate_MessageReceived;
    webSocket4NetFaceValidate.Error += WebSocket4NetFaceValidate_Error;
    webSocket4NetFaceValidate.Open();
    //WebSocketSendmessage();
    //Thread thread = new Thread(WebSocketSendmessage);
    //thread.IsBackground = true;
    //thread.Start();
    Console.ReadKey();
    }

    private static void WebSocket4NetFaceValidate_Error(object sender, SuperSocket.ClientEngine.ErrorEventArgs e)
    {
    throw new NotImplementedException();
    }

    public static void WebSocketSendmessage()
    {
    int s = 88;
    while (true)
    {
    webSocket4NetFaceValidate.Send(s.ToString());
    s++;
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(3));

    }
    Console.WriteLine($"begin#123456");
    byte[] bytess = Encoding.Default.GetBytes($"begin#123456");
    IList<ArraySegment<byte>> list = new List<ArraySegment<byte>>();
    list.Add(new ArraySegment<byte>(bytess));
    webSocket4NetFaceValidate.Send(list);
    }

    private static void WebSocket4NetFaceValidate_Opened(object sender, EventArgs e)
    {
    Console.WriteLine($"begin#123456");
    byte[] bytess = Encoding.Default.GetBytes($"begin#123456");
    IList<ArraySegment<byte>> list = new List<ArraySegment<byte>>();
    list.Add(new ArraySegment<byte>(bytess));
    webSocket4NetFaceValidate.Send(list);
    }

    private static void WebSocket4NetFaceValidate_MessageReceived(object sender, WebSocket4Net.MessageReceivedEventArgs e)
    {
    try
    {
    string returnMessage = e.Message;
    if (string.IsNullOrEmpty(returnMessage))
    {
    return;
    }
    Console.WriteLine(returnMessage);
    }
    catch (Exception ex)
    {

    }
    finally
    {

    }
    }
    }
    }

    4、ConsoleWebsocketClient的Program.cs 代码如下

    using ConsoleWebsocketClient.ClientEntities;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;

    namespace ConsoleWebsocketClient
    {
    class Program
    {
    static WebSocket4Net.WebSocket webSocket4Net = null;
    static void Main(string[] args)
    {
    #region ClientWebsocke客户端
    //Thread.Sleep(TimeSpan.FromSeconds(3));
    Console.WriteLine("WebSocket客户端");
    for (int i = 0; i < 1; i++)
    {
    //System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
    //System.Threading.Tasks.Task.Factory.StartNew(BBB, i);
    //Task.Delay(2000);

    ClientWebsocketEntity clientWebsocketEntity = new ClientWebsocketEntity();
    clientWebsocketEntity.ReturnMessage = Addlog;
    clientWebsocketEntity.StartClient();
    }
    Console.ReadKey();
    #endregion

    #region WebSocket4Net客户端
    //Console.WriteLine("WebSocket客户端");
    //webSocket4Net = new WebSocket4Net.WebSocket("ws://127.0.0.1:1234");
    //webSocket4Net.Opened += WebSocket4Net_Opened;
    //webSocket4Net.MessageReceived += WebSocket4Net_MessageReceived;
    //webSocket4Net.Error += WebSocket4Net_Error;
    //webSocket4Net.Open();
    ////WebSocketSendmessage();
    //Thread thread = new Thread(WebSocketSendmessage);
    //thread.IsBackground = true;
    //thread.Start();
    //Console.ReadKey();
    #endregion


    }

    public static void Addlog(string sss)
    {
    Console.WriteLine(sss);
    }

    public static void WebSocketSendmessage()
    {
    int s = 88;
    while (true)
    {
    webSocket4Net.Send(s.ToString());
    s++;
    Thread.Sleep(TimeSpan.FromSeconds(3));

    }
    }

    private static void WebSocket4Net_Error(object sender, SuperSocket.ClientEngine.ErrorEventArgs e)
    {
    //throw new NotImplementedException();
    }

    private static void WebSocket4Net_Opened(object sender, EventArgs e)
    {
    Console.WriteLine($"begin#123456");
    byte[] bytess = Encoding.Default.GetBytes($"begin#123456");
    IList<ArraySegment<byte>> list = new List<ArraySegment<byte>>();
    list.Add(new ArraySegment<byte>(bytess));
    webSocket4Net.Send(list);
    }

    private static void WebSocket4Net_MessageReceived(object sender, WebSocket4Net.MessageReceivedEventArgs e)
    {
    try
    {
    string returnMessage = e.Message;
    if (string.IsNullOrEmpty(returnMessage))
    {
    return;
    }
    Console.WriteLine(returnMessage);
    }
    catch (Exception ex)
    {

    }
    finally
    {

    }
    }
    }
    }

    5、ConsoleWebsocketClient的App.config 代码如下

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
    </configSections>
    <log4net>
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
    <param name="File" value="LogLog.txt" />
    <param name="AppendToFile" value="true" />
    <param name="MaxSizeRollBackups" value="100" />
    <param name="MaximumFileSize" value="2MB" />
    <param name="RollingStyle" value="Size" />
    <param name="StaticLogFileName" value="true" />
    <layout type="log4net.Layout.PatternLayout">
    <param name="ConversionPattern" value="%-15p %d [%c] %m %n" />
    </layout>
    </appender>
    <root>
    <level value="all" />
    <appender-ref ref="RollingLogFileAppender" />
    </root>
    </log4net>
    <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
    </configuration>

    5、为了测试方便添加了HTML文件websockettest.html

    <!DOCTYPE HTML>
    <html>
    <head>
    <meta http-equiv="content-type" content="text/html" />
    <meta name="author" content="https://www.baidu.com" />
    <title>websocket test</title>
    <script>
    var socket;
    function Connect(){
    try{
    socket=new WebSocket('ws://127.0.0.1:1234');
    }catch(e){
    alert('error');
    return;
    }
    socket.onopen = sOpen;
    socket.onerror = sError;
    socket.onmessage= sMessage;
    socket.onclose= sClose;
    }
    function sOpen(){
    alert('connect success!');
    }
    function sError(e){
    alert("error " + e);
    }
    function sMessage(msg){
    document.getElementById("msgrecv").value = msg.data;

    }
    function sClose(e){
    alert("connect closed:" + e.code);
    }
    function Send(){
    socket.send(document.getElementById("msg").value);
    }
    function Close(){
    socket.close();
    }
    </script>
    </head>

    <body>
    <input id="msg" type="text" size = "200" >
    <input id="msgrecv" type="text" size = "200">
    <button id="connect" onclick="Connect();">Connect</button>
    <button id="send" onclick="Send();">Send</button>
    <button id="close" onclick="Close();">Close</button>

    </body>

    </html>

    以上ConsoleWebsocketServer项目和ConsoleWebsocketClient项目都建好了,就可以运行了,其中可以设置两种Websocket服务端和Websocket客户端(websockettest.html网页端,也是客户端)

     服务端

    客户端

     运行结果

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    只需这10步,通过历史控制文件恢复数据库
    直播丨Oracle 12.2系列安装
    Python爬虫入门教程 70-100 爬虫原理应用到多种场景,Python下载B站视频
    windows python2.7 安装pyqt5
    Activiti任务参数的设置方式和作用域
    python 多线程2
  • 原文地址:https://www.cnblogs.com/1175429393wljblog/p/11284231.html
Copyright © 2011-2022 走看看