热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Session深度探索

什么是Session?web是无状态,这意味着每次页面被回传到服务器时,都重新生成一个web页面类的一个新的实例。众所周知http时无状态的协议。它不能获得客户端的信息。如果用户录

什么是Session?

web是无状态,这意味着每次页面被回传到服务器时,都重新生成一个web页面类的一个新的实例。
众所周知http时无状态的协议。它不能获得客户端的信息。如果用户录入了一些信息,当跳转到下一个页面时,数据丢失,再也不能获得那些数据。我们需要保存这些数据,Session提供了一种把这些信息保存在服务器内存中的一种方式。它能存储各种数据类型包括自定义对象。每个客户端的Session是独立存储。

bubuko.com,布布扣


用session做状态管理是asp.net最好的特性之一。因为它是安全的,对用户透明,能保存各种数据类型。但是对于高负载网站,session能导致一些性能问题。因为session保存在服务器内存中客户端再从服务器读取。让我们看看再web应用程序使用session的优点和缺点。

Session的优点和缺点是什么?

下面是session基本的优点和缺点,稍后我将详细描述。

优点 :  

    * 能帮助我们在整个应用程序中维护用户状态.
    * 易于实现,能存储各种数据类型. 
    * 每个客户端独立存储. 
    * Session是安全的,对用户透明.

缺点 :  

    * 在大量用户情况下,系统开销太大。因为session保存在服务器内存中。
    * 序列化和反序列化session中的数据开销大。因为在StateServer,SQLServer模式时,在保存前必须把对象序列化。

Session数据的保存和读取

在session中保存和读取数据的方式和ViewState相似。我们使用System.Web.SessionState.HttpSessionState类交互Session状态。

在Session中保存数据

  1. Session["UserName"] = txtUser.Text;  

从Session中读取数据

  1. //检查session变量是否为空  
  2. if (Session["UserName"] != null)  
  3. {  
  4.     //从session返回username  
  5.    lblWelcome.Text = "Welcome : " + Session["UserName"];  
  6. }  
  7. else  
  8. {  
  9.    //做别的事情  
  10. }  

我们session保存对象。下面显示怎么在session中保存DataSet对象

  1. //在session中保存dataset  
  2. Session["DataSet"] = _objDataSet;    

下面显示怎么从session读取那个dataset。

  1. //检查session变量是否为空  
  2. if (Session["DataSet"] != null)  
  3. {             
  4.      DataSet _MyDs = (DataSet)Session["DataSet"];  
  5. }  
  6. else  
  7. {  
  8.      //做别的事情   
  9. }  

参考或更多的信息: Read Session Variable Section

Session ID

Asp.net使用120位标志符来跟踪每个session,这足够安全不能逆向工程。当客户端和服务器通讯时,只有SessionID被传输。当客户端请求数据时,asp.net根据SessionID返回相应的数据。按照下列步骤进行:

    * 客户端请求web站点,一些信息被存储在session中。
    * 服务器为客户端生成一个唯一的Session id,存储数据在Session状态提供者中。
    * 客户端带着这个SessionID向服务器请求数据。
    * 服务器在Session提供者中找到Session ID.找出被序列化存储在服务器中的数据,进行类型转化。

bubuko.com,布布扣


Session模式和状态提供者

在ASP.NET有下列Session模式

    *      InProc
    *      StateServer
    *      SQLServer
    *      Custom 

对于每个session模式,都有一个session提供者. 下列图将为你展示他们之间的关系。

bubuko.com,布布扣


下面的表展示session模式和对应的提供者名字.

Session State Mode State Provider
InProc In-Memory Object
StateServer Aspnet_state.exe
SQLServer DataBase
Custom CustomProvider


除了上边的,还有一种模式:“OFF”.如果你选择这个设置,Session将对应用程序无效。因为我们想使用session,所以我们主要关注那四种模式。

 

参考或更多的信息: Session State Providers 

Session状态:

