一个TCP服务端,接入多个客户端,之间通信的问题
如何在一个服务端里,实现其他客户端之间的数据的广播,也就是说,有一个客户端发送消息,其他客户端都能收到这条数据
最便捷的方式,就是将每一个接入的客户端都保存到一个list列表中,然后通过遍历的方式,将数据发送给出自己以外的socker,
值得注意的是,在通过线程,进行单独的数据接收和发送的时候,不要用直接用list[下标]的方式进行数据发送,否则在断开连接之后,清除该对象,那么其他的socker
下标就乱了。所以一定要接收到客户端的请求之后,加入列表,然后在接收发送线程中立马把对用的socker对象取出来,断开连接之后一定要在合适的地方将线程break;否则卡死
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Reflection;
namespace Linux_TCP_Server
{
class Program
{
//const int cycleNum = 10;
//static void Main(string[] args)
//{
// ThreadPool.SetMinThreads(1, 1);
// ThreadPool.SetMaxThreads(5, 5);
// for (int i = 1; i <= cycleNum; i++)
// {
// ThreadPool.QueueUserWorkItem(new WaitCallback(testFun), i.ToString());
// }
// Console.WriteLine("主线程执行!");
// Console.WriteLine("主线程结束!");
// Console.ReadKey();
//}
//public static void testFun(object obj)
//{
// Console.WriteLine(string.Format("{0}:第{1}个线程,当前线程为{2}",
// DateTime.Now.ToString(), obj.ToString(),
// Thread.CurrentThread.ManagedThreadId.ToString()));
// Thread.Sleep(1000);
//}
/*
* 实现原理:
* 服务器作为一个服务端,4G模块和PC上位机作为客户端
* 4G模块是一直连接在服务器上,PC则是不一定,当需要的时候,连接服务器,像4G模块发起更新
* 服务器将双方的数据,进行转接
* 不采用服务器直接端口转发的原因在于:
* 如果采用端口转发,那就意味着,PC这边,是要有一个架设在电脑上的服务端,这个本地服务端,通过端口转发,将本地端口映射到
* 互联网上,也就是服务器上。
* 然后,4G模块,连接服务器的端口,就相当于访问本地pc的端口,这样的劣势在于,如果断开pc的服务端,那么4g模块就断开了
*
*
*
*/
static List<Socket> userList = new List<Socket>();
static TcpListener tcpListener; //创建服务端对象
static Socket socket; //创建socket对象
static TcpClient newClient; //客户端对象
static Socket mysocket;
// static String IP = "10.1.1.69"; //主机IP
static String IP = "192.168.1.4"; //主机IP
static int Port = 4445; //主机端口号
static char[] Master_4G = { 'M', 'A' }; //4G模块的标志
static String Master_4G_IP = null; //4G模块的IP地址
static int Master_4G_Port = 0; //4G模块IP的端口号
static int Master_4G_number = 0; //4G模块连接时的在列表中的下标
static int Master_4G_Stop = 0; //4G模块断开连接标志
static char[] PC = { 'P', 'C' }; //PC上位机的标志
static String PC_IP = null; //PC上位机的IP地址
static int PC_Port = 0; //PC上位机IP对应的端口号
static int PC_number = 0; //PC连接时的在列表中的下标
static int PC_Stop = 0; //PC断开连接标志
static int num = 0;
static int stop_flag = 0;
static int PC_Flag = 0; //表示PC连接成功
static int Master_4G_Flag = 0; //表示4G模块连接成功
static Socket PC_Socket;
static Socket Master_4G_Socket;
static byte SOH = 0X01;
static byte STX = 0X02;
static byte EOT = 0X04;
static void Main(string[] args)
{
//IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName());
//foreach (var ip in ipEntry.AddressList)
//{
// Console.WriteLine("IP Address: " + ip.ToString());
//}
//Console.WriteLine("请输入当前服务器的IP,以回车键结束");
//IP = Console.ReadLine();
//// Console.WriteLine("当前输入的IP:"+IP);
//// Console.WriteLine();
//Console.WriteLine("请输入开放的端口号,以回车键结束");
//Port = Convert.ToInt32(Console.ReadLine());
////Console.WriteLine("当前输入的端口号:" + Port);
////Console.WriteLine();
TCP_Server(); //创建服务端监听
Console.ReadKey();
}
/// <summary>
/// 创建一个TCP服务器,
/// </summary>
static void TCP_Server()
{
Console.WriteLine("创建中。。。");
try
{
tcpListener = new TcpListener(IPAddress.Parse(IP), Port);
//tcpListener = new TcpListener(IPAddress.Parse("185.77.225.13"), 2333);
tcpListener.Start();
//创建一个线程,等待客户端连接
Console.WriteLine("创建成功");
WaitConnect();
//Thread WC_Thread = new Thread(WaitConnect);
//WC_Thread.Start();
}
catch (Exception)
{
Console.WriteLine("地址或端口错误");
}
}
void test()
{
Console.WriteLine("回调");
}
/// <summary>
/// 创建等待客户端连接的线程,只要有客户端连接,就创建一个socke对象,并加入列表
/// </summary>
static void WaitConnect()
{
while (true)
{
try
{
socket = tcpListener.AcceptSocket(); //等待连接,阻塞式
userList.Add(socket); //添加到客户端列
//Console.WriteLine("当前客户端IP:" + userList[num].RemoteEndPoint.ToString().Split(':')[0]);
//Console.WriteLine("当前客户端Port:" + userList[num].RemoteEndPoint.ToString().Split(':')[1]);
//创建一个接收数据的线程
//Thread Receive_Thread = new Thread( ReceiveDate );
//Receive_Thread.Start();
ThreadStart threadStart = new ThreadStart(Change_Data);
threadStart.BeginInvoke(null, null);
num++;
Console.WriteLine("当前连接次数:" + userList.Count);
}
catch (Exception)
{
Console.WriteLine("客户端连接错误!!");
}
}
}
static void Change_Data()
{
byte[] receive = new byte[256];
int Receive_n = 0;
int Index = userList.Count - 1;
Socket socket = userList[Index]; //直接取出对象,否则用列表的下标对象,否则在其他对象删除的时候,下标就乱了
Console.WriteLine("当前客户端IP:" + userList[Index].RemoteEndPoint.ToString().Split(':')[0]);
Console.WriteLine("当前客户端Port:" + userList[Index].RemoteEndPoint.ToString().Split(':')[1]);
while (true)
{
Receive_n = socket.Receive(receive);
//IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("255,255,255,255"), Port);
if (Receive_n > 0)
{
try
{
for (int i = 0; i < userList.Count; i++)
{
if (socket != userList[i])
{
userList[i].Send(receive, Receive_n, SocketFlags.None);
}
}
}
catch (Exception)
{
break;
}
}
else
{
Console.WriteLine("客户端下标:{0}断开链接", Index);
userList.Remove(socket); //移除的时候,用对象,不用下标,否则多次连接会找出连接失败
//if (userList[Index] == null) {
// Console.WriteLine("没有对象");
//}
break;
}
}
}
}
}#好好学习!