C# 使用无功扩展(Rx)进行套接字编程是否可行?
使用Rx编写C# 使用无功扩展(Rx)进行套接字编程是否可行?,c#,.net,sockets,system.reactive,C#,.net,Sockets,System.reactive,使用Rx编写GetMessages函数最简洁的方法是什么: static void Main() { Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); var messages = GetMessages(socket, IPAddress.Loopback, 4000); messages.Subscribe(x => Cons
GetMessages
函数最简洁的方法是什么:
static void Main()
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var messages = GetMessages(socket, IPAddress.Loopback, 4000);
messages.Subscribe(x => Console.WriteLine(x));
Console.ReadKey();
}
static IObservable<string> GetMessages(Socket socket, IPAddress addr, int port)
{
var whenConnect = Observable.FromAsyncPattern<IPAddress, int>(socket.BeginConnect, socket.EndConnect)(addr, port);
// now will receive a stream of messages
// each message is prefixed with an 4 bytes/Int32 indicating it's length.
// the rest of the message is a string
// ????????????? Now What ?????????????
}
好吧,这可能是“作弊”,但我想你可以重新确定我的非Rx答案的用途,并用Observable.Create
将其包装起来
我相当确定将套接字返回为IDisposable
是错误的语义,但不确定会是什么
static IObservable<string> GetMessages(Socket socket, IPAddress addr, int port)
{
return Observable.CreateWithDisposable<string>(
o =>
{
byte[] buffer = new byte[1024];
Action<int, Action<int>> readIntoBuffer = (length, callback) =>
{
var totalRead = 0;
AsyncCallback receiveCallback = null;
AsyncCallback temp = r =>
{
var read = socket.EndReceive(r);
if (read == 0)
{
socket.Close();
o.OnCompleted();
return;
}
totalRead += read;
if (totalRead < length)
{
socket.BeginReceive(buffer, totalRead, length - totalRead, SocketFlags.None, receiveCallback, null);
}
else
{
callback(length);
}
};
receiveCallback = temp;
socket.BeginReceive(buffer, totalRead, length, SocketFlags.None, receiveCallback, null);
};
Action<int> sizeRead = null;
Action<int> messageRead = x =>
{
var message = Encoding.UTF8.GetString(buffer, 0, x);
o.OnNext(message);
readIntoBuffer(4, sizeRead);
};
Action<int> temp2 = x =>
{
var size = BitConverter.ToInt32(buffer, 0);
readIntoBuffer(size, messageRead);
};
sizeRead = temp2;
AsyncCallback connectCallback = r =>
{
socket.EndConnect(r);
readIntoBuffer(4, sizeRead);
};
socket.BeginConnect(addr, port, connectCallback, null);
return socket;
});
}
static IObservable GetMessages(套接字、IPAddress addr、int端口)
{
返回可观察的.CreateWithDisposable(
o=>
{
字节[]缓冲区=新字节[1024];
操作readIntoBuffer=(长度,回调)=>
{
var totalRead=0;
AsyncCallback-receiveCallback=null;
异步回调温度=r=>
{
var read=socket.EndReceive(r);
如果(读==0)
{
socket.Close();
o、 未完成();
返回;
}
totalRead+=读取;
if(总读取<长度)
{
socket.BeginReceive(缓冲区、totalRead、长度-totalRead、SocketFlags.None、receiveCallback、null);
}
其他的
{
回调(长度);
}
};
receiveCallback=temp;
socket.BeginReceive(缓冲区、totalRead、长度、SocketFlags.None、receiveCallback、null);
};
Action sizeRead=null;
Action messageRead=x=>
{
var message=Encoding.UTF8.GetString(缓冲区,0,x);
o、 OnNext(消息);
readIntoBuffer(4,sizeRead);
};
动作节奏2=x=>
{
变量大小=位转换器.ToInt32(缓冲区,0);
readIntoBuffer(大小,messageRead);
};
sizeRead=temp2;
AsyncCallback-connectCallback=r=>
{
插座.端接(r);
readIntoBuffer(4,sizeRead);
};
BeginConnect(addr,port,connectCallback,null);
返回插座;
});
}
按照这些思路做的事情可能会奏效。这没有经过测试,不考虑异常和部分返回消息的情况。但除此之外,我相信这是一个正确的方向
public static IObservable<T> GetSocketData<T>(this Socket socket,
int sizeToRead, Func<byte[], T> valueExtractor)
{
return Observable.CreateWithDisposable<T>(observer =>
{
var readSize = Observable
.FromAsyncPattern<byte[], int, int, SocketFlags, int>(
socket.BeginReceive,
socket.EndReceive);
var buffer = new byte[sizeToRead];
return readSize(buffer, 0, sizeToRead, SocketFlags.None)
.Subscribe(
x => observer.OnNext(valueExtractor(buffer)),
observer.OnError,
observer.OnCompleted);
});
}
public static IObservable<int> GetMessageSize(this Socket socket)
{
return socket.GetSocketData(4, buf => BitConverter.ToInt32(buf, 0));
}
public static IObservable<string> GetMessageBody(this Socket socket,
int messageSize)
{
return socket.GetSocketData(messageSize, buf =>
Encoding.UTF8.GetString(buf, 0, messageSize));
}
public static IObservable<string> GetMessage(this Socket socket)
{
return
from size in socket.GetMessageSize()
from message in Observable.If(() => size != 0,
socket.GetMessageBody(size),
Observable.Return<string>(null))
select message;
}
public static IObservable<string> GetMessagesFromConnected(
this Socket socket)
{
return socket
.GetMessage()
.Repeat()
.TakeWhile(msg => !string.IsNullOrEmpty(msg));
}
public static IObservable<string> GetMessages(this Socket socket,
IPAddress addr, int port)
{
return Observable.Defer(() =>
{
var whenConnect = Observable
.FromAsyncPattern<IPAddress, int>(
socket.BeginConnect, socket.EndConnect);
return from _ in whenConnect(addr, port)
from msg in socket.GetMessagesFromConnected()
.Finally(socket.Close)
select msg;
});
}
public static IObservable GetSocketData(此套接字,
int-sizeToRead,Func-valueExtractor)
{
返回Observable.CreateWithDisposable(观察者=>
{
var readSize=可观测
.FromAsyncPattern(
socket.BeginReceive,
插座(接收端);
var buffer=新字节[sizeToRead];
返回readSize(缓冲区、0、sizeToRead、SocketFlags.None)
.订阅(
x=>observer.OnNext(值提取器(缓冲区)),
observer.OnError,
观察员(未完成);
});
}
公共静态IObservable GetMessageSize(此套接字)
{
返回socket.GetSocketData(4,buf=>BitConverter.ToInt32(buf,0));
}
公共静态IObservable GetMessageBody(此套接字,
int messageSize)
{
返回socket.GetSocketData(messageSize,buf=>
Encoding.UTF8.GetString(buf,0,messageSize));
}
公共静态IObservable GetMessage(此套接字)
{
返回
来自socket.GetMessageSize()中的大小
如果(()=>大小!=0,
socket.GetMessageBody(大小),
可观察。返回(空)
选择消息;
}
公共静态IObservable GetMessagesFromConnected(
这个插座)
{
返回插座
.GetMessage()
.重复
.TakeWhile(msg=>!string.IsNullOrEmpty(msg));
}
公共静态IObservable GetMessages(此套接字,
IP地址地址(int端口)
{
返回可观察的延迟(()=>
{
var whenConnect=可观测
.FromAsyncPattern(
socket.BeginConnect、socket.EndConnect);
连接时从uu u;返回(地址,端口)
来自socket.GetMessagesFromConnected()中的消息
.Finally(socket.Close)
选择味精;
});
}
编辑:如Dave Sexton在中所建议的,可以使用(在GetSockedData中)来处理不完整的读取
编辑:另外,看看杰弗里·梵高的这篇文章:我正在做类似的事情。单个“socket”不能直接转换为
IObservable
,因此我正在探索TPL和Rx的混合。例如,连接操作更像是一项任务,而重复读取可以被视为IO
。如果最终结果足够简单,它将成为其中的一部分。我的想法是,您可以在流的开头使用Connect,然后在流的结尾使用Close,这在某种程度上相当于在生成一些现有Rx示例时使用的MouseDown&MouseUp IObservable事件。当问到这个问题时,它可能不存在,但现在它确实存在,并且可能在这里引起兴趣:“Rxx提供了几个API,使异步调用web请求变得容易,包括(…)System.Net.Sockets.Socket、System.Net.Sockets.TcpClient”。是的,这实际上不起作用,因为EndRecieve返回的值可能小于完整大小,需要另一个请求
public static IObservable<T> GetSocketData<T>(this Socket socket,
int sizeToRead, Func<byte[], T> valueExtractor)
{
return Observable.CreateWithDisposable<T>(observer =>
{
var readSize = Observable
.FromAsyncPattern<byte[], int, int, SocketFlags, int>(
socket.BeginReceive,
socket.EndReceive);
var buffer = new byte[sizeToRead];
return readSize(buffer, 0, sizeToRead, SocketFlags.None)
.Subscribe(
x => observer.OnNext(valueExtractor(buffer)),
observer.OnError,
observer.OnCompleted);
});
}
public static IObservable<int> GetMessageSize(this Socket socket)
{
return socket.GetSocketData(4, buf => BitConverter.ToInt32(buf, 0));
}
public static IObservable<string> GetMessageBody(this Socket socket,
int messageSize)
{
return socket.GetSocketData(messageSize, buf =>
Encoding.UTF8.GetString(buf, 0, messageSize));
}
public static IObservable<string> GetMessage(this Socket socket)
{
return
from size in socket.GetMessageSize()
from message in Observable.If(() => size != 0,
socket.GetMessageBody(size),
Observable.Return<string>(null))
select message;
}
public static IObservable<string> GetMessagesFromConnected(
this Socket socket)
{
return socket
.GetMessage()
.Repeat()
.TakeWhile(msg => !string.IsNullOrEmpty(msg));
}
public static IObservable<string> GetMessages(this Socket socket,
IPAddress addr, int port)
{
return Observable.Defer(() =>
{
var whenConnect = Observable
.FromAsyncPattern<IPAddress, int>(
socket.BeginConnect, socket.EndConnect);
return from _ in whenConnect(addr, port)
from msg in socket.GetMessagesFromConnected()
.Finally(socket.Close)
select msg;
});
}