Session状态意味着我们需要为应用程序作些设置。要么在web.config中设置,要么在页面代码中设置。在web.config中SessionState节点是用来做配置的,如:session模式,超时,状态连接串,自定义提供者等。在讨论session模式前,先概述一下session事件。

Session 事件

在asp.net有两个事件: 

    * Session_Start 
    * Session_End 

你可以在web应用程序的global.asax文件中操作这两个事件。当一个新的session初始化时,触发session_start事件。当一个session过期时触发Session_End事件。

  1. void Session_Start(object sender, EventArgs e)  
  2. {  
  3.         // 当新的session产生时,这段代码运行  
  4.   
  5. }  
  6.   
  7. void Session_End(object sender, EventArgs e)  
  8. {  
  9.         // 当新的session结束时,这段代码运行.  
  10.    
  11. }  


参考或更多的信息: Application and Session Events

Session模式

我已经讨论了ASP.NET中的session模式,下面是几种不同的模式:

    * Off 
    * InProc 
    * StateServer 
    * SQLServer 
    * Custom 

如果你在web.config设置了Session Mode="off".session将在这个应用程序中无效,你可以想下面的方式设置:

  1. <system.web>         
  2.     
  3.   <sessionState  mode="Off">sessionState>  
  4. system.web>  

InPorc Session 模式

这是Asp.Net的缺省模式. 它存储session信息在当前的应用程序域中. 这是性能最好的模式. 最大的缺点是当应用程序重启时,session数据丢失。关于InPorc模式session优缺点后面再讨论。

概述:

由于存储session信息在当前的应用程序域中. 所以它易于操作,快速有效.

bubuko.com,布布扣

InProc session存储数据在应用程序域的内存对象中,被应用程序池的工作进程操作。 所以当我们重启服务器时丢失Session数据。如果客户端请求这个数据,状态提供者从内存对象中读取,并返回客户端。在web.config文件中我们可以设置Session模式,超时等:

  1. <system.web>         
  2.     
  3.   <sessionState  mode ="InProc" timeout ="30">sessionState>  
  4. system.web>  


这个Session超时被设置为30分钟保持激活。也能通过代码中设置。

  1. Session.TimeOut=30;   

在asp.net中,有两类Session事件:Session_Start() 和 Session_End;InProc session模式时唯一支持Session_End()的模式。这个事件将在Session超时期结束后被调用。一般session状态流动象下面这样:
 

Session_End()被调用依赖于Session超时,这是一个非常快速的机制,因为返回或存储数据不用序列化,数据一直存储在相同的应用程序域中

什么时候我们该使用InProc Session模式?

InProc 是缺省的模式,对小型的,用户数量比较少的网站非常有用。在web园场景中避免使用。

优缺点


有点 :

    *     Session数据存储在当前应用程序域的内存对象中,所以访问快速。 
    *     对数据不需要序列化,和反序列化.
    *     实现简单,如同View State.

缺点 :  

    *      如果工作进程或应用程序域回收时,所有session数据丢失。
    *      虽然他是最快速的,但当Session数据比较多,或用户比较多时比较影响性能。因为它使用的时内存。
    *      在web园场景中不能使用.
    *      也不适合web农场场景.

StateServer Session 模式

概述

这种也被称为进程外模式 StateServer使用一个独立的Windows Services。它不依赖IIS 也能运行于一个独立的服务器. 这种Session模式总的来说被aspnet_state.exe管理。StateServer可以和web应用程序使用同一个服务器,但是它在web应用程序域的外边。这就容许你即使重启了asp.net进程但session数据仍然是活动的。

 bubuko.com,布布扣


配置

在StateServer模式下,session数据被存在在独立的服务器,它依赖iis,被aspnet_state.exe操作。aspnet_state.exe进程是一个windows Services。你可以在windows MMC 或 命令提示行下启动。

bubuko.com,布布扣


缺省情况下ASP.NET state service启动模式是手动, 我们必须设置为自动启动模式.

bubuko.com,布布扣

在命令行执行 "net start aspnet_state"启动. 缺省端口42424 , 可以通过注册表修改这个端口号,如下图:

 bubuko.com,布布扣


