在PowerShell中,可以通过HttpListener 实现一个简单的Web服务器。但在实践当中,这个服务是串行,每次只能处理一个连接,性能不好,只能做一些简单的用途。如果要同时服务多个连接请求,需要在PowerShell使用多线程来进行处理。下面是具体的实践:
#定义一个哈希表全局变量,用来保存WebServer的全局信息
$PSWeb = [Hashtable]::Synchronized(@{})
#定义用于线程的脚本块,该脚本用于处理具体的服务请求
#传入两个参数,一个是request请求对象,一个是Respone对象
$PSWeb.ScriptBlock = {
Param([System.Net.HttpListenerRequest]$req,System.Net.HttpListenerResponse]$rsp)
#在代码块里不能使用代码块以外的地方自定义的函数。但是可以在代码块里自定义定义函数哦
#这个函数用于网客户端返回JASON信息
function Ret_response([System.Net.HttpListenerResponse]$resp,$ret,$msg){
$retstr = '{"ret":' + $ret + ',"msg":"' + $msg + '"}'
$retbytes = [System.Text.Encoding]::UTF8.GetBytes($retstr)
$resp.StatusCode = "200"
$resp.ContentType = 'application/json'
$resp.ContentLength64 = $retbytes.Length
}
#直接调用自定义的函数,向客户端返回信息
Ret_response $rsp 0 "海内存知己,天涯若比邻"
#关闭连接
$rsp.Close()
#返回
return
}
#定义一个运行线程用的Runspace池,这个池可以限制同时运行的处理请求的线程数量,本例设置为5
$PSWeb.RunspacePool = [Runspacefactory]::CreateRunspacePool(1,5)
#设置监听的IP地址和端口号
$url = "http://*:8888/"
Try {
#建立HttpListener对象,设置服务目录并开始监听
$PSWeb.listener = New-Object System.Net.HttpListener
$PSWeb.listener.Prefixes.Add($url)
$PSWeb.listener.Start()
#打开RunSpacePool
$PSWeb.RunspacePool.Open()
Write-Host "服务启动: $url..."
#循环处理请求
while ($PSWeb.listener.IsListening){
#阻塞读取服务请求
$content = $PSWeb.listener.GetContext()
#读到服务请求后,建立相应的线程并异步运行
$ps = [PowerShell]::Create().AddScript($PSWeb.ScriptBlock)
#设置线程的Runspacepool
$ps.RunspacePool = $PSWeb.RunspacePool
#按顺序传入参数
[void]$ps.AddArgument($content.Request)
[void]$ps.AddArgument($content.Response)
#异步运行线程
[void]$ps.BeginInvoke()
}
}
catch
{
#异常处理
Write-Error "有错误发生:"
$_.Exception.Message
}
finally
{
#释放资源
$PSWeb.listener.Stop()
$PSWeb.RunspacePool.Close()
}
具体的服务可以在代码块里实现。
这段代码可以在powershell里实现多线程的Web Server服务,能正常运行。当然还有不小的提升空间,比如说可以在循环里取些线程的返回值,做一些线程的清理操作之类的工作。
#定义一个数组,保存运行中的线程信息
$PSWeb.Jobs = New-Object System.Collections.ArrayList
#然后在循环里改成如下代码:
while ($PSWeb.listener.IsListening){
$content = $PSWeb.listener.GetContext()
$ps = [PowerShell]::Create().AddScript($PSWeb.ScriptBlock)
$ps.RunspacePool = $PSWeb.RunspacePool
[void]$ps.AddArgument($content.Request)
[void]$ps.AddArgument($content.Response)
[void]$ps.BeginInvoke()
#往Jobs数据里加入新建的线程信息
[void]$PSWeb.Jobs.Add((New-Object PSObject -Property @{
Ps = $ps
Result = $ps.BeginInvoke()
}))
#定义一个数组,保存已经完成运行的线程
$CompletedJobs = @()
#遍历Jobs数组,查找已完成运行的线程,处理返回结果
ForEach ( $Job in $PSWeb.Jobs) {
if ($Job.Result.IsCompleted)
{
#已运行完成的线程,处理器返回结果
Write-Host $Job.Ps.EndInvoke($Job.Result)
#释放线程资源
$Job.Ps.Dispose()
#将已完成的线程加入到临时的数组里,以便可以从Jobs数组里删除
$CompletedJobs += $Job
}
}
#从Jobs列表中删除已完成运行的线程
foreach ($Job in $CompletedJobs) {
$PSWeb.Jobs.Remove($Job)
}
}