using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
using System.Net.NetworkInformation;

namespace 黄炎培_服务器
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

/// <summary>
/// 服务器倾听客户端连接线程
/// </summary>
Thread threadWatchs = null;

/// <summary>
/// 服务器套接字
/// </summary>
Socket socketServer = null;

/// <summary>
/// 服务器监控客户端连接情况的线程
/// </summary>
Thread MonitoThread = null;

/// <summary>
/// 客户端ip与套接字的集合
/// </summary>
Dictionary<string, Socket> dictSocket = new Dictionary<string, Socket>();

/// <summary>
/// 客户端ip与线程的集合
/// </summary>
Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();

/// <summary>
/// 客户端连接线程的数量
/// </summary>
long numThreadVal = 0;

/// <summary>
/// 服务器ip
/// </summary>
string strServerIP = "192.168.4.3";

/// <summary>
/// 服务器端口
/// </summary>
int serverPort = 8080;

/// <summary>
/// 缓存数据长度
/// </summary>
int receiveDataValLengt = 1024;//缓存区长度

/// <summary>
/// 用于Ping客户端
/// </summary>
Ping monitoPing = new Ping();

/// <summary>
/// 异常断开的客户端
/// </summary>
Dictionary<string, string> dictBodClient = new Dictionary<string, string>();

/// <summary>
/// 指示释放释放线程
/// </summary>
bool isClearThread = false;

ulong numDataFlow;

private void Form1_Load(object sender, EventArgs e)
{
//开启服务器
openServer(strServerIP, serverPort);
//开启服务器监控线程
MonitoThread = new Thread(monitoThreadsDynamic);
//后台线程
MonitoThread.IsBackground = true;
//启动线程
MonitoThread.Start();
}

/// <summary>
/// 开始实时监控客户端的连接情况
/// </summary>
void monitoThreadsDynamic()
{
delegateShowMseeage("开始实时监控客户端连接情况");
while (true)
{
Thread.Sleep(3000);
try
{
foreach (var vv in dictSocket)
{
PingReply reply = monitoPing.Send(vv.Key.Split(':')[0],1000);
//如果Ping通
if (reply.Status == IPStatus.Success)
{
//表示客户端连接正常
delegateShowMseeage("客户端" + vv.Key + "连接正常");
}
else
{
delegateShowMseeage("客户端" + vv.Key + "连接异常");
//添加异常客户端连接到集合dictBodClient
dictBodClient.Add(vv.Key, "old");

}
}

//释放异常连接的线程
foreach (var vvv in dictThread)
{
isClearThread = false;
foreach (var vvvv in dictBodClient)
{
if (vvv.Key == vvvv.Key)
{
isClearThread = true;
break;
}
}
if (isClearThread)
{
vvv.Value.Abort();
delegateShowMseeage("客户端" + vvv.Key + "占用的线程已释放");
}
}

//从集合合中移除异常连接的客户端
foreach (var vvv in dictBodClient)
{
//从集合中移除客户端套接字
dictSocket.Remove(vvv.Key);
//从集合中移除客户端线程
dictThread.Remove(vvv.Key);
//从列表中移除客户端套接字的远程终结点
deleteClientSocket(vvv.Key);
//跨线程显示提示数据
delegateShowMseeage("客户端" + vvv.Key + "断开连接");
}
}
catch (Exception se)
{
//MessageBox.Show(se.Message);
delegateShowMseeage(se.Message);
}

dictBodClient.Clear();
//获得当前程序运行的线程总数量
numThreadVal = Process.GetCurrentProcess().Threads.Count;
delegateShowMseeage("当前的线程总数量为:" + numThreadVal);
//获得客户端连接所占用的线程数量
numThreadVal = dictThread.LongCount();
//跨线程显示消息
delegateShowMseeage("其中客户端连接的线程数量为:" + numThreadVal);
}
}