现在让我们看看web.config关于StateServer的配置。stateConnectionString指定运行的的服务器。缺省ip:127.0.0.1 (localhost) 端口 42424。

  1. <system.web>         
  2.     
  3.   <sessionState  mode ="StateServer"   
  4.                  stateConnectionString ="tcpip=127.0.0.1:42424" /> 
  5.   system.web>  

当我们使用StateServer时,可以配置stateNetworkTimeOut,用来指定在撤销请求前,可以等待StateServer响应的秒数。缺省10秒。

  1. <system.web>         
  2.     
  3.   <sessionState  mode ="StateServer"   
  4.                  stateConnectionString ="tcpip=127.0.0.1:42424"   
  5.                  stateNetworkTimeout ="40">          
  6.   sessionState>  
  7. system.web>  


StateServer Session模式是怎么工作的?  

使用StateServer Session模式,可以避免当web服务器重启时不必要的数据丢失。StateServer被aspnet_state.exe(windows Services)维护。该进程维护所有session数据。但存储数据前需要序列化。

bubuko.com,布布扣

如上图所示,客户端向服务器发出请求,服务器存储session数据到状态服务器。StateServer可以是当前系统,也可以是不同系统。但它依赖iis.状态服务器的位置在web.config的stateConnectionString属性设置。如果我们设置为127.0.0.1:42424,它将在本地系统中存储数据。可以通过改变ip来改变StateServer的位置,要确保aspnet_state.exe在那个服务器运行,否则在存储数据到session时将得到下面的异常。

bubuko.com,布布扣

当存储数据到session时,数据需要被序列化,使用State Provider把数据存储在StateServer 。获取数据时,State provider返回数据。下图给出这个过程:

bubuko.com,布布扣

例子

步骤 1 :  打开 Visual Studio > 文件 > 新建 > 网站 . 选择位置"http",新建一个web application .

 bubuko.com,布布扣


如果打开IIS你会看到以web application名称命名的虚拟目录被创建。

 bubuko.com,布布扣


步骤2 :  新建一个简单的UI. 我们将存储数据在session中。为此我们新建一个类:"StudentInfo"  :

  1. [Serializable]  
  2. public class StudentInfo  
  3. {  
  4.     //Default Constructor  
  5.     public StudentInfo()  
  6.     {  
  7.         
  8.     }  
  9.     ///   
  10.     /// Create object of student Class  
  11.     ///   
  12.     /// Int RollNumber  
  13.     /// String Name  
  14.     public StudentInfo(int intRoll, string strName)  
  15.     {  
  16.         this.Roll = intRoll;  
  17.         this.Name = strName;  
  18.     }  
  19.   
  20.     private int intRoll;  
  21.     private string strName;  
  22.     public int Roll  
  23.     {  
  24.         get  
  25.         {  
  26.             return intRoll;  
  27.         }  
  28.         set  
  29.         {  
  30.             intRoll = value;  
  31.         }  
  32.     }  
  33.   
  34.     public string Name  
  35.     {  
  36.         get  
  37.         {  
  38.             return strName;  
  39.         }  
  40.         set  
  41.         {  
  42.             strName = value;  
  43.         }  
  44.     }  
  45. }  


现在看看后台代码,我们需要两个按钮,一个保存session数据,一个返回session数据。

  1. protected void btnSubmit_Click(object sender, EventArgs e)  
  2. {  
  3.     
  4.     StudentInfo _objStudentInfo = new StudentInfo(Int32.Parse( txtRoll.Text) ,txtUserName.Text);  
  5.     Session["objStudentInfo"] = _objStudentInfo;  
  6.     ResetField();  
  7. }  
  8. protected void btnRestore_Click(object sender, EventArgs e)  
  9. {  
  10.     StudentInfo _objStudentInfo = (StudentInfo) Session["objStudentInfo"];  
  11.     txtRoll.Text = _objStudentInfo.Roll.ToString();  
  12.     txtUserName.Text = _objStudentInfo.Name;  
  13.      
  14. }  


