C# 我如何复制CookieContainer?
给出了CookieContainer的实例 此类型的任何公共静态(在Visual Basic中共享)成员都是线程安全的。任何实例成员都不能保证线程安全 因此,如果没有同步,我无法跨多个并发HTTP请求使用相同的容器。不幸的是,从MSDN的文档来看,还不清楚如何正确地同步它 解决方案是为每个请求使用主容器的副本,一旦请求完成,副本中的cookie可以合并回主容器。可以以同步方式创建副本和合并C# 我如何复制CookieContainer?,c#,.net,cookies,C#,.net,Cookies,给出了CookieContainer的实例 此类型的任何公共静态(在Visual Basic中共享)成员都是线程安全的。任何实例成员都不能保证线程安全 因此,如果没有同步,我无法跨多个并发HTTP请求使用相同的容器。不幸的是,从MSDN的文档来看,还不清楚如何正确地同步它 解决方案是为每个请求使用主容器的副本,一旦请求完成,副本中的cookie可以合并回主容器。可以以同步方式创建副本和合并 因此,问题是:如何复制CookieContainer类的实例?您可以使用反射获取与所有Uris相关的所有c
因此,问题是:如何复制CookieContainer类的实例?您可以使用反射获取与所有
Uri
s相关的所有cookie,然后创建新的CookieContainer
并将其添加到其中,可能如下所示:
public static CookieContainer DeepClone(CookieContainer src)
{
CookieContainer cookieContainer = new CookieContainer();
Hashtable table = (Hashtable)src.GetType().InvokeMember("m_domainTable", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, src, new object[] { });
foreach (var tableKey in table.Keys)
{
String str_tableKey = (string)tableKey;
if (str_tableKey[0] == '.')
str_tableKey = str_tableKey.Substring(1);
SortedList list = (SortedList)table[tableKey].GetType().InvokeMember("m_list", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, table[tableKey], new object[] { });
foreach (var listKey in list.Keys)
{
String url = "https://" + str_tableKey + (string)listKey;
CookieCollection collection = src.GetCookies(new Uri(url));
foreach (Cookie c in collection)
cookieContainer.Add(new Cookie(c.Name, c.Value, c.Path, c.Domain)
{
Comment = c.Comment,
CommentUri = c.CommentUri,
Discard = c.Discard,
Expired = c.Expired,
Expires = c.Expires,
HttpOnly = c.HttpOnly,
Port = c.Port,
Secure = c.Secure,
Version = c.Version
});
}
}
return cookieContainer;
}
CookieContainer类是可序列化的。既然您说过无论如何都需要序列化它,为什么不直接使用BinaryFormatter将其序列化到MemorySteam,然后反序列化以生成副本呢 我知道这太简单了,所以如果没有帮助,请忽略
private CookieContainer CopyContainer(CookieContainer container)
{
using(MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, container);
stream.Seek(0, SeekOrigin.Begin);
return (CookieContainer)formatter.Deserialize(stream);
}
}
你可以用反射来做。这可能会得到改进,YMMV:
//Set up the source cookie container
var cookieContainerA = new CookieContainer();
cookieContainerA.Add(new Uri("http://foobar.com"), new Cookie("foo", "bar"));
cookieContainerA.Add(new Uri("http://foobar.com"), new Cookie("baz", "qux"));
cookieContainerA.Add(new Uri("http://abc123.com"), new Cookie("abc", "123"));
cookieContainerA.Add(new Uri("http://abc123.com"), new Cookie("def", "456"));
//Set up our destination cookie container
var cookieContainerB = new CookieContainer();
//Get the domain table member
var type = typeof(CookieContainer);
var domainTableField = type.GetField("m_domainTable", BindingFlags.NonPublic | BindingFlags.Instance);
var domainTable = (Hashtable)domainTableField.GetValue(cookieContainerA);
//Iterate the domain table
foreach (var obj in domainTable)
{
var entry = (DictionaryEntry)obj;
//The domain is the key (we only need this for our Console.WriteLine later)
var domain = entry.Key;
var valuesProperty = entry.Value.GetType().GetProperty("Values");
var values = (IList)valuesProperty.GetValue(entry.Value);
foreach (var valueObj in values)
{
//valueObj is a CookieCollection, cast and add to our destination container
var cookieCollection = (CookieCollection)valueObj;
cookieContainerB.Add(cookieCollection);
//This is a dump of our source cookie container
foreach (var cookieObj in cookieCollection)
{
var cookie = (Cookie)cookieObj;
Console.WriteLine("Domain={0}, Name={1}, Value={2}", domain, cookie.Name, cookie.Value);
}
}
}
//Test the copying
//var foobarCookies = cookieContainerB.GetCookies(new Uri("http://foobar.com"));
//var abc123Cookies = cookieContainerB.GetCookies(new Uri("http://abc123.com"));
看看这个类,您会发现当cookie集合中发生更改时,并发场景会发生,对吗
您会注意到,CookieContainer的作者在这些集合中使用了lock{}
和SyncRoot
来更改代码的各个部分,我不认为这种方法不适用于并发场景
此外,您还可以注意到,任何添加的内容都是字面上的克隆,因此容器内的cookie和所做的所有操作都不会与cookie容器外的对象引用相混淆。在最糟糕的情况下,我丢失了一些东西,克隆也给了我们一个提示,您必须复制什么,以及如何做到这一点,在使用其他帖子中描述的反射方法的情况下(我个人不认为它是黑客,因为它符合要求,它是管理的、合法的和安全的代码:))。
事实上,MSDN文档中提到的都是“任何实例成员都不能保证线程安全。”-这是一种提醒,因为你是对的,你真的需要小心。然后使用这样的语句,您可以假设基本上有两件事:1)非静态成员根本不安全。2) 有些成员可以是线程安全的,但它们没有正确的文档记录。只是添加了我自己的twist,以转换为Base64字符串
public static string ToBase64(CookieContainer container)
{
string str = null;
byte[] bytes = null;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, container);
bytes = ms.ToArray();
}
str = Convert.ToBase64String(bytes);
return str;
}
public static CookieContainer FromBase64(string container_base64)
{
CookieContainer cc = null;
byte[] bytes = Convert.FromBase64String(container_base64);
using (MemoryStream ms = new MemoryStream(bytes))
{
BinaryFormatter bf = new BinaryFormatter();
cc = (CookieContainer)bf.Deserialize(ms);
}
return cc;
}
你看过使用Reflector或DotPeak的源代码吗?我相信CookieContainer是线程安全的,我已经多次使用它来处理共享单个CookieContainer的许多web服务请求,我从来没有遇到过任何问题。我做到了,我仍然需要能够复制它以进行序列化。我认为这符合您的要求。对于同步而言,
lock
有什么问题?@AmerSawan,这是一个黑客攻击,不是吗?@AmerSawan由于内存流没有保存非托管资源,因此没有必要在那里进行dispose/close操作。此外,我假设我的代码足以证明这一原理,代码的任何用户都会足够聪明,能够适应自己的使用,并根据需要调用Dispose/Close。因为MemoryStream
实现了IDisposable
,您需要调用Dispose
,否则FXCop将报告代码警告。@bonomo是的,它看起来对我有效,但web开发不是我的主要工作,您可能希望明天尝试,并让我知道它是否对您有效。我主要为嵌入式系统和桌面应用程序编写软件SVS2019 v16.5.4,带有.NET 4.7.2的web应用程序,Win 2012R2标准x64-尝试调用formatter.Serialize()会导致异常“对象图不能为空。\r\n参数名称:图”。我做错了什么?使用反射是一种技巧。如果你看一下内部,还有5个字段你没有提到,它们定义了容器的状态。你也要抄吗?如果在CookieContainer类之外还有什么东西需要复制的话?这是一种黑客行为,但不管出于什么原因,你试图做的事情并不简单,所以黑客就是你剩下的。如果你反对反思,也许你可以提一下。其中3个内部构件映射到属性(Capacity、MaxCookieSize和PerDomainCapacity),您可以通过分配来复制这些属性。计数是N/A,这会留下m_fqdnMyDomain,我不确定TBHAlan的序列化解决方案是否是最好的选择,因为你可以重新发布sombody的else答案:使用反射是一种攻击。如果你看一下内部,还有5个字段你没有提到,它们定义了容器的状态。你也要抄吗?如果CookieContainer类之外还有什么东西需要复制的话?反射和序列化都是黑客攻击,但我认为使用反射更常见,性能更友好!序列化是合法的(经批准的)黑客行为。