/// <summary>
/// 开启服务器
/// </summary>
/// <param name="serverIP"></param>
/// <param name="serverPort"></param>
void openServer(string serverIP, int serverPort)
{
//实例化服务器套接字
socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//配置网络端点
IPEndPoint ipAndPort = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);
try
{
//设置服务器套接字的连接参数
socketServer.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
//将网络端点绑定到服务器套接字
socketServer.Bind(ipAndPort);
}
catch (SocketException se)
{
MessageBox.Show("异常:" + se.Message);
return;
}
//服务器开始倾听,指定最多客户端数量为10000
socketServer.Listen(10000);
//实例化服务器倾听客户端连接的线程
threadWatchs = new Thread(WatchClientConnecting);
//后台运行线程
threadWatchs.IsBackground = true;
//启动线程
threadWatchs.Start();
//显示提示消息
showMesssge("服务器启动成功");
}

/// <summary>
/// 开始倾听客户端
/// </summary>
void WatchClientConnecting()
{
//跨线程显示提示消息
delegateShowMseeage("服务器开始倾听客户端连接");
while (true)
{
//倾听新的客户端连接请求
Socket newClientConnecting = socketServer.Accept();
//添加客户端套接字的远程终结点到列表
addClientSocket(newClientConnecting.RemoteEndPoint.ToString());
//新建一条新的客户端线程
Thread newClinetThread = new Thread(receiveData);
//后台运行客户端
newClinetThread.IsBackground = true;
//启动线程,并将 新客户端的套接字 绑定到线程执行的方法
newClinetThread.Start(newClientConnecting);
//添加客户端套接字到集合
dictSocket.Add(newClientConnecting.RemoteEndPoint.ToString(), newClientConnecting);
//添加客户端接受数据的线程到集合
dictThread.Add(newClientConnecting.RemoteEndPoint.ToString(), newClinetThread);
//跨线程显示提示信息
delegateShowMseeage("新客户端:" + newClientConnecting.RemoteEndPoint.ToString());
}
}

/// <summary>
/// 接受数据
/// </summary>
/// <param name="socketConnecting"></param>
void receiveData(object socketConnecting)
{
//获取该线程绑定的客户端套接字
Socket socketClient = socketConnecting as Socket;
while (true)
{
//新建一个缓存区
byte[] receiveDataVal = new byte[receiveDataValLengt];
//数据长度
int receiveDataLength = -1;
try
{
//接受数据填入缓存区并获得数据长度
receiveDataLength = socketClient.Receive(receiveDataVal);
//接受到数据
if (receiveDataLength > 0)
{
//跨线程显示接受到的数据
delegateShowReceiveData(socketClient, receiveDataVal, receiveDataLength);
//综合处理接受到的数据
receiveDataIntegratedProcessing(receiveDataVal, receiveDataLength);
numDataFlow += (uint)receiveDataLength;
showDataFlow();
}
else
{
//从集合中移除客户端套接字
dictSocket.Remove(socketClient.RemoteEndPoint.ToString());
//从集合中移除客户端线程
dictThread.Remove(socketClient.RemoteEndPoint.ToString());
//从列表中移除客户端套接字的远程终结点
deleteClientSocket(socketClient.RemoteEndPoint.ToString());
//跨线程显示提示数据
delegateShowMseeage("客户端" + socketClient.RemoteEndPoint.ToString() + "断开连接");
//释放该线程
return;
}
}
catch
{
//从集合中移除客户端套接字
dictSocket.Remove(socketClient.RemoteEndPoint.ToString());
//从集合中移除客户端线程
dictThread.Remove(socketClient.RemoteEndPoint.ToString());
//从列表中移除客户端套接字的远程终结点
deleteClientSocket(socketClient.RemoteEndPoint.ToString());
//跨线程显示提示数据
delegateShowMseeage("异常:" + "客户端" + socketClient.RemoteEndPoint.ToString() + "断开连接");
//释放该线程
return;
}
}
}