步骤 3 : 配置state server.确保aspnet_state.exe在指定的服务器上被启动.

步骤 4 : 运行程序

bubuko.com,布布扣

输入数据,提交。

测试

现在我们做下面的测试,以打消你的关于StateServer的疑惑。

首先 :删除studentinfo类前面的[ Serializable ]关键字。如果提交会出现下面错误。它说明存在数据前,对象需要被序列化的。

bubuko.com,布布扣

接着: 运行应用程序,点击提交按钮,保存数据。重启iis.如果在InProc下,session数据已经丢失。但在StateServer模式下,点击Restore按钮,你仍然可以得到原始session数据。因为State server存储数据不依赖于当前的iis,它独立保存。

bubuko.com,布布扣

最后 : 在Windows Services MMC中,停止aspnet_state.exe, 提交数据. 你将得到下面错误:

bubuko.com,布布扣

因为State Server进程没有运行。

优缺点

优点 : 

    *      保存数据独立于iis, 任何iis的问题不会影响Session数据.
    *      在负载均衡,web农场和web园场景,它很有用. 

缺点 :

    *      处理慢,由于序列化和反序列化。
    *      State Server需要一直运行。

参考更多的信息:

    *  State Server Session Mode 
    * Asp.Net Session State

SQL Server Session 模式

概述

这种sesion模式更安全,更可靠。在这种模式下session数据被序列化存储在SQL Server数据库中。主要缺点还是跟数据的序列化和反序列化的开销有关。这是web农场场景里最好的模式

什么时候该用SQL Server Session模式? 

    * 这种模式更安全,更可靠。
    * 它集中保存数据于数据库.
    * 如果发生连续重启服务器,应该使用这种模式。
    * 在web园,web农场中,最完美的模式。
    * 当在不用的应用程序之间需要共享session数据,可以使用这种模式

配置SQL Server Session 模式:

在这种模式, 我们存储session数据在SQL Server中, 我们首先需要在web.config提供一个database连接串。可以在web.config中sqlConnectionString 属性提供这个连接串。
然后安装,配置SQL Server.我解释怎么使用aspnet_regsql命令配置SQL server。


步骤 1: 在命令行,定位到Framework 版本的文件目录

例如 :c:\windows\microsoft.net\framework\.

步骤 2 : 使用下列参数运行aspnet_regsql命令:

bubuko.com,布布扣
参数 描述
-ssadd 添加SQLServer session 模式支持
-sstype p P代表持久,把session数据持久化到SQL Server
-S 指定服务器名
-U 指定用户名
-P 指定密码


运行后,将得到下列信息:

bubuko.com,布布扣


步骤 3 : 打开 SQL Server Management Studio,一个新的数据库ASPState被创建,里边包含两张表。  
    * ASPStateTempApplications 
    * ASPStateTempSessions

bubuko.com,布布扣

现在我们只需要改变State Server例子中的web.config连接串。运行应用程序。

存储 Roll 和 User Name, 点击提交按钮。 and 在SQL Server Management Studio,打开ASPStateTempSessions表会看到我们的session数据。

bubuko.com,布布扣

测试:
我们做下列测试,同State Server模式一样.

   1. 删除StydentInfo类上的Serialize关键字
   2. 重启IIS,点击Restore
   3. 停止SQL Server服务。 

优缺点: 

优点 :

    * 重启IIS,session数据不受影响.
    * 最可靠,最安全的session模式.
    * 集中保存数据,易于其它应用程序访问。
    * 在web农场,web园模式中很有用。

缺点 : 

    * 处理很慢
    * 对象的序列化,和反序列化开销大
    * 由于session数据被不同的服务器操作,所以要小心对待SQL Server, 它要一直运行

参考更多的信息: Read SQL Server Mode

自定义Session模式 

 概述: 
一般我们都使用InProc, StateServer 或 SQL Server模式,但我们也需要知道自定义session模式基本原理。这种模式很有趣,因为它让我们完全控制每件事甚至session ID.你可以自己写产生session ID的算法。

