C# Socket 如何完全发送/接收数据
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# Socket 如何完全发送/接收数据相关的知识,希望对你有一定的参考价值。
参考技术A发送:
private void button2_Click(object sender, EventArgs e)
if (s.Connected == true)
try
string abc = textBox3.Text;
s.Send(Encoding.ASCII.GetBytes(abc));
MessageBox.Show("向服务器发送:" + abc);
catch
MessageBox.Show("发送失败");
接收:
System.Timers.Timer aTimer = new System.Timers.Timer();
byte[] res = new byte[1024];
private void Form1_Load(object sender, EventArgs e)
Control.CheckForIllegalCrossThreadCalls = false;
aTimer.Enabled = false;
Thread thread1 = new Thread(TimerMange);
thread1.IsBackground = true;
thread1.Start();
void TimerMange()
aTimer.Elapsed += new ElapsedEventHandler(socket_rev); //定时事件的方法
aTimer.Interval = 100;
private void socket_rev(object sender, EventArgs e)
int receiveLength = s.Receive(res, res.Length, SocketFlags.None);
if (receiveLength > 0)
textBox4.Text = Encoding.ASCII.GetString(res, 0, receiveLength);
string abc = "HaveReceive";
s.Send(Encoding.ASCII.GetBytes(abc));
扩展资料:
C#Socket异步通讯学习
思路
1、Socket分为面向连接协议(如TCP)和无连接协议(如UDP)
2、Socket通信分为同步操作模式和异步操作模式,同步模式在建立连接之前/收到消息之前会阻塞当前进程,异步模式不会阻塞当前进程
综合以上两点,考虑到体验,当然是选择异步Socket啦,另外,这里使用的是面向连接的协议,那么实现思路大致如下:
1、创建Socket对象
2、绑定IP和端口
3、侦听连接
4、开始一个异步操作来接收一个接入的连接请求
5、处理接入的请求
6、向客户端发送消息
C# UDP Socket:获取接收者地址
【中文标题】C# UDP Socket:获取接收者地址【英文标题】:C# UDP Socket: Get receiver address 【发布时间】:2011-05-08 01:07:52 【问题描述】:我有一个异步 UDP 服务器类,其套接字绑定在 IPAddress.Any 上,我想知道接收到的数据包发送到哪个 IPAddress(...或在哪个 IPAddress 上接收)。看来我不能只使用 Socket.LocalEndPoint 属性,因为它总是返回 0.0.0.0 (这是有道理的,因为它绑定到那个......)。
以下是我目前正在使用的代码中有趣的部分:
private Socket udpSock;
private byte[] buffer;
public void Starter()
//Setup the socket and message buffer
udpSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
udpSock.Bind(new IPEndPoint(IPAddress.Any, 12345));
buffer = new byte[1024];
//Start listening for a new message.
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
udpSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, udpSock);
private void DoReceiveFrom(IAsyncResult iar)
//Get the received message.
Socket recvSock = (Socket)iar.AsyncState;
EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
int msgLen = recvSock.EndReceiveFrom(iar, ref clientEP);
byte[] localMsg = new byte[msgLen];
Array.Copy(buffer, localMsg, msgLen);
//Start listening for a new message.
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
udpSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, udpSock);
//Handle the received message
Console.WriteLine("Recieved 0 bytes from 1:2 to 3:4",
msgLen,
((IPEndPoint)clientEP).Address,
((IPEndPoint)clientEP).Port,
((IPEndPoint)recvSock.LocalEndPoint).Address,
((IPEndPoint)recvSock.LocalEndPoint).Port);
//Do other, more interesting, things with the received message.
如前所述,这总是打印如下一行:
从 127.0.0.1:1678 到 0.0.0.0:12345 收到 32 个字节
我希望它是这样的:
从 127.0.0.1:1678 到 127.0.0.1:12345 收到 32 个字节
提前感谢您对此的任何想法! --亚当
更新
好吧,我找到了一个解决方案,虽然我不喜欢它......基本上,我不是打开一个绑定到 IPAddress.Any 的单个 udp 套接字,而是为每个可用的 IPAddress 创建一个唯一的套接字。因此,新的 Starter 函数如下所示:
public void Starter()
buffer = new byte[1024];
//create a new socket and start listening on the loopback address.
Socket lSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
lSock.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
EndPoint ncEP = new IPEndPoint(IPAddress.Any, 0);
lSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref ncEP, DoReceiveFrom, lSock);
//create a new socket and start listening on each IPAddress in the Dns host.
foreach(IPAddress addr in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
if(addr.AddressFamily != AddressFamily.InterNetwork) continue; //Skip all but IPv4 addresses.
Socket s = new Socket(addr.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
s.Bind(new IPEndPoint(addr, 12345));
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
s.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, s);
这只是为了说明这个概念,这段代码最大的问题是每个套接字都试图使用相同的缓冲区......这通常是一个坏主意......
必须有更好的解决方案;我的意思是,源和目标是 UDP 数据包头的一部分!哦,好吧,我想我会继续这样做,直到有更好的东西。
【问题讨论】:
【参考方案1】:我也遇到了同样的问题。我没有看到使用ReceiveFrom
或其异步变体来检索接收到的数据包的目标地址的方法。
但是...如果您使用ReceiveMessageFrom
或其变体,您将获得IPPacketInformation
(通过引用ReceiveMessageFrom
和EndReceiveMessageFrom
,或作为SocketAsyncEventArgs
的属性传递给您的回调在ReceiveMessageFromAsync
)。该对象将包含接收数据包的 IP 地址和接口号。
(请注意,此代码尚未经过测试,因为我使用了 ReceiveMessageFromAsync
而不是假冒的 Begin/End 调用。)
private void ReceiveCallback(IAsyncResult iar)
IPPacketInformation packetInfo;
EndPoint remoteEnd = new IPEndPoint(IPAddress.Any, 0);
SocketFlags flags = SocketFlags.None;
Socket sock = (Socket) iar.AsyncState;
int received = sock.EndReceiveMessageFrom(iar, ref flags, ref remoteEnd, out packetInfo);
Console.WriteLine(
"0 bytes received from 1 to 2",
received,
remoteEnd,
packetInfo.Address
);
注意,您显然应该调用SetSocketOption(SocketOptionLevel.IP, SocketOptionName.PacketInformation, true)
作为设置套接字的一部分,在您之前 Bind
它。 ...ReceiveMessageFrom... 方法将为您设置它,但您可能只会在设置选项后 Windows 看到的任何数据包上看到有效信息。 (在实践中,这并不是什么大问题——但何时/如果它发生了,原因将是一个谜。最好完全阻止它。)
【讨论】:
经过测试,现在可以使用了。这是迄今为止有关该主题的几个问题的最佳答案。我很想看看 ReceiveMessageFromAsync 版本,但如果你不回答,我可能会自己解决。 @StephenKennedy:老实说,我认为我什至没有那个代码了。 :P 不过,IIRC 并不是很复杂;只需设置一个SocketAsyncEventArgs
,将处理程序附加到其Completed
事件,然后将对象传递给ReceiveMessageFromAsync
。
不用担心。我有 SendToAsync() 工作,所以可以玩 ReceiveMessageFromAsync() tmw :) 谢谢。
事实上,IPPacketInformation
包含接收数据包的 NIC 的 Interface
(实际上是索引),以及它被发送到的目标的 Address
。因此,您可以判断它是发送到多播地址还是广播地址,而不仅仅是您的 NIC 上的哪个单播地址。【参考方案2】:
关于缓冲区问题,请尝试以下方法:
创建一个名为 StateObject 的类来存储您希望在回调中拥有的任何数据,并带有一个缓冲区,如果您需要它还包括套接字(我看到您当前正在将 udpSock 作为您的 stateObject 传递)。将新创建的对象传递给异步方法,然后您将可以在回调中访问它。
public void Starter()
StateObject state = new StateObject();
//set any values in state you need here.
//create a new socket and start listening on the loopback address.
Socket lSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
lSock.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
EndPoint ncEP = new IPEndPoint(IPAddress.Any, 0);
lSock.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, SocketFlags.None, ref ncEP, DoReceiveFrom, state);
//create a new socket and start listening on each IPAddress in the Dns host.
foreach(IPAddress addr in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
if(addr.AddressFamily != AddressFamily.InterNetwork) continue; //Skip all but IPv4 addresses.
Socket s = new Socket(addr.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
s.Bind(new IPEndPoint(addr, 12345));
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
StateObject objState = new StateObject();
s.BeginReceiveFrom(objState.buffer, 0, objState.buffer.length, SocketFlags.None, ref newClientEP, DoReceiveFrom, objState);
在搜索这个问题时我发现:
http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.beginreceivefrom.aspx
然后,您可以像当前对 udpSock 和缓冲区所做的那样从 AsyncState 转换 StateObject,以及您需要的任何其他数据都将存储在那里。
我想现在唯一的问题是如何以及在哪里存储数据,但由于我不知道你的实现,所以我无能为力。
【讨论】:
【参考方案3】:我认为如果你绑定到 127.0.0.1 而不是 IPAddress.Any 你会得到你想要的行为。
0.0.0.0 故意表示“每个可用的 IP 地址”,并且它非常字面意义上地作为您的 bind 语句的结果。
【讨论】:
虽然是这样,但我不想绑定到 127.0.0.1(或任何其他特定 IP 地址)。如果我不清楚,我深表歉意,但理想情况下,我想在具有多个网络适配器(监听所有网络适配器)的机器上运行这个 UDP 服务器,并且我想知道数据包被发送到哪一个。【参考方案4】:从该套接字获取地址的一种方法是将其连接到发送者。 完成此操作后,您将能够获取本地地址(或至少一个可路由到发件人的地址),但是,您将只能从连接的端点接收消息。
要取消连接,您需要再次使用 connect,这次指定一个包含 AF_UNSPEC
家族的地址。不幸的是,我不知道如何在 C# 中实现这一点。
(免责声明:我从未编写过一行 C#,这通常适用于 Winsock)
【讨论】:
【参考方案5】:亚当
这个没测试过……我们试试
private void DoReceiveFrom(IAsyncResult iar)
//Get the received message.
Socket recvSock = (Socket)iar.AsyncState;
//EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
Socket clientEP = recvSock.EndAccept(iar);
int msgLen = recvSock.EndReceiveFrom(iar, ref clientEP);
byte[] localMsg = new byte[msgLen];
Array.Copy(buffer, localMsg, msgLen);
//Start listening for a new message.
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
udpSock.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, udpSock);
//Handle the received message
/*
Console.WriteLine("Recieved 0 bytes from 1:2 to 3:4",
msgLen,
((IPEndPoint)recvSock.RemoteEndPoint).Address,
((IPEndPoint)recvSock.RemoteEndPoint).Port,
((IPEndPoint)recvSock.LocalEndPoint).Address,
((IPEndPoint)recvSock.LocalEndPoint).Port);
//Do other, more interesting, things with the received message.
*/
Console.WriteLine("Recieved 0 bytes from 1:2 to 3",
msgLen,
((IPEndPoint)recvSock.RemoteEndPoint).Address,
((IPEndPoint)recvSock.RemoteEndPoint).Port,
clientEP.RemoteEP.ToString();
【讨论】:
谢谢,但我很确定这段代码不会工作,甚至无法编译......如果没有第一个开始(Socket.EndAccept),你不能结束异步接受调用(Socket.EndAccept)。 BeginAccept),并且在无连接套接字上接受没有意义。此外,Socket.EndReceiveFrom 调用需要对 EndPoint 的引用,而不是 Socket。以上是关于C# Socket 如何完全发送/接收数据的主要内容,如果未能解决你的问题,请参考以下文章
UDP 协议 C# UdpClient乱序接收数据包丢失的问题 Socket ReceiveBufferSize
通过套接字从 c# 向节点 socket.io 服务器发送数据