/// <summary>
/// 综合处理接受到的数据
/// </summary>
/// <param name="datas"></param>
/// <param name="length"></param>
void receiveDataIntegratedProcessing(byte[] datas, int length)
{
//if (length == 5)
//{
// if (datas[0] == 0x11 && datas[4] == 0x11)
// {
// foreach (var clients in dictSocket)
// {
// newOneThreadSendDataToClient(clients.Value, datas, length);
// }
// }
// else if (datas[0] == 0x12 && datas[4] == 0x12)
// {
// foreach (var clients in dictSocket)
// {
// newOneThreadSendDataToClient(clients.Value, datas, length);
// }
// }
//}

//*******************************************************************
//将接受的数据发送给所有客户端
//*******************************************************************
//遍历客户端套接字的集合
foreach (var clients in dictSocket)
{
//新建一条线程发送数据到客户端
newOneThreadSendDataToClient(clients.Value, datas, length);
}
}

void newOneThreadSendDataToClient(Socket clientSocket, byte[] datas, int length)
{
//跨线程显示提示消息
delegateShowMseeage("新建一条线程准备开始发送数据");
//新建发送数据的参数模型
dataSendArgsMode sendDataArgs = new dataSendArgsMode();
//将客户端套接字绑定到模型
sendDataArgs.sockets = clientSocket;
//将数据绑定到模型
sendDataArgs.datas = datas;
//将数据长度绑定到模型
sendDataArgs.length = length;
//新建发送数据到客户端的线程
Thread threadSendDataToClient = new Thread(sendDataToClient);
//后台运行线程
threadSendDataToClient.IsBackground = true;
//启动线程,并将 发送数据的参数模型 绑定到线程执行的方法
threadSendDataToClient.Start(sendDataArgs);

}

/// <summary>
/// 发送数据到客户端
/// </summary>
/// <param name="obj"></param>
void sendDataToClient(object obj)
{
//获取用于发送数据的参数模型
dataSendArgsMode args = obj as dataSendArgsMode;

try
{
//从数据参数模型中提取数据发送到模型中的客户端
args.sockets.Send(args.datas, 0, args.length, SocketFlags.None);
//跨线程显示提示消息
delegateShowMseeage("数据:" + getStringFormByte(args.datas, args.length) + "发送到了客户端:" + args.sockets.RemoteEndPoint.ToString());
delegateShowMseeage("数据发送完毕,关闭线程");
//释放该线程
numDataFlow += (uint)args.length;
showDataFlow();
return;
}
catch
{
//从集合中移除客户端套接字
dictSocket.Remove(args.sockets.RemoteEndPoint.ToString());
//从集合中移除客户端线程
dictThread.Remove(args.sockets.RemoteEndPoint.ToString());
//从列表中移除客户端套接字的远程终结点
deleteClientSocket(args.sockets.RemoteEndPoint.ToString());
//跨线程显示提示消息
delegateShowMseeage("异常:" + "客户端" + args.sockets.RemoteEndPoint.ToString() + "断开连接" + ",关闭该线程");
//释放该线程
return;
}

}

/// <summary>
/// 从列表中删除客户端
/// </summary>
/// <param name="socket"></param>
void deleteClientSocket(string strClientSocket)
{
//封装一个方法进行委托
Action<string> actionDelegate = (x) =>
{
//从列表中移除指定客户端套接字的远程终结点
lbOnlineClient.Items.Remove(x.ToString());
};
//将参数委托到指定方法执行
txtMessage.Invoke(actionDelegate, strClientSocket);
}

/// <summary>
/// 添加客户端到列表
/// </summary>
/// <param name="clientSocket"></param>
void addClientSocket(string strClientSocket)
{
//封装一个方法进行委托
Action<string> actionDelegate = (x) =>
{
//向列表中添加指定客户端套接字的远程终结点
lbOnlineClient.Items.Add(x.ToString());
};
//将参数委托到指定方法执行
txtMessage.Invoke(actionDelegate, strClientSocket);
}