可以通过继承SessionStateStoreProviderBase类实现自定义Provide(保存session数据的机制)。通过实现ISessionIDManager产生新session ID.

自定义Session实现期间,下列方法被调用:
bubuko.com,布布扣
在初始化方法中,设置自定义提供者。它将用指定的提供者初始化连接。SetItemExpireCallback用来设置SessionTimeOut,我们能注册任何方法,以在session过期时被调用。InitializeRequest在每次请求时被调用。CreateNewStoreData用来产生一个新的SessionStateStoreData实例。

什么时候使用自定义Session模式?

    *      不想在SQL Server存储session数据.
    *      想用一些存在的表存储session数据.
    *      需要产生自己的session ID. 

怎么配置?  

配置web.config如下: 

  1. <system.web>   
  2.   <sessionState  mode ="Custom" customProvider ="AccessProvider">  
  3.     <providers >  
  4.       <add name ="AccessProvider" type ="CustomDataType"/>  
  5.     providers>  
  6.   sessionState>  
  7. system.web>  


优缺点:

优点 :

    *      我们能在已经存在的表中存储session数据。当我们不想用SQL Server数据库,想使用老数据库时,特别有用。
    *      不依赖IIS.重启web服务器,不会影响session数据。
    *      自定义产生Session ID的算法. 

缺点:

    *      处理很慢.
    *      产生自定义state provider,是个很底层的任务,必须小心操作,确保安全。

建议使用第三方的Provide,而不是自定义。

参考更多的信息:Custom Mode  

如果你想知道更多关于session模式的信息,请读这篇MSDN文章

Session 和 COOKIEs 的关系

客户端需要COOKIEs配合session工作。因为客户端需要使用一个适当的session id代表每个请求。

我们可以按照下面操作它:

使用COOKIEs:当session被使用时,ASP.NET自动生成一个叫ASP.NET_SessionId的特殊COOKIEs.这是缺省的,Session ID通过这个COOKIEs传递。

不使用COOKIEs:一些老的浏览器不支持COOKIEs,或者用户在浏览器中禁止COOKIEs.这是ASP.NET通过特殊标记的URL传输Session ID.

无COOKIEs Session怎么工作?  

当用户请求一个页面时,服务器编码session ID,把它加到每一页面链接上。当用户点击链接,ASP.NET解码session ID,传送到用户请求的页面。这样在请求页面就可以得到session数据了。如果ASP.NET检测到用户的浏览器不支持COOKIEs,这一切就自动发生了。

怎么实现无COOKIEs Session?

  1. <system.web>   
  2.   <sessionState  COOKIEless ="true">sessionState>  
  3. system.web>  

从Session变量中删除Session

方法 描述
Session.Remove(strSessionName); 在session集合中删除某项
Session.RemoveAll() 删除session集合中所有项
Session.Clear() 删除session集合中所有项,注意Clear和RemoveAll没有什么不同,内部是RemoveAll()调用Clear()
Session.Abandon() 销毁当前的session

启动或禁用Session


为了性能优化,我们可以启用或禁用session.因为每页读些session都有很大的开销。所以最好的方式是根据需要禁止或启用session。而不是一直启动session.禁止或启用session有两种方式

    * 页面级 
    * 应用程序级  

页面级:
我们能在Page指令中使用EnableSessionState属性,在页面级禁止session.

  1. <%@ Page Language="C#"  EnableSessiOnState="False"  

这将在特定的页面禁止session活动。
同样我们可以设置session只读,这将容许访问数据,不许写session数据。

  1. <%@ Page Language="C#"  EnableSessiOnState="ReadOnly"  


应用程序级 : 

在web.config中使用EnableSessionState 属性禁止整个web应用程序的sesson.

  1. <system.web>  
  2.     
  3.   <pages enableSessionState ="false"/>  
  4. system.web>  

通常我们使用页面级别。因为某些页面不需要session,或只读session.

参考更多的信息 : How To Disable ASP.Net Session State in ASP.NET

Session深度探索,布布扣,bubuko.com

Session深度探索


推荐阅读
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
author-avatar
小石子Sandra
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有