使用WCF实现消息推送
使用WCF实现消息推送
1.协议
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WCFHub.IService { [ServiceContract(CallbackContract=typeof(IEventCallback))] public interface IEventService { [OperationContract(IsOneWay = true)] void Subscribe(SubscribeArg a); [OperationContract(IsOneWay = true)] void Unsubscribe(ArgumentBase a); [OperationContract] DateTime Ping(); } public interface IEventCallback { [OperationContract(IsOneWay = true)] void OnMessageReceived(ArgumentBase a); } }
2.实现
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections.Concurrent; using System.ServiceModel; using System.ServiceModel.Channels; namespace WCFHub.IService { public class EventServiceImpl:IEventService { public static readonly ConcurrentDictionary _Subscribers = new ConcurrentDictionary(); public string ClientIpAndPort() { OperationContext context = OperationContext.Current; MessageProperties properties = context.IncomingMessageProperties; RemoteEndpointMessageProperty endpoint = properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty; return endpoint.Address + ":" + endpoint.Port.ToString(); } public void Subscribe(SubscribeArg a) { Console.WriteLine(ClientIpAndPort()); var callback = OperationContext.Current.GetCallbackChannel(); a.Username=a.Username.ToLower(); _Subscribers[a.Username]=new SubscribeContext(){Arg=a,Callback=callback}; #region 事件处理 ICommunicationObject obj = (ICommunicationObject)callback; obj.Closed += (s,e) => { Console.WriteLine("Closed"); }; obj.Faulted += (s, e) => { Console.WriteLine("Faulted"); }; obj.Closing += (s,e) => { Console.WriteLine("Closeing" + OperationContext.Current); var callback2=(IEventCallback)s; _Subscribers.ToList().ForEach(ent => { if (ent.Value.Callback == callback2) { RemoveSubscriber(ent.Value.Arg.Username); } }); }; #endregion } public void Unsubscribe(ArgumentBase a) { RemoveSubscriber(a.Model); } private static void RemoveSubscriber(string username) { username = username.ToLower(); if (_Subscribers.ContainsKey(username)) { SubscribeContext outObj = null; _Subscribers.TryRemove(username, out outObj); } } public static void PostData(ArgumentBase a) { Console.WriteLine("收到待发消息:" + a.Model); _Subscribers.ToList().ForEach(subscriber => { ICommunicationObject callback = (ICommunicationObject)subscriber.Value.Callback; if (((ICommunicationObject)callback).State == CommunicationState.Opened) { try { //此处需要加上权限判断、订阅判断等 subscriber.Value.Callback.OnMessageReceived(a); } catch (Exception ex) { RemoveSubscriber(subscriber.Value.Arg.Username); Console.WriteLine("PostData:" + ex.Message); } } else { RemoveSubscriber(subscriber.Value.Arg.Username); Console.WriteLine("PostData,用户链接已经关闭"); } }); } #region IEventService 成员 public DateTime Ping() { Console.WriteLine("Ping:" + ClientIpAndPort() +"," +DateTime.Now); return DateTime.Now; } #endregion } public class SubscribeContext { public SubscribeArg Arg { get; set; } public IEventCallback Callback { get; set; } } }
3.实体类
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WCFHub.IService { [Serializable] public class ArgumentBase { private int code; private string msg; private T model; public int Code { get { return code; } set { code = value; } } public string Msg { get { return msg; } set { msg = value; } } public T Model { get { return model; } set { model = value; } } } public class SubscribeArg : ArgumentBase { public String Username { get; set; } public List Alarms { get; set; } public SubscribeArg() { Alarms = new List(); } } }
4.服务托管
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.ServiceModel; using WCFHub.IService; namespace WCFHub.Win { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private ServiceHost _Host = new ServiceHost(typeof(EventServiceImpl)); private void Form1_Load(object sender, EventArgs e) { _Host.AddServiceEndpoint(typeof(IEventService), new NetTcpBinding(SecurityMode.None), "net.tcp://192.168.30.30:9999/EventService" ); _Host.Open(); Console.WriteLine("服务开启..."); } protected override void OnClosed(EventArgs e) { _Host.Close(); base.OnClosed(e); Console.WriteLine("服务关闭!"); } private void button1_Click(object sender, EventArgs e) { var data = new ArgumentBase() { Model = textBox1.Text + "," + DateTime.Now.ToString() }; EventServiceImpl.PostData(data); } } }
5.客户端
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using WCFHub.IService; using System.ServiceModel; using System.Threading; namespace WCFHub.WinClient { #region MessageReceive public delegate void MessageReceivedHandle(ArgumentBase a); public class NotifyManager : IEventCallback { public event MessageReceivedHandle MessageReceived; public static int C_MaxErrCount = 5; public static int C_HeartbeatInterval = 1000 * 10;//10秒一次心跳检测 IEventService _Proxy = null; private int ErrCounter = 0; public bool Enabled { get; set; } public NotifyManager() { Enabled = false; } private void Close() { if (_Proxy != null) { try { var comObj = _Proxy as ICommunicationObject; comObj.Abort(); } catch { } } } public void Start() { Enabled = true; StartInternal(); #region 心跳检测 var timer = new System.Timers.Timer(); timer.Enabled = false; timer.Interval = C_HeartbeatInterval; timer.Elapsed += (s, ie) => { try { WriteLine("心跳检测..."); timer.Enabled = false; _Proxy.Ping(); ErrCounter = 0; } catch (Exception ex) { WriteLine(ex.Message); ErrCounter++; if (ErrCounter >= C_MaxErrCount) { Close(); StartInternal(); } } finally { timer.Enabled = true; } }; timer.Start(); #endregion } private void StartInternal() { if (!Enabled) return; lock (this) { try { #region ErrCounter = 0; _Proxy = WCFHelper.Factory.CreateChannel(new InstanceContext(this)); var comObj = _Proxy as ICommunicationObject; comObj.Faulted += (s, ie) => { WriteLine("Faulted"); }; comObj.Closed += (s, ie) => { WriteLine("Closed!"); }; comObj.Closing += (s, ie) => { WriteLine("Closing!"); }; WriteLine("加载并配置完成!"); _Proxy.Subscribe(new SubscribeArg() { Username = Guid.NewGuid().ToString("N") }); WriteLine("注册成功!"); #endregion } catch (Exception ex) { WriteLine(ex.Message); } } } public void Stop() { Enabled = false; Close(); } public void WriteLine(string msg) { Console.WriteLine(msg + "," + DateTime.Now); } #region IEventCallback 成员 public void OnMessageReceived(ArgumentBase a) { if (MessageReceived != null) { MessageReceived(a); } } #endregion } #endregion public partial class Form1 : Form { private SynchronizationContext SyncContext = null; public Form1() { InitializeComponent(); SyncContext = SynchronizationContext.Current; } NotifyManager _NotifyManager = null; private void Form1_Load(object sender, EventArgs e) { _NotifyManager = new NotifyManager(); _NotifyManager.MessageReceived += OnMessageReceived; _NotifyManager.Start(); } protected override void OnClosed(EventArgs e) { if (_NotifyManager != null) { _NotifyManager.MessageReceived -= this.OnMessageReceived; _NotifyManager.Stop(); } base.OnClosed(e); } public void OnMessageReceived(ArgumentBase a) { Console.WriteLine("收到消息:" + a.Model +",InvokeRequired:" + this.InvokeRequired); if (this.InvokeRequired) { SyncContext.Post((d) => { textBox1.Text += a.Model + Environment.NewLine; }, null); } else { textBox1.Text += a.Model + Environment.NewLine; } } private void button1_Click(object sender, EventArgs e) { if (_NotifyManager != null) { Console.WriteLine((_NotifyManager as ICommunicationObject).State); } } } public class WCFHelper { private static DuplexChannelFactory _channelFac; public static DuplexChannelFactory Factory { get { if (_channelFac == null) { _channelFac = new DuplexChannelFactory(typeof(NotifyManager), new NetTcpBinding(SecurityMode.None), EndpointStr); } return _channelFac; } } private static string EndpointStr { get { return "net.tcp://192.168.30.30:9999/EventService"; } } } }
标签: WCF, 消息推送
c# 使用多线程 匿名函数
#region 事件
Thread twork;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
twork = new Thread(DoWork);
twork.Start();
}
protected override void OnHandleDestroyed(EventArgs e)
{
base.OnHandleDestroyed(e);
if (twork!=null)
{
twork.Abort();
twork.Join();
}
}
#endregion
#region 私有方法
private void DoWork()
{
while (true)
{
if (this.FindForm() != null) //控件加载出来了 需要进行数据刷新操作
{
//执行数据刷新操作
GetData();
}
Thread.Sleep(PublicDim.RealCarRefreshTime);
}
}
private CarRecords carRecords = null;
private void GetData()
{
if (qwSelectBayonet.SelectCodeList != null && qwSelectBayonet.SelectCodeList.Count > 0)
{
ArrayOfString arr=new ArrayOfString();
foreach (string code in qwSelectBayonet.SelectCodeList)
{
arr.Add(code);
}
#region 同步方式
//List listRealData = null;
//try
//{
// listRealData = PublicDim.WebserviceClient.GetRealTimeCarDataByRoadCodes_Extend_Client(arr);
//}
//catch(Exception ex)
//{ }
//if (listRealData != null && listRealData.Count > 0)
//{
// List listShow = listRealData.Take(200).Skip(0).ToList();
// if (listRealData!=null)
// {
// listRealData.Clear();
// listRealData = null;
// }
// GC.Collect();
// qpRealContent.Invoke((EventHandler)(delegate
// {
// if (carRecords != null)
// {
// carRecords.ClearAndRealease();
// carRecords.CloseWorkThread();
// carRecords.UpdateData();
// }
// else
// {
// qpRealContent.Controls.Clear();
// carRecords = new CarRecords();
// carRecords.IsReal = true;
// carRecords.ListRealData = listShow; //listRealData;
// carRecords.Height = qpRealContent.Height;
// carRecords.Width = qpRealContent.Width;
// qpRealContent.Controls.Add(carRecords);
// carRecords.Anchor = PublicDim.ControlsAnchor;
// }
// }));
//}
#endregion
#region 异步方式
PublicDim.WebserviceClient.GetRealTimeCarDataByRoadCodes_Extend_ClientCompleted += WebserviceClient_GetRealTimeCarDataByRoadCodes_Extend_ClientCompleted;
PublicDim.WebserviceClient.GetRealTimeCarDataByRoadCodes_Extend_ClientAsync(arr);
#endregion
}
}
线程池
在C#编程语言中,使用线程池可以并行地处理工作,当强制线程和更新进度条时,会使用内建架构的ThreadPool类,为批处理使用多核结构,这里我们来看在C#编程语言中一些关于来自System.Threading的ThreadPool的用法的例子。
介绍
.NET Framework提供了包含ThreadPool类的System.Threading 空间,这是一个可直接访问的静态类,该类对线程池是必不可少的。它是公共“线程池”设计样式的实现。对于后台运行许多各不相同的任务是有用的。对于单个的后台线种而言有更好的选项。
线程的最大数量。这是完全无须知道的。在.NET中ThreadPool的所有要点是它自己在内部管理线程池中线程。多核机器将比以往的机器有更多的线程。微软如此陈述“线程池通常有一个线程的最大数量,如果所有的线程都忙,增加的任务被放置在队列中直到它们能被服务,才能作为可用的线程。”
用法位置
线程池类型能被用于服务器和批处理应用程序中,线程池有更廉价的得到线程的内部逻辑,因为当需要时这些线程已被形成和刚好“连接”,所以线程池风格代码被用在服务器上。
MSDN表述:“线程池经常用在服务器应用程序中,每一个新进来的需求被分配给一个线程池中的线程,这样该需求能被异步的执行,没有阻碍主线程或推迟后继需求的处理。”
MSDN 参考
ThreadPool VS BackgroundWorker
如果你正在使用Windows窗体,宁可使用BackgroundWorker来对付那些更简单的线程需求,BackgroundWorker在网络访问和其他一些简单的事情方面做得很好。但对于多处理器的批处理来说,你需要ThreadPool。
BackgroundWorker 教程
当你的程序要批处理时,考虑线程池
当你的程序产生很多(3个以上)线程时,考虑线程池
当你的程序使用Windows窗体时,考虑后台执行。
线程要考虑的事 同样,如何使用线程的细节能帮助发现最好的代码。下面比较线程情形和哪个类是最好的。
你需要一个额外的线程 使用后台执行
你有许多短期的线程 使用线程池
需求
线程很重要,但对于那些不会花很长时间来执行且只做一件事情的大多数应用程序来说却并不重要的。线程对于界面可用性不是很重要的的应用程序而言也不是很重要,要尽量避免使用线程(译者注:比如进度条不是很重要的应用程序)。
连接方法
可使用QueueUserWorkItem连接方法(methods)到线程池。方法要运行在线程上,则必须把它连接到QueueUserWorkItem。如何实现呢?必须使用WaitCallback。在MSDN中,WaitCallback被描述成当线程池执行时要被调用的委托回调方法,是回调它的参数的委托。
WaitCallback
只需指定“new WaitCallback”语句作为ThreadPool.QueueUserWorkItem的第一个参数来使用WaitCallback.不需要任何其他的代码来使用这方法生效。
使用WaitCallback[c#]的例子
[csharp] view plaincopyprint?
四个辅助线程 上图显示了共有10个线程,但只有四个辅助线程(Worker Thread)在程序中被分配给MainWindow.ProcessFile.
约束辅助线程
如果你有一个双核或四核系统,你将考虑最多两个四个很费力的线程。我们能在运行的线程中保持一个_threadCount 字段并且跟踪它的数值。用这个线程计数字段,你将需要在C#语言中使用一个锁来避免造成这个字段读和写的错误,锁保护你的线程被其他线程所改变。
计数线程的例子 [C#]
[csharp] view plaincopyprint?
(jsonResult);
if (obj != null)
{
Console.WriteLine("字符串反序列化后的对象:"+obj.ToString());
}
else
{
Console.WriteLine("字符串反序列化失败!");
}
Console.Read();
}
///
/// json 序列化
///
///
static string serialization(testjsonobject[] arrjsonobject)
{
StringBuilder sb = new StringBuilder();
serializer.Serialize(arrjsonobject, sb);
return sb.ToString();
}
///
/// jason反序列化
///
///
static T deserialization(string jsonString)
{
return serializer.Deserialize(jsonString);
}
}
public class testjsonobject
{
public string id
{
get;
set;
}
public string name
{
get;
set;
}
}
}
C# WebClient实现采集
C#提交表单:
C#模拟POST提交表单(一)--WebClient
分类: C#2011-11-15 11:03 7115人阅读 评论(2) 收藏 举报
c#stringbyteheaderurl工具
C#的提交表单方式主要有两种WebClient与HttpWebRequest,这里先介绍一种
WebClient,转送门:http://msdn.microsoft.com/zh-cn/library/system.net.webclient(v=VS.80).aspx
[csharp] view plaincopyprint?
C#模拟POST提交表单(二)--HttpWebRequest以及HttpWebResponse
分类: C#2011-11-19 23:21 8740人阅读 评论(3) 收藏 举报
c#encodingweb开发浏览器服务器stream
上次介绍了用WebClient的方式提交POST请求,这次,我继续来介绍用其它一种方式
HttpWebRequest以及HttpWebResponse
自认为与上次介绍的WebClient最大的不同之处在于HttpWebRequest更灵活,也更强大,比如,HttpWebRequest支持Cookie,而WebClient就不支持,因此,如果要登录某个网站才能进行某些操作的时候,HttpWebResponse就派上用场了。
补充:
WebClient是可以操作Cookie的,因为Cookie本质上就是个字符串,只要服务器返回的头是“SetCooie:xxx”,所以,按照返回的格式做下处理(不能原样返回,具体可以抓包分析下格式),存起来,然后在HTTP请求头上加上“Cookie:xxx”即可
首先要提下Referer还有Cookie
Referer:就是一般在浏览器中发送Http请求时带的头信息,被广泛用来统计点击信息,即从那个点击而来,所以有些网站也会用这个性质来防盗链,很多时候如果什么图片仅限内部交流之类的,就是用了这个原理。
Cookie:某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密),通常大家登录的时候就要用到它,登录后,网站会储存一个Cookie的东西在本地计算机上,然后每次访问网站时,便会把这个网站的Cookie也一并发送过去,服务器就凭借这个来确认你的身份。它是个重要信息,有些黑客也会通过盗取Cookie的方式来侵入你的账户。
好了,现在开始具体说明:
[csharp] view plaincopyprint?
- void Example()
- {
- // 连接 ProcessFile 方法到线程池.
- //注意: 'a' 是一个作为参数的对象
- ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), a);
- }
- private void ProcessFile(object a)
- {
- // 我被连接到线程池通过 WaitCallback.
- }
- //指定作为线程池方法的参数的类
- class ThreadInfo
- {
- public string FileName { get; set; }
- public int SelectedIndex { get; set; }
- }
- class Example
- {
- public Example()
- {
- // 声明一个新的参数对象
- ThreadInfo threadInfo = new ThreadInfo();
- threadInfo.FileName = "file.txt";
- threadInfo.SelectedIndex = 3;
- //发送自定义的对象到线程方法
- ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), threadInfo);
- }
- private void ProcessFile(object a)
- {
- ThreadInfo threadInfo = a as ThreadInfo;
- string fileName = threadInfo.FileName;
- int index = thread.SelectedIndex;
- }
- }
- //设置进度条的长度.
- // 这里我们有6个单位来完成,所以6是最大值。
- // 最小值通常是0
- progressBar1.Maximum = 6; // 或其他数字
- progressBar1.Minimum = 0;
- public partial class MainWindow : Form
- {
- // 这是运行在UI线程来更新条的委托
- public delegate void BarDelegate();
- //该窗体的构造器(由Visual Studio自己产生)
- public MainWindow()
- {
- InitializeComponent();
- }
- //当按下按钮,启动一个新的线程
- private void button_Click(object sender, EventArgs e)
- {
- // 设定进度条的长度.
- progressBar1.Maximum = 6;
- progressBar1.Minimum = 0;
- // 给线程传递这些值.
- ThreadInfo threadInfo = new ThreadInfo();
- threadInfo.FileName = "file.txt";
- threadInfo.SelectedIndex = 3;
- ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), threadInfo);
- }
- // 运行在后台线程上的东西
- private void ProcessFile(object a)
- {
- // (省略)
- // 使用'a'做一些重要的事.
- // 告诉UI 我们已经完成了.
- try
- {
- // 在窗体中调用委托 .
- this.Invoke(new BarDelegate(UpdateBar));
- }
- catch
- {
- //当一些问题发生后我们能使程序恢复正常
- }
- }
- //更新进度条.
- private void UpdateBar()
- {
- progressBar1.Value++;
- if (progressBar1.Value == progressBar1.Maximum)
- {
- // 结束了,进度条满了.
- }
- }
- }
四个辅助线程 上图显示了共有10个线程,但只有四个辅助线程(Worker Thread)在程序中被分配给MainWindow.ProcessFile.
约束辅助线程
如果你有一个双核或四核系统,你将考虑最多两个四个很费力的线程。我们能在运行的线程中保持一个_threadCount 字段并且跟踪它的数值。用这个线程计数字段,你将需要在C#语言中使用一个锁来避免造成这个字段读和写的错误,锁保护你的线程被其他线程所改变。
计数线程的例子 [C#]
[csharp] view plaincopyprint?
- // 锁住这个对象.
- readonly object _countLock = new object();
- private void ProcessFile(object argument)
- {
- // 约束辅助线程的数量
- while (true)
- {
- lock (_countLock)
- {
- if (_threadCount < 4)
- {
- // Start the processing
- _threadCount++;
- break;
- }
- }
- Thread.Sleep(50);
- }
- // Do work...
- }
- string postString = "arg1=a&arg2=b";//这里即为传递的参数,可以用工具抓包分析,也可以自己分析,主要是form里面每一个name都要加进来
- byte[] postData = Encoding.UTF8.GetBytes(postString);//编码,尤其是汉字,事先要看下抓取网页的编码方式
- string url = "http://localhost/register.php";//地址
- WebClient webClient = new WebClient();
- webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");//采取POST方式必须加的header,如果改为GET方式的话就去掉这句话即可
- byte[] responseData = webClient.UploadData(url, "POST", postData);//得到返回字符流
- string srcString = Encoding.UTF8.GetString(responseData);//解码
C#模拟POST提交表单(二)--HttpWebRequest以及HttpWebResponse
分类: C#2011-11-19 23:21 8740人阅读 评论(3) 收藏 举报
c#encodingweb开发浏览器服务器stream
上次介绍了用WebClient的方式提交POST请求,这次,我继续来介绍用其它一种方式
HttpWebRequest以及HttpWebResponse
自认为与上次介绍的WebClient最大的不同之处在于HttpWebRequest更灵活,也更强大,比如,HttpWebRequest支持Cookie,而WebClient就不支持,因此,如果要登录某个网站才能进行某些操作的时候,HttpWebResponse就派上用场了。
补充:
WebClient是可以操作Cookie的,因为Cookie本质上就是个字符串,只要服务器返回的头是“SetCooie:xxx”,所以,按照返回的格式做下处理(不能原样返回,具体可以抓包分析下格式),存起来,然后在HTTP请求头上加上“Cookie:xxx”即可
首先要提下Referer还有Cookie
Referer:就是一般在浏览器中发送Http请求时带的头信息,被广泛用来统计点击信息,即从那个点击而来,所以有些网站也会用这个性质来防盗链,很多时候如果什么图片仅限内部交流之类的,就是用了这个原理。
Cookie:某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密),通常大家登录的时候就要用到它,登录后,网站会储存一个Cookie的东西在本地计算机上,然后每次访问网站时,便会把这个网站的Cookie也一并发送过去,服务器就凭借这个来确认你的身份。它是个重要信息,有些黑客也会通过盗取Cookie的方式来侵入你的账户。
好了,现在开始具体说明:
[csharp] view plaincopyprint?
- HttpWebRequest request = (HttpWebRequest)WebRequest.Create("POST请求的地址");
- request.CookieContainer = new CookieContainer();
- CookieContainer cookie = request.CookieContainer;//如果用不到Cookie,删去即可
- //以下是发送的http头,随便加,其中referer挺重要的,有些网站会根据这个来反盗链
- request.Referer = “http://localhost/index.php”;
- request.Accept = "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
- request.Headers["Accept-Language"] = "zh-CN,zh;q=0.";
- request.Headers["Accept-Charset"] = "GBK,utf-8;q=0.7,*;q=0.3";
- request.UserAgent = "User-Agent:Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1";
- request.KeepAlive = true;
- //上面的http头看情况而定,但是下面俩必须加
- request.ContentType = "application/x-www-form-urlencoded";
- request.Method = "POST";
- Encoding encoding = Encoding.UTF8;//根据网站的编码自定义
- byte[] postData = encoding.GetBytes(postDataStr);//postDataStr即为发送的数据,格式还是和上次说的一样
- request.ContentLength = postData.Length;
- Stream requestStream = request.GetRequestStream();
- requestStream.Write(postData, 0, postData.Length);
- HttpWebResponse response = (HttpWebResponse)request.GetResponse();
- Stream responseStream = response.GetResponseStream();
- //如果http头中接受gzip的话,这里就要判断是否为有压缩,有的话,直接解压缩即可
- if (response.Headers["Content-Encoding"] != null && response.Headers["Content-Encoding"].ToLower().Contains("gzip"))
- {
- responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
- }
- StreamReader streamReader = new StreamReader(responseStream, encoding);
- string retString = streamReader.ReadToEnd();
- streamReader.Close();
- responseStream.Close();
- return retString;