/// <summary>
/// 跨线程显示接受到的数据
/// </summary>
/// <param name="socket"></param>
/// <param name="datas"></param>
/// <param name="length"></param>
void delegateShowReceiveData(Socket socket, Byte[] datas, int length)
{
//封装一个方法进行委托
Action<string> actionDelegate = (x) =>
{
//向文本框追加文本
txtMessage.AppendText(System.DateTime.Now.ToString()+ " -> "+ x.ToString() + "\r\n");
txtMessage.ScrollToCaret();
};
//将参数委托到指定方法执行
txtMessage.Invoke(actionDelegate, "收到来自 " + socket.RemoteEndPoint.ToString() + " 的数据:" + getStringFormByte(datas, length));
}

/// <summary>
/// 跨线程显示数据
/// </summary>
/// <param name="message"></param>
void delegateShowMseeage(string message)
{
//封装一个方法进行委托
Action<string> actionDelegate = (x) =>
{
if (txtMessage.Text.Length > 2000000)
txtMessage.Text = string.Empty;
//向文本框追加文本
txtMessage.AppendText(System.DateTime.Now.ToString()+" -> " + x.ToString() + "\r\n");
txtMessage.ScrollToCaret();
};
//将参数委托到指定方法执行
txtMessage.Invoke(actionDelegate, message);

}

/// <summary>
/// 显示数据流量
/// </summary>
void showDataFlow()
{
//封装一个方法进行委托
Action<string> actionDelegateShowFlow = (x) =>
{
lblDataFlow.Text = x.ToString();
};
//将参数委托到指定方法执行
lblDataFlow.Invoke(actionDelegateShowFlow, numDataFlow.ToString());
}

/// <summary>
/// 显示数据
/// </summary>
/// <param name="message"></param>
void showMesssge(string message)
{
//向文本框追加文本
txtMessage.AppendText(System.DateTime.Now.ToString() + " -> " + message + "\r\n");
txtMessage.ScrollToCaret();
}

/// <summary>
/// 16进制的字符串形式
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
string getStringFormByte(byte[] data, int length)
{
string str = "";
for (int i = 0; i < length; i++)
{
str += data[i].ToString("X").PadLeft(2, '0') + ' ';
}
return str;
}
}

/// <summary>
/// 发送数据的参数
/// </summary>
class dataSendArgsMode
{
/// <summary>
/// 套接字
/// </summary>
public Socket sockets;

/// <summary>
/// 数据
/// </summary>
public byte[] datas;

/// <summary>
/// 长度
/// </summary>
public int length;
}
}

