鉴于,CookieContainer的实例为not thread safe。
此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。
事实证明,如果没有同步,我不能在多个并发HTTP请求中使用相同的容器。不幸的是,MSDN的文档不清楚如何正确地同步它。
解决方案是为每个请求使用主容器的副本,一旦请求完成,副本中的cookie可以合并回主容器。创建副本和合并可以以同步方式完成。
所以问题是:如何制作CookieContainer类实例的副本?
答案 0 :(得分:8)
CookieContainer类是Serializable。既然你说你需要序列化它,为什么不只是使用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);
}
}
答案 1 :(得分:4)
看看CookieContainter类,你会发现当cookie集合发生变化时会发生并发场景,对吗?
你会注意到CookieContainer的作者负责使用lock {}
和SyncRoot
围绕代码的这些收集更改部分,我不认为这种方法没有得到解决并发场景。
此外,您可以注意到任何添加的Cookie实际上是克隆,因此容器内的Cookie和所有操作都不会搞乱cookie容器外的对象引用。在最糟糕的情况下,我错过了一些东西,克隆还给了我们一个关于你究竟要复制什么以及如何使用它的提示,如果使用其他帖子中描述的反射方法(我个人不会考虑)它是一个黑客,因为它符合要求,它是管理,合法和安全的代码:))。
事实上,提到的所有MSDN文档都是“任何实例成员都不能保证线程安全”。 - 它是一种提醒,因为你是对的,你真的需要小心。然后用这样的陈述你可以基本上假设两件事:1)非静态成员根本不安全。 2)某些成员可以是线程安全的,但没有正确记录。
答案 2 :(得分:2)
您可以使用Reflection获取与所有Uri
相关的所有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;
}
答案 3 :(得分:1)
你可以用反射来做。这可以改进和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"));
答案 4 :(得分:0)
只需在Alan's answer above上添加我自己的转折即可转换为Base64字符串。
moveProductFromWaitListToCart