C# Socket 一个客户端对应多台设备接收数据问题

baidu_23536865 2015-01-12 05:30:28
由于一些特殊情况,现在我不能做成多台客户端去请求一台服务器的方法。

我编写了一个c# Socket 客户端程序 用来收取多台设备的数据,每一台设备大概5分钟发送一次数据包,我接收解析处理并且存储到数据库.

现在有一个问题,我把多台服务端设备的IP地址和端口存放在XML文件中,然后开始用一台测试可以正常,但是用了多台之后,一直就接收数据就处于第一台设备收,是不是我代码哪里错了 第一次做这个

/// <summary>
/// 程序入口
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
//创建一个Socket
foreach (Address addressValue in GetAddress())
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接到指定服务器的指定端口
try
{
socket.Connect(addressValue.IP, addressValue.Port);
Console.WriteLine("{0}:{1}:Conneted...." + "\r\n", addressValue.IP, addressValue.Port);
//实现接受消息的方法
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
Console.Read();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}

/// <summary>
/// 异步接收数据
/// </summary>
/// <param name="ar"></param>
public static void ReceiveMessage(IAsyncResult ar)
{
try
{
var socket = ar.AsyncState as Socket;
var length = socket.EndReceive(ar);

string Ip = (socket.RemoteEndPoint as IPEndPoint).Address.ToString();
int port = (socket.RemoteEndPoint as IPEndPoint).Port;

//分多次接收
byte[] reallData = new byte[length];
Array.Copy(buffer, reallData, reallData.Length);
string data = BitConverter.ToString(reallData) + "\r\n";

StatisticsData(data, Ip, port);

//接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}


我在这里用了一个循环来获取多台设备地址。
...全文
1264 30 打赏 收藏 转发到动态 举报
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
sunpengsheng01 2015-06-04
  • 打赏
  • 举报
回复
引用 12 楼 chengbin0602 的回复:
你这个反向的设计,你第二台服务端和同一个客户端能连接成功吗?这么做会冲突的。一个客户端同一个实例只会连接一个服务端,不可能一个实例连接两个服务端。没有这样的逻辑。 你若想非要实现这样的设计,你必须用多线程技术,为每一个连接单独创建一个实例。
我也遇到过 就是线程不好控制,我想请问一下 怎么利用线程技术实现这个功能,越详细越好 谢谢了!
zz997788 2015-01-18
  • 打赏
  • 举报
回复
关注一下, 一个客服端对多个服务器端,我也遇到过。
全体起立 2015-01-14
  • 打赏
  • 举报
回复
如果你不再钻牛角尖,其实你的问题已经解决了。要么在服务端修改为一次性发送数据,要么服务端将每个分割部分做连接标记,接收端根据连接规则合并数据。至于同时接收多台服务端数据的问题,最简单的办法就是同时创建多个线程,再存储到数据库的方法中加锁,必须确保同时只有一个线程操作数据库。 或者采用注册事件的方法委托给一个方法操作数据库。
全体起立 2015-01-14
  • 打赏
  • 举报
回复
不知道你服务端发送的数据有多大,如果有几百兆甚至几个G,那么着实需要分段发送来缩短传送时间。 如果数据量不是很大,就算几十M对于TCP连接来说,数据的传输稳定性是不用担心的。 现在你的服务器发送数据情况来看,分段并放入循环去异步发送数据,一是发送的每段的顺序和接收顺序你无法保证,容易导致拼接时数据错误无法使用。 所以建议发送的数据尽量一次性完成而在接收方采用缓冲区分段接收。如果非要分段发送,你必须把服务端每段发送的数据都要做文件名和顺序标记,否则接收方无法判断接收到的是什么东西,或者使用时间戳进行标记,加上发送一个额外的连接数据包,记录所有分段的时间戳,服务端接收数据时实时去连接包中去验证,直到所有数据都接受完成再进行数据连接。 你可要知道,你一旦分段发送数据,在接收方每次异步接收到的数据之间将不再有任何联系,你必须人为的根据一个验证规则去识别哪些接收到的数据其实应该组合在一起。而且必须要同时接收一个验证规则去连接想要连接的数据。如果没有规则,接收端会胡乱的去合并数据的。
全体起立 2015-01-14
  • 打赏
  • 举报
回复
肯刻苦钻研是好事。但也别太钻牛角尖,否则太耽误时间,还不一定解决问题。 针对你的问题,你这里//Array.Copy(buffer, reallData, reallData.Length);这句显得一点意义都没有。 如果想实现真正的分包发送,你要在数据进入异步发送前分割,然后把段数据加入一个序号标记,然后放入一个循环中,分别进行异步发送。接收端接收得到的是多段数据,将其存入内存或者本地文件,再提取每段数据的标记,按照序号进行连接。这就非常复杂了。 修改方案如下:上面那句多余的话代码去掉即可。 buffer 这个缓冲区在 BeginReceive 的异步操作当中,会自动按照你设置的缓冲区大小分段读取数据,直到数据读取完成执行委托的回调函数。你在委托函数里得到的数据就是已经分段读取并合完成后的数据,由于你采用tcp连接,根本无需担心数据传输错误丢包问题,除非是网络中断,无法连接了。
吹风的兔子 2015-01-14
  • 打赏
  • 举报
回复
多个设备 往你的 Socket 发送数据; 你的 Socket 只接收; —— 那么,你的 Socket 就是一个 服务端角色嘛。 —————————————————————————————— 实在不行的话,就用 WebService 吧。 之前,用 WebService 传输过 3G 的电影文件 —— 速度 一点不比 Socket 慢。
sinat_23182153 2015-01-14
  • 打赏
  • 举报
回复
引用 26 楼 chengbin0602 的回复:
如果你不再钻牛角尖,其实你的问题已经解决了。要么在服务端修改为一次性发送数据,要么服务端将每个分割部分做连接标记,接收端根据连接规则合并数据。至于同时接收多台服务端数据的问题,最简单的办法就是同时创建多个线程,再存储到数据库的方法中加锁,必须确保同时只有一个线程操作数据库。 或者采用注册事件的方法委托给一个方法操作数据库。
baidu_23536865 2015-01-13
  • 打赏
  • 举报
回复
引用 13 楼 tcmakebest 的回复:
属于不同连接的数据没有区分开啊,应该定义一个数据结构来存储, 然后在 BeginReceive 的最后一个参数传递.用 ar.AsyncState 取出.不然共用一个buffer数据都混了.数据结构至少应包含 Socket,buffer,reallData 等.
就是这样做的,来区分每一台设备发送的数据,只不过每台发送过来的数据量 不能一次接收完整。以下是完整代码 异步函数中的

 /// <summary>
        /// 异步接收数据
        /// </summary>
        /// <param name="ar"></param>
        public static void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                var socket = ar.AsyncState as Socket; 

                var length = socket.EndReceive(ar);

                //分多次接收
                byte[] reallData = new byte[length];
                //Array.Copy(buffer, reallData, reallData.Length);
                string data = BitConverter.ToString(reallData) + "\r\n";

                StatisticsData(data);

                //接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
tcmakebest 2015-01-13
  • 打赏
  • 举报
回复
属于不同连接的数据没有区分开啊,应该定义一个数据结构来存储, 然后在 BeginReceive 的最后一个参数传递.用 ar.AsyncState 取出.不然共用一个buffer数据都混了.数据结构至少应包含 Socket,buffer,reallData 等.
全体起立 2015-01-13
  • 打赏
  • 举报
回复
你这个反向的设计,你第二台服务端和同一个客户端能连接成功吗?这么做会冲突的。一个客户端同一个实例只会连接一个服务端,不可能一个实例连接两个服务端。没有这样的逻辑。 你若想非要实现这样的设计,你必须用多线程技术,为每一个连接单独创建一个实例。
baidu_23536865 2015-01-13
  • 打赏
  • 举报
回复
引用 10 楼 Z65443344 的回复:
不要胡乱copy,你这不是每次都给覆盖掉了吗 你得记录下每次到底把数据写入数组的哪里到哪里了,下次从后面继续往里写,同时累加总数据长度 或者把byte[]改为List<byte>
搞糊涂了,您就高抬贵手 帮我修改一下这段代码吧。
於黾 2015-01-13
  • 打赏
  • 举报
回复
不要胡乱copy,你这不是每次都给覆盖掉了吗 你得记录下每次到底把数据写入数组的哪里到哪里了,下次从后面继续往里写,同时累加总数据长度 或者把byte[]改为List<byte>
baidu_23536865 2015-01-13
  • 打赏
  • 举报
回复
引用 6 楼 Z65443344 的回复:
不要去除,拿到循环外面去 否则循环执行完,程序不就退出了
为什么我这里用了copy没用呢?我看网上是这样合并的
baidu_23536865 2015-01-13
  • 打赏
  • 举报
回复
回答完 分都给你啊 我可以加分
baidu_23536865 2015-01-13
  • 打赏
  • 举报
回复
引用 6 楼 Z65443344 的回复:
不要去除,拿到循环外面去 否则循环执行完,程序不就退出了
谢谢回答,还有一个问题 麻烦了,看您知道不

               var socket = ar.AsyncState as Socket;
                var length = socket.EndReceive(ar);

                //分多次接收
                byte[] reallData = new byte[length];
                Array.Copy(buffer, reallData, reallData.Length);
                string data = BitConverter.ToString(reallData) + "\r\n";

                StatisticsData(data);
我的一台设备由于数据量比较大,可能一次的数据收取过来的时候,是分了4次左右发送的 那么 var length = socket.EndReceive(ar);这个地方接收的时候 我看了一下 最大是1024 我这里该如何确保一次接收的数据是一个完整的包?该如何合并或者判断 谢谢~
於黾 2015-01-13
  • 打赏
  • 举报
回复
不要去除,拿到循环外面去 否则循环执行完,程序不就退出了
baidu_23536865 2015-01-13
  • 打赏
  • 举报
回复
引用 4 楼 tcmakebest 的回复:
连接了第一台后 Console.Read(); 这样就停住了,需要按回车才能继续啊
去除以后第一台设备也不能读了- -! 我这段代码该如何修改?
tcmakebest 2015-01-13
  • 打赏
  • 举报
回复
既然使用了TCP方式,必须假定不能一次性收全数据包的,这个处理不能省略,因此数据包的开头,结尾标识至少需要其一. 在循环中buffer都没有初始化,那就是共用同一个,问题很严重. 多次接收必然要用一个地方保存,通常用MemoryStream不限制容量.
  • 打赏
  • 举报
回复
但就这一行而言,你要同时连上多个设备,不能在循环中阻塞!大致应该写
 
        static List<Socket> cs = new List<Socket>();

        static void Main(string[] args)
        {
            //创建一个Socket
            foreach (Address addressValue in GetAddress())
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                cs.Add(socket);
                try
                {
                    socket.Connect(addressValue.IP, addressValue.Port);
                    Console.WriteLine("{0}:{1}:Conneted...." + "\r\n", addressValue.IP, addressValue.Port);
                    //实现接受消息的方法
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
               }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            Console.WriteLine("....................按任意键结束");
            Console.Read(); 
        }
  • 打赏
  • 举报
回复
Console.Read(); 是干什么的?
加载更多回复(10)

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