c#tcp多线程服务器实例代码的更多相关文章

  1. C# TCP多线程服务器示例

    前言 之前一直很少接触多线程这块.这次项目中刚好用到了网络编程TCP这块,做一个服务端,需要使用到多线程,所以记录下过程.希望可以帮到自己的同时能给别人带来一点点收获- 关于TCP的介绍就不多讲,神马 ...

  2. 【unix网络编程第三版】阅读笔记(四):TCP客户/服务器实例

    本篇博客主要记录一个完整的TCP客户/服务器实例的编写,以及从这个实例中引发的对僵死进程的处理等问题. 1. TCP客户/服务器功能需求 本实例完成以下功能: (1) 客户从标准输入读入一行文本,并写 ...

  3. 套接字TCP控制台服务器程序代码示范

    套接字TCP控制台服务器程序代码示范  https://blog.csdn.net/txwtech/article/details/90344081

  4. UDP和多线程服务器

    UDP: UDP是数据报文传输协议,这个传输协议比较野蛮,发送端不需要理会接收端是否存在,直接就发送数据,不会像TCP协议一样建立连接.如果接收端不存在的话,发送的数据就会丢失,UDP协议不会去理会数 ...

  5. java多线程编程实例

    [转]这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下. 1.三个售票窗口同时出售20张票程序分析:   ...

  6. TCP粘包/拆包 ByteBuf和channel 如果没有Netty? 传统的多线程服务器,这个也是Apache处理请求的模式

    通俗地讲,Netty 能做什么? - 知乎 https://www.zhihu.com/question/24322387 谢邀.netty是一套在java NIO的基础上封装的便于用户开发网络应用程 ...

  7. TCP通信 - 服务器开启多线程与read()导致服务器阻塞问题

    TCP通信的文件上传案例 本地流:客户端和服务器和本地硬盘进行读写,需要使用自己创建的字节流 网络流:客户端和服务器之间读写,必须使用Socket中提供的字节流对象 客户端工作:读取本地文件,上传到服 ...

  8. TCP通信的客户端代码实现和TCP通信的服务器代码实现

    TCP通信的客户端代码实现 package com.yang.Test.ServerStudy; import java.io.*; import java.net.Socket; /** * TCP ...

  9. 第四章 基本TCP套接字编程 第五章 TCP客户/服务器程序实例

    TCP客户与服务器进程之间发生的重大事件时间表 TCP服务器 socket() --- bind() --- listen() --- accept() --- read() --- write -- ...

随机推荐

  1. SpringCloud 踩坑之 注册中心绑定端口一直是8080

    今天在启动注册中心服务时,突然端口一直是8080,找了好久一直没找到原因,先看看我有问题的配置 spring: application: name: eureka-server profiles: d ...

  2. 【Linux基础总结】Linux基本命令

    Linux基本命令 Linux系统下的文件类型.权限.所属用户与组 文件类型 - (文件) d(目录) ->类似windows系统下的文件夹 l (链接) ->类似windows系统下的快 ...

  3. fork...join的用法

    如果希望在仿真的某一时刻同时启动多个任务,可以使用fork....join语句.例如,在仿真开始的 100 ns 后,希望同时启动发送和接收任务,而不是发送完毕后再进行接收,如下所示: initial ...

  4. hdu5381 The sum of gcd]莫队算法

    题意:http://acm.hdu.edu.cn/showproblem.php?pid=5381 思路:这个题属于没有修改的区间查询问题,可以用莫队算法来做.首先预处理出每个点以它为起点向左和向右连 ...

  5. linux --自已的域名无法登陆机器的解决办法:同步时间

    昨天发现自己的域名无法访问host了,因此我们测试环境便无法安装,显示SSH not connectted ,随后发现时间不同步: 因此以下命令可以实现时间同步: /opt/quest/bin/vas ...

  6. 微信浏览器中禁止下拉出现网页由xxx.xxxxx.com提供,QQ浏览器X5内核提供技术支持这个

    直接上代码 window.onload = function(){ document.body.addEventListener('touchmove', function (e) { e.preve ...

  7. 宝塔webHook自动同步代码的使用

    #!/bin/bashecho ""#输出当前时间date --date='0 days ago' "+%Y-%m-%d %H:%M:%S"echo " ...

  8. nginx均衡负载

    一直在担心session 问题,结果试了2个web 论坛,discuz 和phpbb ,前面用nginx 均衡负载,后端是apache httpd +php ,mysql 用同一个,修改一下confi ...

  9. NetCore项目实战篇07---服务保护之polly

    1.  为什么要用polly 前面的项目中,一个服务调用另一个(Zhengwei.Identity调用Zhengwei.Use.Api)服务时是直接调用的,在这个调用的过程中可能会发生各种瞬态故障,这 ...

  10. 实战!我用 Wireshark 让你“看得见“ TCP

    每日一句英语学习,每天进步一点点: 前言 为了让大家更容易「看得见」 TCP,我搭建不少测试环境,并且数据包抓很多次,花费了不少时间,才抓到比较容易分析的数据包. 接下来丢包.乱序.超时重传.快速重传 ...