Socket 中级篇(三)C# Socket的同步连接和异步连接区别:大规模客服端连接、少开线程、高性能

目录

参考:

一、简介

1.1、提出问题

1.2、问题分析 

1.3、拓展Socket的同步连接与异步连接

1.3.1、什么是同步连接、异步连接

二、Socket同步通信实现

2.1、Server

2.2、Client

三、Socket异步通信实现



参考:

https://www.debugease.com/csharp/1114414.html

一、简介

1.1、提出问题

      服务端明明监听到一个服务端,但是却没有连接成功,如下所示:

服务端的监听源码:

using KeenRay.Common;
using KeenRay.Common.SocketCommu;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Threading;
using System.Xml.Linq;

namespace KeenRay.SocketCommu
{
    public class SocketCommuInit : BindableBase
    {
        #region 属性集

        //服务端
        public Socket Client
        {
            get;
            set;
        }

        public int _receiveFlagValue = 0;
        /// <summary>
        ///  接收标志值
        /// </summary>
        public int ReceiveFlagValue
        {
            get { return _receiveFlagValue; }
            set { SetProperty(ref _receiveFlagValue, value); }
        }

        #endregion

        #region 字段集
        SocketCommuReceive socketCommuReceive = new SocketCommuReceive();
        //服务器
        Socket Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //监听多个Client
        Dictionary<string, Socket> clientList = new Dictionary<string, Socket>();
        //IP
        string strSocketIP = null;
        //端口号
        int point = 40000;
        
        #endregion

        #region 方法集

        #region 单例
        private volatile static SocketCommuInit _instance = null;
        private static readonly object lockobj = new object();

        /// <summary>
        /// 创建实例
        /// </summary>
        /// <returns></returns>
        public static SocketCommuInit CreateInstance()
        {
            if (_instance == null)
            {
                lock (lockobj)
                {
                    if (_instance == null)
                    {
                        _instance = new SocketCommuInit();
                    }
                }
            }
            return _instance;
        }
        #endregion

        #region IP配置
        /// <summary>
        /// IP、端口配置
        /// </summary>
        public void IPandPortConfig()
        {
            try
            {
                string strSocketConfigFilePath;
                strSocketConfigFilePath = Environment.CurrentDirectory + "\\Config" + "\\SocketAndBullHeadConfig.xml";
                XDocument SocketDocument = XDocument.Load(strSocketConfigFilePath);
                XElement SocketRoot = SocketDocument.Root;
                //Socket节点
                XElement SocketPoint = SocketRoot.Element("Socket");
                //IP
                XElement SocketIP = SocketPoint.Element("SocketIP");
                strSocketIP = SocketIP.Value;
                //端口
                XElement SocketPort = SocketPoint.Element("SocketPort");
                point = Convert.ToInt32(SocketPort.Value);
            }
            catch(Exception ex)
            {
                LogHelper.Error("SocketClass.cs::IPandPortConfig() IP/端口配置发生错误:--------------" + ex.ToString());
            }
        }
        #endregion

        #region 启动Socket
        /// <summary>
        /// 启动
        /// </summary>
        public void StartFlag()
        {
            //启动Socket
            string strSocketStartFlag = null;
            string strSocketConfigFilePath;
            strSocketConfigFilePath = Environment.CurrentDirectory + "\\Config" + "\\SocketAndBullHeadConfig.xml";
            XDocument SocketDocument = XDocument.Load(strSocketConfigFilePath);
            XElement SocketRoot = SocketDocument.Root;
            //Socket节点
            XElement SocketPoint = SocketRoot.Element("Socket");
            //启动Socket
            XElement SocketStart = SocketPoint.Element("SocketStart");
            strSocketStartFlag = SocketStart.Value;

            if (strSocketStartFlag == "1")
            {
                IPAddress IP = IPAddress.Parse(strSocketIP);
                IPEndPoint iPEndPoint = new IPEndPoint(IP, point);
                Server.Bind(iPEndPoint);
                Server.Listen(10);
                //开启线程监听
                Thread th = new Thread(ListenFunc);
                th.IsBackground = true;
                th.Start(Server);
            }
        }
        #endregion

        #region 监听
        /// <summary>
        /// 监听
        /// </summary>
        /// <param name="objListen"></param>
        public void ListenFunc(object Server)
        {
            while (true)
            {
                try
                {
                    //客户端
                    Client = ((Socket)Server).Accept();
                    //clientList.Add(Client.RemoteEndPoint.ToString(), Client); //可以监听多个Client
                    Thread threadServer = new Thread(new ParameterizedThreadStart(socketCommuReceive.Receive));
                    threadServer.IsBackground = true;
                    threadServer.Start(Client);
                }
                catch (Exception ex)
                {
                    LogHelper.Error("SocketClass.cs::Listen()监听发错了错误:--------------" + ex.ToString());
                }
            }
        }
        #endregion

        #endregion
    }
}

客户端的连接源码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Threading;
using KeenRayLargePC.Views;
using System.Windows.Interop;
using System.Xml.Linq;
using System.Drawing.Imaging;
using System.Drawing;
using KeenRayLargePC.Common;
using KeenRayLargePC.Config;
using System.ComponentModel;
using System.Windows.Forms.Integration;
using AxKrayImgViewLib;
using KeenRayLargePC.DicomOperate;
using Prism.Commands;
using KeenRayLargePC.PadDataAcessLayer;
using KeenRayLargePC.PadDbContent;
using System.Linq.Expressions;
using KeenRayLargePC.Server;

namespace KeenRayLargePC
{
    public partial class MainWindow : Window,INotifyPropertyChanged
    {
        #region 命令集
        /// <summary>
        /// 窗宽改变命令
        /// </summary>
        public DelegateCommand SliderWWValueChangedCommand { get; private set; }

        /// <summary>
        /// 窗位改变命令
        /// </summary>
        public DelegateCommand SliderWLValueChangedCommand { get; private set; }

        #endregion

        #region 属性集

       

        BitmapImage bmpImage = null;
        public BitmapImage BmpImage
        {
            get
            {
                return bmpImage;
            }
            set
            {
                bmpImage = value;
                RaiseChanged("VideoImage");
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        public void RaiseChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
            }
        }

        private int _winWidth = 1;
        /// <summary>
        /// 窗宽
        /// </summary>
        public int WinWidth
        {
            get { return _winWidth; }
            set
            {
                _winWidth = value;
                if (PropertyChanged != null)
                {
                    //RaisePropertyChanged("PopupEraserMoreIsOpen");
                    PropertyChanged(this, new PropertyChangedEventArgs("WinWidth"));
                }
            }
        }

        private int _winLevel = 1;
        /// <summary>
        /// 窗位
        /// </summary>
        public int WinLevel
        {
            get { return _winLevel; }
            set
            {
                _winLevel = value;
                if (PropertyChanged != null)
                {
                    //RaisePropertyChanged("PopupEraserMoreIsOpen");
                    PropertyChanged(this, new PropertyChangedEventArgs("WinLevel"));
                }
            }
        }

        private bool _popupEraserMoreIsOpen;
        /// <summary>
        /// 清除菜单是否打开
        /// </summary>
        public bool PopupEraserMoreIsOpen
        {
            get { return _popupEraserMoreIsOpen; }
            set
            {
                _popupEraserMoreIsOpen = value;
                if (PropertyChanged != null)
                {
                    //RaisePropertyChanged("PopupEraserMoreIsOpen");
                    PropertyChanged(this, new PropertyChangedEventArgs("PopupEraserMoreIsOpen"));
                }
            }
        }
        #endregion

        #region 标志集

        /*页面标志*/
        const int HOMEPAGE_FLAG = 1;
        const int STUDYPAGE_FLAG = 2;
        const int REVIEWPAGE_FLAG = 3;

        /*曝光参数标志*/
        const int KV_PLUS_FLAG = 11;
        const int KV_REDUCE_FLAG = 12;
        const int MA_PLUS_FLAG = 13;
        const int MA_REDUCE_FLAG = 14;
        const int MS_PLUS_FLAG = 15;
        const int MS_REDUCE_FLAG = 16;
        const int MAS_PLUS_FLAG = 17;
        const int MAS_REDUCE_FLAG = 18;

        const int MAS_MODE_FLAG = 19;
        const int MAMS_MODE_FLAG = 20;
        const int FOCUS_FLAG = 21;
        const int START_EXPOSE_FLAG = 22;//启动曝光按钮
        const int SAVE_EXPOSE_VALUE = 23;//保存曝光参数

        /*导航状态标志*/
        const int OPEN_VIDEO_FLAG = 31;//打开视频监控
        const int CLOSE_VIDEO_FLAG = 32;//关闭视屏监控
        const int PANEL_TEMP_FLAG = 33; //平板温度
        const int HEAT_CAPACITY_FLAG = 34;//热容量
        const int BATTERY_CAPACITY_FLAG = 35;//电池容量   
        const int EXPOSE_STATUS_FLAG = 36;//曝光状态。接收到的内容(int),0:就绪。1:预备。2:曝光。3:禁止。 

        /*其他信息标志*/
        const int DICOM_FLAG = 41;      //Dicom
        const int USER_INFO_FLAG = 42;  //用户信息

        /*状态标志*/
        bool blStartFlag = false;       //Socket启动标志
        bool imageEditFlag = false;     //图像编辑标识
        bool blCameraRecStatue = false; //视频监控开关状态。false:关闭。true:打开。
        bool blImageRecStatue = false;
        bool blCommonRecStatue = false; //其他状态开关标志     
        const string strCameraRecStatue = "CameraRecStatue";
        const string strImageRecStatue = "ImageRecStatue";
        const string strCommonRecStatue = "CommonRecStatue";
        bool blUpdateTimeFlag = true;                                     //是否运行实时更新时间
        bool blCanAnalysisPackageHead = true;                             //是否允许解析包头  
        bool blCanAnalysisPackageContent = true;                          //是否允许解析包内容
        #endregion

        #region 属性集 

        #region 图像显示控件属性
        public bool m_bEditFlag = false;            //编辑标志
        public int m_nGrabAcqStatus;                //0-DR  1-RF  2-SPOT
        public int nWindowLevel;                    //窗位
        public int nWindowWidth;                    //窗宽
        public KrayDicomLib.DicomFile m_cDicomFile;
        ImgViewHandle handle = new ImgViewHandle();
      
        #endregion

        #region 包属性:用来解析包,实现被动接收,如接收Dicom,用户信息
        byte[] bytPageType      = new byte[4];                            //页面类型
        byte[] bytRecFlag       = new byte[4];                            //标志位字节
        byte[] bytContentLong   = new byte[4];                            //长度字节  
        byte[] bytContent       = new byte[0];                            //内容
        int iPageFlag           = -1;                                     //页面类型
        int iRecFlag            = -1;                                     //接收标志
        int iContentLong        = -1;                                     //包内容长度
        int iPathLong           = -1;                                     //Dicom图路径长度
        const int FLAG_LENGTH = 12;                                       //包头长度
        #endregion

        #region Dicom图操作 
        PatientInfoDal patientInfoDal = new PatientInfoDal();
        SeriesInfoDal seriesInfoDal = new SeriesInfoDal();
        ImagesInfoDal imagesInfoDal = new ImagesInfoDal();
        List<PatientInfo> patientInfoList = new List<PatientInfo>();
        List<SeriesInfo> seriesInfoList = new List<SeriesInfo>();
        List<ImagesInfo> imagesInfoList = new List<ImagesInfo>();
        #endregion

        #region 视频监控属性

        List<byte> VideoList = new List<byte>();                    //存放视频帧
        const int BITMAP_IMAGE_LENGTH = 921654;                     //帧字节长度
        byte[] btCurrentBitmapImage = new byte[BITMAP_IMAGE_LENGTH];//当前帧
        byte[] btLastBitmapImage = new byte[BITMAP_IMAGE_LENGTH];   //上一帧

        #endregion

        #region Socket通用属性

        List<byte> bufferList = new List<byte>();                    //缓存队列
        byte[] btRecBuffer = new byte[16* 1000 * 1000];//socket接收缓存区

        int Port = 50000;//端口号
        IPAddress IP = IPAddress.Parse("127.0.0.1");
        IpConfig ipConfig = new IpConfig();
        IPEndPoint serverIPEndPoint = null;
        public Socket Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        Log log = Log.CreateInstance();//日志类
        iPadReceive ipadReceive = iPadReceive.CreateInstance();
        iPadSend ipadSend       = iPadSend.CreateInstance();
        InitPacs initPacs       = InitPacs.CreateInstance();//启动Pacs初始配置。
        SendModality sendModality = SendModality.CreateInstance();

        #endregion

        #endregion

        #region 方法集

        #region 单例
        //public volatile static MainWindow _instance = null;
        //public static readonly object lockobj = new object();

        / <summary>
        / 创建实例
        / </summary>
        / <returns></returns>
        //public static MainWindow CreateInstance()
        //{
        //    if (_instance == null)
        //    {
        //        lock (lockobj)
        //        {
        //            if (_instance == null)
        //            {
        //                _instance = this;
        //            }
        //        }
        //    }
        //    return _instance;
        //}
        #endregion

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public MainWindow()
        {
            //InitializeComponent();
            log.ClearLogFunc();//清除日志
            ipadReceive.setMainWindowDeleaget(this);
            ipadSend.setMainWindowDeleaget(this);
            //更新UI: 可以用
            App.Current.Dispatcher.Invoke((Action)delegate ()
            {
                this.DataContext = this;
            });
            //窗宽改变命令初始化
            SliderWWValueChangedCommand = new DelegateCommand(SliderWWValueChangedFun);
            //窗位改变命令初始化
            SliderWLValueChangedCommand = new DelegateCommand(SliderWLValueChangedFun);

            m_nGrabAcqStatus = 0;
            nWindowWidth = 65535;
            nWindowLevel = 32768;

            //获取配置文件默认的IP、Port
            ipConfig.IpConfigFunc(ref blStartFlag, ref IP, ref Port);
            StartFunc();//默认就启动Socket连接PC

            /* 线程更新时间 */
            Thread UpdateTimeThread = new Thread(UpdateTime);
            UpdateTimeThread.IsBackground = true;
            UpdateTimeThread.Start();     
        }

       

        /// <summary>
        /// 启动
        /// </summary>
        private void StartFunc()
        {
            try
            {
                if (blStartFlag == true)
                {
                    serverIPEndPoint = new IPEndPoint(IP, Port);
                    //获得要连接的远程服务器应用程序的IP地址和端口号
                    Client.Connect(serverIPEndPoint);
                    if (Client.Connected == true)
                    {
                        log.WriteDebugLogFunc("MainWindow.xaml.cs::StartFunc()——" + "已连接服务器" + "\n\n");
                        MessageBox.Show("连接服务器状态:成功");
                        /*开线程接收*/
                        Thread threadReceive = new Thread(ipadReceive.Receive);
                        threadReceive.IsBackground = true;
                        threadReceive.Start(Client);

                        /*开线程发送归档状态信息*/
                        Thread thSendModality = new Thread(sendModality.SendImageArchiveFunc2);
                        thSendModality.IsBackground = true;
                        thSendModality.Start();
                    }
                    else
                    {
                        log.WriteDebugLogFunc("MainWindow.xaml.cs::StartFunc()——" + "服务器已经开启,但无法连接服务器" + "\n\n");
                    }
                }
            }
            catch (Exception ex)
            {
                //MessageBox.Show("服务器未开启或客户端输入的IP不正确");
                log.WriteErrorLogFunc("MainWindow.xaml.cs::StartFunc()——" + "服务器未开启或客户端输入的IP不正确:" + ex.ToString() + "\n\n");
            }
        }

        /// <summary>
        /// 导图:iPads导图到Pacs
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SendPacs_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Pacs pacsDialog = new Pacs();
                pacsDialog.SetValidServerList();
                pacsDialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                pacsDialog.ShowDialog();
            }
            catch (Exception ex)
            {
                log.WriteErrorLogFunc("MainWindow.xaml.cs::btn_Image_Click()——" + "视频监控开关状态发送错误:" + ex.ToString() + "\n\n");
            }
        }

        /// <summary>
        /// 关闭系统
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnSysShutDown_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        /// <summary>
        /// 系统配置,弹窗window窗口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSystemConfig_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (Client != null && Client.Connected == true)
                {
                    MessageBox.Show("Pad已经连上PC,无需配置IP/Port了");
                    return;
                }

                SystemConfigWindow systemConfigWindow = new SystemConfigWindow();
                systemConfigWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                if (systemConfigWindow.ShowDialog() == true)
                {
                    IP = IPAddress.Parse(systemConfigWindow.IP);
                    Port = systemConfigWindow.Port;
                    blStartFlag = true;//若是IP配置正确,则启动Socket连接
                }

                StartFunc();//启动Socket连接
            }
            catch(Exception ex)
            {
                log.WriteErrorLogFunc("MainWindow.xaml.cs::btnSystemConfig_Click()——" + "IP格式或端口不正确:" + ex.ToString() + "\n\n");
            }
        }

        #region 更新时间
        /// <summary>
        /// 更新时间
        /// </summary>
        private void UpdateTime()
        {
            while (blUpdateTimeFlag == true)
            {
                Thread.Sleep(1000);
                //更新UI: 可以用
                App.Current.Dispatcher.Invoke((Action)delegate ()
                {
                    this.lblDateTime_Second.Content = DateTime.Now.ToLongTimeString().ToString();
                    this.lblDateTime_day.Content = DateTime.Now.ToString("yyyy-MM-dd");
                });
            }
        }
        #endregion

        #region 发送视频监控

        /// <summary>
        /// 视频监控开关状态
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Camera_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (blCameraRecStatue == false)//如果在关闭状态下,那就打开它。
                {
                    //视频打开,其他功能必须关闭 
                    ModuleRunFlagFunc(strCameraRecStatue);
                    ipadSend.SendPackageFunc(STUDYPAGE_FLAG, OPEN_VIDEO_FLAG, null);
                }
                else if (blCameraRecStatue == true)// 如果在打开状态下,那就关闭它。
                {
                    blCameraRecStatue = false;
                    ipadSend.SendPackageFunc(STUDYPAGE_FLAG, CLOSE_VIDEO_FLAG, null);
                    //关闭视频监控下,必须清空Image的画面
                    App.Current.Dispatcher.Invoke((Action)delegate ()
                    {
                        imgDisPlay.Source = null;
                    });
                }
            }
            catch (Exception ex)
            {
                log.WriteErrorLogFunc("MainWindow.xaml.cs::Camera_Click()——" + "视频监控开关状态发送错误:" + ex.ToString() + "\n\n");
            }
        }

        #endregion

        #region 发送曝光标志
        /// <summary>
        /// 启动曝光
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExpose_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, START_EXPOSE_FLAG, null);
        }

        private void btn_KvPlus_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, KV_PLUS_FLAG, null);
        }

        private void btn_KvReduce_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, KV_REDUCE_FLAG, null);
         }

        private void btn_mAPlus_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, MA_PLUS_FLAG, null);
          }

        private void btn_mAReduce_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, MA_REDUCE_FLAG, null);
         }

        private void btn_msPlus_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, MS_PLUS_FLAG, null);
         }

        private void btn_msReduce_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, MS_REDUCE_FLAG, null);
        }

        private void btn_mAsPlus_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, MAS_PLUS_FLAG, null);
         }

        private void btn_mAsReduce_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, MAS_REDUCE_FLAG, null);
        }

        private void btn_mAsMode_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, MAS_MODE_FLAG, null);
        }

        private void btn_mAmsMode_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, MAMS_MODE_FLAG, null);
        }

        /// <summary>
        /// 焦点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Focus_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, FOCUS_FLAG, null);
        }

        private void btn_SaveExposeValues_Click(object sender, RoutedEventArgs e)
        {
            //其他功能必须关闭 
            ModuleRunFlagFunc(strCommonRecStatue);
            ipadSend.SendPackageFunc(STUDYPAGE_FLAG, SAVE_EXPOSE_VALUE, null);
        }
        #endregion

        /// <summary>
        /// 模块运行标志
        /// </summary>
        /// <param name="str"></param>
        public void ModuleRunFlagFunc(string str)
        {
            switch (str)
            {
                case strCameraRecStatue:
                    blCameraRecStatue = true;
                    blImageRecStatue = false;
                    blCommonRecStatue = false;
                    break;
                case strImageRecStatue:
                    blCameraRecStatue = false;
                    blImageRecStatue = true;
                    blCommonRecStatue = false;
                    //同时需要发一个信息给主程序,关闭视频监控,不让它发信息过来了。
                    ipadSend.SendPackageFunc(STUDYPAGE_FLAG, CLOSE_VIDEO_FLAG, null);
                    //并且让当前的image清零
                    App.Current.Dispatcher.Invoke((Action)delegate ()
                    {
                        this.imgDisPlay.Source = null;
                    });
                    break;
                case strCommonRecStatue:
                    //同时需要发一个信息给主程序,关闭视频监控,不让它发信息过来了。
                    ipadSend.SendPackageFunc(STUDYPAGE_FLAG, CLOSE_VIDEO_FLAG, null);
                    //并且让当前的image清零
                    App.Current.Dispatcher.Invoke((Action)delegate ()
                    {
                        this.imgDisPlay.Source = null;
                    });
                    blCameraRecStatue = false;
                    blImageRecStatue = false;
                    blCommonRecStatue = true;
                    break;
                default:
                    break;
            }
        }

        #region Dicom图

        /// <summary>
        /// 点击加载一张Dicom图:主要用来测试
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OpenDicom_Click(object sender, RoutedEventArgs e)
        {
            try
            {   /*直接打开*/
                //System.Windows.Forms.OpenFileDialog open = new System.Windows.Forms.OpenFileDialog();
                //open.Filter = "DCM图像文件|*.dcm";
                //open.RestoreDirectory = false;
                //if (open.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                //{
                //    handle.PutImage(this.imageview, open.FileName, ref m_cDicomFile);
                //}
                适应窗口
                //imageview.FtsImage();
                //this.imageview.Invalidate();
                隐藏视频监控窗口
                //this.imgDisPlay.Visibility = Visibility.Hidden;
                //this.host.Visibility = Visibility.Visible;

                /*调用ShowDicom打开*/
                System.Windows.Forms.OpenFileDialog open = new System.Windows.Forms.OpenFileDialog();
                open.Filter = "DCM图像文件|*.dcm";
                open.RestoreDirectory = false;
                if (open.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    ipadReceive.ShowDicom(open.FileName);
                }
            }
            catch (Exception ex)
            {
                log.WriteErrorLogFunc("MainWindow.xaml.cs::OpenDicom_Click()——" + "打开dicom图发生错误:" + ex.ToString() + "\n\n");
            }
        }

        /// <summary>
        /// 左旋转
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_LeftRotate_Click(object sender, RoutedEventArgs e)
        {
            this.imageview.LeftRotate();
            this.imageview.Invalidate();
        }

        /// <summary>
        /// 右旋转
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_RightRotate_Click(object sender, RoutedEventArgs e)
        {
            this.imageview.RightRotate();
            this.imageview.Invalidate();
        }

        /// <summary>
        /// 下侧翻
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_DownFlip_Click(object sender, RoutedEventArgs e)
        {
            this.imageview.FlipVertical();
            this.imageview.Invalidate();
        }

        /// <summary>
        /// 右侧翻
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_RightFlip_Click(object sender, RoutedEventArgs e)
        {
            this.imageview.FlipHorizon();
            this.imageview.Invalidate();
        }

        /// <summary>
        /// 等比显示:显示一张图
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_ShowOneImage_Click(object sender, RoutedEventArgs e)
        {
            if (this.imageview.HasImage)
            {
                this.imageview.RealSizeImage();
                this.imageview.Invalidate();
            }
        }

        /// <summary>
        /// 全屏显示
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_FullScreen_Click(object sender, RoutedEventArgs e)
        {
            //适应窗口
            imageview.FtsImage();
            imageview.Invalidate();
        }

        /// <summary>
        /// 手抓移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_AllowMove_Click(object sender, RoutedEventArgs e)
        {
            if (this.imageview.HasImage)
            {
                this.imageview.ProcType = KrayImgViewLib.tagPROC_TYPE.PT_MOVE;
            }
        }

        /// <summary>
        /// 禁止移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_ForbiddenMove_Click(object sender, RoutedEventArgs e)
        {
            if (this.imageview.HasImage)
            {
                this.imageview.ProcType = KrayImgViewLib.tagPROC_TYPE.PT_NONE;
                this.imageview.Invalidate();
            }
        }

        /// <summary>
        /// 右标记
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnR_Click(object sender, RoutedEventArgs e)
        {
            //string sFontName = configInfoModel.RLMarkFontName;
            //int nFontSize = configInfoModel.RLMarkFontSize;
            string sFontName = "微软雅黑";
            int nFontSize = 200;
            if (this.imageview.HasImage)
            {
                this.imageview.DrawFlag = "R";
                this.imageview.AddText(200, 30, "R", nFontSize, sFontName);
                this.imageview.ProcType = KrayImgViewLib.tagPROC_TYPE.PT_NONE;
                this.imageview.Invalidate();
                SetEditFlag(true);
            }
        }

        /// <summary>
        /// 设置编辑标志值
        /// </summary>
        /// <param name="state"></param>
        public void SetEditFlag(bool state)
        {
            imageEditFlag = state;
        }

        /// <summary>
        /// 左标识命令功能函数
        /// </summary>
        private void btnL_Click(object sender, RoutedEventArgs e)
        {
            //string sFontName = configInfoModel.RLMarkFontName;
            //int nFontSize = configInfoModel.RLMarkFontSize;
            string sFontName = "微软雅黑";
            int nFontSize = 200;

            if (this.imageview.HasImage)
            {
                int X2 = this.imageview.Width - 200;
                int Y2 = 30;

                this.imageview.DrawFlag = "L";
                this.imageview.AddText(X2, Y2, "L", nFontSize, sFontName);
                this.imageview.ProcType = KrayImgViewLib.tagPROC_TYPE.PT_NONE;
                this.imageview.Invalidate();
                SetEditFlag(true);
            }
        }

        /// <summary>
        /// 文字标识命令功能函数
        /// </summary>
        private void btnText_Click(object sender, RoutedEventArgs e)
        {
            string sFontName = "微软雅黑";
            int nFontSize = 80;

            if (this.imageview.HasImage)
            {
                this.imageview.SetTextFont(sFontName, nFontSize);
                this.imageview.ProcType = KrayImgViewLib.tagPROC_TYPE.PT_TEXT;
                SetEditFlag(true);
            }
        }

        /// <summary>
        /// 清除更多命令功能函数
        /// </summary>
        private void btnClearMore_Click(object sender, RoutedEventArgs e)
        {
            PopupEraserMoreIsOpen = true;
            //PopupEraserMoreIsOpen = true;
        }

        /// <summary>
        /// 清除所有命令功能函数
        /// </summary>
        private void btnClearAll_Click(object sender, RoutedEventArgs e)
        {
            if (this.imageview.HasImage)
            {
                this.imageview.DeleteAnnotation(true);
                this.imageview.Invalidate();
                SetEditFlag(true);
            }
            PopupEraserMoreIsOpen = false;
        }

        /// <summary>
        /// 清除所选命令功能函数
        /// </summary>
        private void btnClearChoose_Click(object sender, RoutedEventArgs e)
        {
            if (this.imageview.HasImage)
            {
                this.imageview.DeleteAnnotation(false);
                this.imageview.Invalidate();
                SetEditFlag(true);
            }
            PopupEraserMoreIsOpen = false;
        }

        /// <summary>
        /// 反白
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnInvert_Click(object sender, RoutedEventArgs e)
        {
            if (this.imageview.HasImage)
            {
                this.imageview.BlackWhite();
                this.imageview.Invalidate();
                SetEditFlag(true);
            }
        }

        /// <summary>
        /// 自动窗宽窗位
        /// </summary>
        private void btnAutoWL_Click(object sender, RoutedEventArgs e)
        {
            int winWidth = WinWidth;                                             //窗宽
            int winLevel = WinLevel;                                             //窗位

            if (this.imageview.HasImage)
            {
                this.imageview.SetOptimizeWindowLevel();
                this.imageview.GetWindowLevel(out winWidth, out winLevel);

                WinWidth = winWidth;
                WinLevel = winLevel;

                //UpdateWWWLSlider();
                SetEditFlag(true);
            }
        }

        /// <summary>
        /// 窗宽改变命令初始化功能函数
        /// </summary>
        private void SliderWWValueChangedFun()
        {
            if (this.imageview != null)
            {
                if (this.imageview.HasImage)
                {
                    WinWidth = (int)this.SliderWW.Value;
                    RefreshWWWLImageView();
                }
            }
        }

        /// <summary>
        /// 窗位改变命令初始化功能函数
        /// </summary>
        private void SliderWLValueChangedFun()
        {
            if (this.imageview != null)
            {
                if (this.imageview.HasImage)
                {
                    WinLevel = (int)this.SliderWL.Value;
                    RefreshWWWLImageView();
                }
            }
        }

        /// <summary>
        /// 窗宽窗位控件值与直方图更新显示功能函数
        /// </summary>
        private void RefreshWWWLImageView()
        {
            //判断是否有图像,当前绘画非病人信息(后面增加)
            if (this.imageview.HasImage)
            {
                this.imageview.SetWindowLevel(WinWidth, WinLevel);
                this.imageview.Invalidate();
            }
        }

        /// <summary>
        /// 保存图像单击功能函数
        /// </summary>
        private void btnSaveImage_Click(object sender, RoutedEventArgs e)
        {
            string msg = "保存图像";
            SaveImageFun(msg);
        }

        /// <summary>
        /// 保存图像函数
        /// </summary>
        private void SaveImageFun(string msg)
        {
            int imgHeight = 0;                                                         //图像高
            int imgWidth = 0;                                                          //图像宽
            int imgBit = 0;                                                            //图像位
            int imgSize = 0;                                                           //图像大小

            /*图像显示控件无图像,则返回*/
            if (!this.imageview.HasImage)
            {
                //EMessageBox.Show("未选择图像或显示控件无图像!", "提示", EMessageBoxButton.OK, EMessageBoxImage.Information);
                System.Windows.MessageBox.Show("显示控件无图像");
                SetEditFlag(false);
                return;
            }
        }
        #endregion

        #endregion

    }
}


1.2、问题分析 

      仔细考虑了一下上下文,对比了https://blog.csdn.net/xpj8888/article/details/83536880,并单部执行了,发现了是参数类型的问题。Receive(Object obj)应该替换为Receive(object obj),就不会出错了。

      Object是类,而object是类型。具体的区别,可以参考https://docs.microsoft.com/en-us/dotnet/api/system.object?redirectedfrom=MSDN&view=netframework-4.8

1.3、拓展Socket的同步连接与异步连接

       针对1.1问题,我当时考虑、并调试了好久,好在终于发现问题。我一度地怀疑Socket同步与异步的问题。我是一个不言放弃、勇于探索的人。好了,现在花点时间,来学习Socket同步与异步。学习,就要举一反三。

1.3.1、什么是同步连接、异步连接

http://blog.sina.com.cn/s/blog_5d29ee450102vrfg.html

      同步连接:

      比如1.1就是一个同步通信方式,它的工作方式与阻塞优点类似。即,每个服务对象,都要开启一个线程,浪费线程资源,性能差,可接入用户数量少。(但要注意同步通信与阻塞通信不是等同的

      异步连接:参考https://www.debugease.com/csharp/1114414.html

二、Socket同步通信实现

        同步实现,请参考1.1。或者参考下面的完整代码:

2.1、Server

MainWindow.xaml

<Window x:Class="Server.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Server"
        mc:Ignorable="d"
        Title="Server" Height="450" Width="850">

    <Grid Height="400" Width="800">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="6*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <Canvas Grid.Row="0" Background="#FF00FFC0" >
            <Label Content="Server IP" Canvas.Left="19" Canvas.Top="14"/>
            <Label Content="Server Port" Canvas.Left="221" Canvas.Top="14"/>
            <Label Content="Clients IPs/Ports" Canvas.Left="397" Canvas.Top="15"/>
            <TextBox x:Name="txtPort" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="50000" VerticalAlignment="Top" Width="82" Canvas.Left="297" Canvas.Top="15"/>
            <TextBox x:Name="txtIP" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="127.0.0.1" VerticalAlignment="Top" Width="107" Canvas.Left="86" Canvas.Top="15"/>
            <!--<ComboBox x:Name="cboUsers" HorizontalAlignment="Left" VerticalAlignment="Top" Width="150" Height="23" Canvas.Left="505" Canvas.Top="15">
            </ComboBox>-->
        </Canvas>
        <Canvas Grid.Row="1" Background="#FFD3E5C2" >
            <TextBox x:Name="txtContent" HorizontalAlignment="Left" Height="185" TextWrapping="Wrap" VerticalAlignment="Top" Width="375" Canvas.Left="26" Canvas.Top="9"/>
            <Label Content="消息记录" Canvas.Left="337" Canvas.Top="13" Background="#FFDFC5C5"/>
            <TextBox x:Name="txtSendMsg" HorizontalAlignment="Left" Height="87" TextWrapping="Wrap" VerticalAlignment="Top" Width="376" Canvas.Top="205" Canvas.Left="26"/>
            <Label Content="编辑消息" Canvas.Left="339" Canvas.Top="209" Background="#FFDFC5C5" RenderTransformOrigin="0.741,7.92"/>
            <Border BorderBrush="#FF39222B" BorderThickness="2" Height="300" Width="342" Canvas.Left="448" Canvas.Top="-2"/>
            <Border BorderBrush="#FF39222B" BorderThickness="2" Height="43" Width="342" Canvas.Left="448" Canvas.Bottom="0" Canvas.Top="255" Background="#FF7B7BDF"/>
            <Image x:Name="Image_PlayImage" Height="250" Width="326" Canvas.Left="455" Canvas.Top="3"/>
        </Canvas>
        <Canvas Grid.Row="2" Background="#FF00FFC0" >
            <Button x:Name="btnSendMsg" Content="发送消息" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Click="btnSendMsg_Click" Canvas.Left="141" Canvas.Top="19"/>
            <!--<Button x:Name="btnShock" Content="震动" HorizontalAlignment="Left"  VerticalAlignment="Top" Width="57" Click="btnShock_Click" Canvas.Left="266" Canvas.Top="19"/>-->
            <Button x:Name="btnSendFile" Content="发送文件" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Click="btnSendFile_Click" Canvas.Left="629" Canvas.Top="19"/>
            <Button x:Name="btnChooseFile" Content=" 选择文件" HorizontalAlignment="Left" VerticalAlignment="Top" Width="55" Click="btnChooseFile_Click" Canvas.Left="528" Canvas.Top="19"/>
            <TextBox x:Name="txtFilePath" HorizontalAlignment="Left" Height="27" TextWrapping="Wrap" VerticalAlignment="Top" Width="330" Canvas.Left="455" Canvas.Top="-36"/>
            <Button x:Name="BtnSysShutDown" Content="退出" BorderThickness="2" Click="BtnSysShutDown_Click"  Height="41" Width="71" Background="#FFA31D77" Canvas.Left="2" Canvas.Top="4" ></Button>

        </Canvas>
    </Grid>
</Window>

MainWindow.xaml.cs

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace Server
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        string Receive_IPandPort = "";
        public MainWindow()
        {
            InitializeComponent();
            //当点击开始监听的时候,在服务器端创建一个负责监听IP地址跟端口号的Socket
            Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse("127.0.0.1");//IPAddress.Any;
                                                        //创建端口号对象
            IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
            //监听
            socketWatch.Bind(point);
            ShowMsg("监听成功");
            socketWatch.Listen(10);

            Thread th = new Thread(Listen);
            th.IsBackground = true;
            th.Start(socketWatch);
        }


        public delegate void delegate1(string str);//定义委托
        public delegate int delegate2(string endPoint);

        Socket Server;
        //将远程连接的客户端的IP地址和Socket存入集合中
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();
        /// <summary>
        /// 等待客户端的连接,并且创建与之通信的Socket
        /// </summary>
        /// <param name="o"></param>
        void Listen(object o)
        {
            Socket socketWatch = o as Socket;
            //等待客户端的连接,并且创建一个负责通信的Socket
            while (true)
            {
                try
                {
                    //负责跟客户端通信的Socket
                    Server = socketWatch.Accept();
                    //将远程连接的客户端的IP地址和Socket存入集合中
                    dicSocket.Add(Server.RemoteEndPoint.ToString(), Server);
                    //将远程连接的客户端的IP地址和端口号存储下拉框中
                    //cboUsers.Items.Add(Server.RemoteEndPoint.ToString());
                    //Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate2(cboUsers.Items.Add), Server.RemoteEndPoint.ToString());
                    Receive_IPandPort = Server.RemoteEndPoint.ToString();
                    //192.168.0.100:连接成功
                    Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), Server.RemoteEndPoint.ToString() + ":" + "连接成功");
                    //开启一个新的线程,不停地接收客户端发送过来的消息
                    Thread th = new Thread(Receive);
                    th.IsBackground = true;
                    th.Start(Server);
                }
                catch { }
            }
        }



        /// <summary>
        /// 服务端不停地接收客户端发送过来的消息
        /// </summary>
        /// <param name="o"></param>
        void Receive(object o)
        {
            Socket Server = o as Socket;
            while (true)
            {
                try
                {
                    //客户端连接成功后,服务器应该接收服务器应该接收客户端发来的消息
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    //实际接收到的有效字节数
                    int r = Server.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    else if (buffer[0] == 0)
                    {
                        string str = Encoding.UTF8.GetString(buffer, 1, r-1);
                        Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[接收]" + Server.RemoteEndPoint.ToString() + ":" + str);
                    }
                    else if (buffer[0] == 1)
                    {
                        string savePath;
                        byte[] fileContext = new byte[0];
                        GetSavePathAndFileContextByte(buffer, out savePath, out fileContext); //读取从客户端传输过来的文件名、文件内容
                        System.IO.File.WriteAllBytes(savePath, fileContext);//将内容写入到制定的路径。
                        //MessageBox.Show("保存图片成功");
                        ShowImage(savePath);
                        string fileName = System.IO.Path.GetFileName(savePath);
                        Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[接收]" + Server.RemoteEndPoint.ToString() + ":" + "接收的图片或文件:" + fileName);
                    }
                    else if (buffer[0] == 2)
                    {
                        //Shock();
                    }

                }
                catch
                {
                    //MessageBox.Show("你接收的是非法文件或非法消息");
                }
            }
        }

        private void ShowMsg(string str)
        {
            txtContent.AppendText(str + "\r\n");
        }

        private void ShowImage(string path)
        {
            this.Dispatcher.BeginInvoke((Action)delegate ()//异步委托,直接更新UI线程
            {
                //string fileName = System.IO.Path.GetFileName(path);
                //Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[接收]" + Client.RemoteEndPoint.ToString() + ":" + "接收的图片或文件:" + fileName);
                string ReceiveFileNameProperty_str = System.IO.Path.GetExtension(path);    //获取后缀。
                //必须判断,是图片执行,并显示。
                if ((ReceiveFileNameProperty_str == ".jpg")
                    || (ReceiveFileNameProperty_str == ".png")
                    || (ReceiveFileNameProperty_str == ".bmp")
                    || (ReceiveFileNameProperty_str == ".jpeg")
                    || (ReceiveFileNameProperty_str == ".JPG")
                    || (ReceiveFileNameProperty_str == ".PNG")
                    || (ReceiveFileNameProperty_str == ".BMP")
                    || (ReceiveFileNameProperty_str == ".JPEG"))
                {
                    Image_PlayImage.Source = new BitmapImage(new Uri(path, UriKind.Absolute));//绝对路径
                }
                //btn_StartServer.Content = "接收完成";
            }
          );
        }

        /// <summary>
        /// 服务器给客户端发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendMsg_Click(object sender, RoutedEventArgs e)
        {
            string str = txtSendMsg.Text.Trim();
            byte[] buffer = Encoding.UTF8.GetBytes(str);
            List<byte> list = new List<byte>();
            list.Add(0);
            list.AddRange(buffer);
            //将泛型集合转换为数组
            byte[] newBuffer = list.ToArray();
            //获得用户在下拉框中选中的IP地址
            //string ip = cboUsers.SelectedItem.ToString();
            //dicSocket[ip].Send(newBuffer);
            dicSocket[Receive_IPandPort].Send(newBuffer);
            txtSendMsg.Text = "";
            //发送时,也要在消息显示发送的消息。
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[发送]" + Server.RemoteEndPoint.ToString() + ":" + str);
        }

        /// <summary>
        /// 选择要发送的文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnChooseFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.InitialDirectory = "C:\\Users\\Administrator\\Desktop\\Server发";
            //ofd.Title = "请选择要发送的文件";
            //ofd.Filter = "所有文件|*.*";
            ofd.ShowDialog();
            txtFilePath.Text = ofd.FileName;
            ShowImage(txtFilePath.Text);
        }

        private void btnSendFile_Click(object sender, RoutedEventArgs e)
        {
            
            string path = txtFilePath.Text;//文件的路径
            string fileName = System.IO.Path.GetFileName(path);
            Byte[] fileNameByte = GetFileNameByte(fileName);//文件名的字节数。
            Byte[] fileContentByte = GetfileContentByte(path);//文件内容。
            Byte[] combomArray = GetCombomFileByte(fileNameByte, fileContentByte);//总发送的内容。
            //dicSocket[cboUsers.SelectedItem.ToString()].Send(combomArray, 0, combomArray.Length, SocketFlags.None);
            dicSocket[Receive_IPandPort].Send(combomArray, 0, combomArray.Length, SocketFlags.None);
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[发送]" + Server.RemoteEndPoint.ToString() + ":" + "已经发送图片或文件:" + fileName);
        }

        private Byte[] GetFileNameByte(string SendFileName_str)
        {
            byte[] fileNameByteVar = System.Text.Encoding.UTF8.GetBytes(SendFileName_str);
            return fileNameByteVar;
        }

        private Byte[] GetfileContentByte(string path)
        {

            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);//读取文件
            Byte[] fileContentByteVar = new Byte[fs.Length];
            try
            {
                fs.Read(fileContentByteVar, 0, fileContentByteVar.Length);
                fs.Close();
                //return fileContentByteVar;
            }
            catch
            {
                MessageBox.Show("获取文件内容字节失败");
                //return fileContentByteVar;
            }
            return fileContentByteVar;

        }

        /// <summary>
        /// 四部分,第一部分为1B,区分发送类型,即指令或文件。二部分为4B,保存文件名所占的字符串长度。三部分,保存文件名的内容。四部分,文件的正式内容。
        /// </summary>
        /// <param name="fileNameByte"></param>
        /// <param name="fileContentByte"></param>
        /// <returns></returns>
        private Byte[] GetCombomFileByte(Byte[] fileNameByte, Byte[] fileContentByte)
        {
            int fileNameLong = fileNameByte.Length;//将文件的长度保存为一个整数。
            byte[] fileNameLongByte = new byte[4];//将整数放在4个字节中传输。
            fileNameLongByte = BitConverter.GetBytes(fileNameLong);//一个int等于四个字节。
            int combomFileLong = 1 + fileNameByte.Length + fileNameLongByte.Length + fileContentByte.Length;//总字节长度
            Byte[] combomFileByteTemple = new byte[combomFileLong];
            for (int i = 0; i < combomFileLong; i++)
            {
                if (i == 0)//区分发送类型,即指令或文件
                {
                    combomFileByteTemple[0] = 1;// 1表示发送文件
                }
                else if ((i > 0) && (i < (fileNameLongByte.Length + 1)))//确认文件名的长度,四个字节保存。
                {
                    combomFileByteTemple[i] = fileNameLongByte[i-1];
                }
                else if ( (i >= (fileNameLongByte.Length + 1)) &&  (i < (fileNameByte.Length + fileNameLongByte.Length + 1)) )//文件名的内容
                {
                    combomFileByteTemple[i] = fileNameByte[i - fileNameLongByte.Length - 1];//从第0号位置读取fileNameByte
                }
                else
                {
                    combomFileByteTemple[i] = fileContentByte[i - fileNameByte.Length - fileNameLongByte.Length - 1];//从第0号位置读取fileContentByte
                }
            }
            return combomFileByteTemple;
        }

        /// <summary>
        /// 获取文件路径path(path包含了文件夹、文件名、文明类型)、文件内容
        /// </summary>
        /// <param name="fs"></param>
        /// <param name="path"></param>
        /// <param name="fileContext"></param>
        private void GetSavePathAndFileContextByte(Byte[] fs, out string savePath, out byte[] fileContext)
        {
            string folderStr = "C:\\Users\\lanmage2\\Desktop\\Server收\\";//文件夹的位置
            string fileNameStr;//从客户端传输过来的文件名
            int fileNameLong = 0;
            byte[] fileNameLongByte = new byte[4];//固定四个字节,用来保存名字的长度。
            byte[] fileNameByte = new byte[0];//初始化为0
            byte[] fileContextByte = new byte[0];//初始化为0
            int count = 0;
            while (count < fs.Length)
            {
                if (count == 0)
                {
                    //不做任何处理,方便逻辑的理解
                }
                else if ( (count > 0) && (count < 4 + 1 ) )
                {          
                    fileNameLongByte[count - 1] = fs[count];
                    fileNameLong = BitConverter.ToInt32(fileNameLongByte, 0);//将字节转化为整数,该整数位文件名的长度。
                    fileNameByte = new byte[fileNameLong];
                    fileContextByte = new byte[fs.Length - 1 - 4 - fileNameByte.Length];
                }
                else if ((count >= (4 + 1)) && (count < (fileNameByte.Length + 4 + 1)))
                {
       
                    fileNameByte[count - 4 - 1] = fs[count];
                }
                else
                {
                    fileContextByte[count - (fileNameByte.Length + 4 + 1)] = fs[count];
                }
                count++;
            }
            fileNameStr = Encoding.UTF8.GetString(fileNameByte);//将字节数组转为字符串
            savePath = folderStr + fileNameStr;
            fileContext = fileContextByte;
        }

        private void BtnSysShutDown_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }
    }
}

2.2、Client

MainWindow.xaml

<Window x:Class="Client.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Client"
        mc:Ignorable="d"
        Title="Client" Height="450" Width="850" HorizontalAlignment="Center" VerticalAlignment="Center" >
    <Grid Height="400" Width="800">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="6*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>

        <Canvas Grid.Row="0" Background="#FF8080C6" >
            <!--<Border Height="50" Background="#FF307ECA" Margin="23,11,16,358"/>-->
            <Label Content="Server IP" Canvas.Left="49" Canvas.Top="13"/>
            <Label Content="Server Port" Canvas.Left="332" Canvas.Top="13"/>
            <Label Content="连接状态" Canvas.Left="555" Canvas.Top="13"/>
            <TextBox x:Name="txtServer" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="127.0.0.1" VerticalAlignment="Top" Width="120" Canvas.Left="117" Canvas.Top="15"/>
            <TextBox x:Name="txtPort" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="50000" VerticalAlignment="Top" Width="50" Canvas.Left="416" Canvas.Top="15"/>
            <TextBox x:Name="txtBox_Connect" Text="未连接" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Margin="630,15,0,0" />
        </Canvas>
        <Canvas Grid.Row="1" Background="#FFCFCFDB" >

            <TextBox x:Name="txtLog" HorizontalAlignment="Left" Height="185" TextWrapping="Wrap" VerticalAlignment="Top" Width="364"  BorderBrush="#FF5700FF" Canvas.Top="9" Canvas.Left="26"/>
            <Label Content="消息记录" Canvas.Left="328" Canvas.Top="13" Background="#FFDFC5C5"/>
            <TextBox x:Name="txtSendMsg" HorizontalAlignment="Left" Height="83" TextWrapping="Wrap" VerticalAlignment="Top" Width="366"  BorderBrush="#FF5700FF" Canvas.Top="205" Canvas.Left="24"/>
            <Label Content="编辑消息" Canvas.Left="327" Canvas.Top="209" Background="#FFDFC5C5" RenderTransformOrigin="0.741,7.92"/>
            <Border BorderBrush="#FF39222B" BorderThickness="2" Height="299" Width="342" Canvas.Left="448" Canvas.Top="-2"/>
            <Border BorderBrush="#FF39222B" BorderThickness="2" Height="43" Width="342" Canvas.Left="448" Canvas.Bottom="0" Canvas.Top="254" Background="#FF0086FF"/>
            <Image x:Name="Image_PlayImage" Height="243" Width="330" Canvas.Left="454" Canvas.Top="6"/>
        </Canvas>
        <Canvas Grid.Row="2" Background="#FF8080C6" >
            <Button x:Name="btnSendMsg" Content="发送消息" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Click="btnSendMsg_Click" Canvas.Left="128" Canvas.Top="15"/>
            <Button x:Name="btnOpenImage" Content="打开图片" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Click="btnOpenImage_Click" Canvas.Left="499" Canvas.Top="15"/>
            <Button x:Name="btnSendImage" Content="发送图片" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Click="btnSendImage_Click" Canvas.Left="652" Canvas.Top="15"/>
            <TextBox x:Name="txtFilePath" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" VerticalAlignment="Top" Width="334" Canvas.Left="452" Canvas.Top="-35"/>
            <Button x:Name="BtnSysShutDown" Content="退出" BorderThickness="2" Click="BtnSysShutDown_Click"  Height="41" Width="71" Background="#FFA31D77" Canvas.Left="2" Canvas.Top="4" ></Button>
        </Canvas>
    </Grid>
</Window>

MainWindow.xaml.cs

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace Server
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        string Receive_IPandPort = "";
        public MainWindow()
        {
            InitializeComponent();
            //当点击开始监听的时候,在服务器端创建一个负责监听IP地址跟端口号的Socket
            Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse("127.0.0.1");//IPAddress.Any;
                                                        //创建端口号对象
            IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
            //监听
            socketWatch.Bind(point);
            ShowMsg("监听成功");
            socketWatch.Listen(10);

            Thread th = new Thread(Listen);
            th.IsBackground = true;
            th.Start(socketWatch);
        }


        public delegate void delegate1(string str);//定义委托
        public delegate int delegate2(string endPoint);

        Socket Server;
        //将远程连接的客户端的IP地址和Socket存入集合中
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();
        /// <summary>
        /// 等待客户端的连接,并且创建与之通信的Socket
        /// </summary>
        /// <param name="o"></param>
        void Listen(object o)
        {
            Socket socketWatch = o as Socket;
            //等待客户端的连接,并且创建一个负责通信的Socket
            while (true)
            {
                try
                {
                    //负责跟客户端通信的Socket
                    Server = socketWatch.Accept();
                    //将远程连接的客户端的IP地址和Socket存入集合中
                    dicSocket.Add(Server.RemoteEndPoint.ToString(), Server);
                    //将远程连接的客户端的IP地址和端口号存储下拉框中
                    //cboUsers.Items.Add(Server.RemoteEndPoint.ToString());
                    //Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate2(cboUsers.Items.Add), Server.RemoteEndPoint.ToString());
                    Receive_IPandPort = Server.RemoteEndPoint.ToString();
                    //192.168.0.100:连接成功
                    Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), Server.RemoteEndPoint.ToString() + ":" + "连接成功");
                    //开启一个新的线程,不停地接收客户端发送过来的消息
                    Thread th = new Thread(Receive);
                    th.IsBackground = true;
                    th.Start(Server);
                }
                catch { }
            }
        }



        /// <summary>
        /// 服务端不停地接收客户端发送过来的消息
        /// </summary>
        /// <param name="o"></param>
        void Receive(object o)
        {
            Socket Server = o as Socket;
            while (true)
            {
                try
                {
                    //客户端连接成功后,服务器应该接收服务器应该接收客户端发来的消息
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    //实际接收到的有效字节数
                    int r = Server.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    else if (buffer[0] == 0)
                    {
                        string str = Encoding.UTF8.GetString(buffer, 1, r-1);
                        Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[接收]" + Server.RemoteEndPoint.ToString() + ":" + str);
                    }
                    else if (buffer[0] == 1)
                    {
                        string savePath;
                        byte[] fileContext = new byte[0];
                        GetSavePathAndFileContextByte(buffer, out savePath, out fileContext); //读取从客户端传输过来的文件名、文件内容
                        System.IO.File.WriteAllBytes(savePath, fileContext);//将内容写入到制定的路径。
                        //MessageBox.Show("保存图片成功");
                        ShowImage(savePath);
                        string fileName = System.IO.Path.GetFileName(savePath);
                        Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[接收]" + Server.RemoteEndPoint.ToString() + ":" + "接收的图片或文件:" + fileName);
                    }
                    else if (buffer[0] == 2)
                    {
                        //Shock();
                    }

                }
                catch
                {
                    //MessageBox.Show("你接收的是非法文件或非法消息");
                }
            }
        }

        private void ShowMsg(string str)
        {
            txtContent.AppendText(str + "\r\n");
        }

        private void ShowImage(string path)
        {
            this.Dispatcher.BeginInvoke((Action)delegate ()//异步委托,直接更新UI线程
            {
                //string fileName = System.IO.Path.GetFileName(path);
                //Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[接收]" + Client.RemoteEndPoint.ToString() + ":" + "接收的图片或文件:" + fileName);
                string ReceiveFileNameProperty_str = System.IO.Path.GetExtension(path);    //获取后缀。
                //必须判断,是图片执行,并显示。
                if ((ReceiveFileNameProperty_str == ".jpg")
                    || (ReceiveFileNameProperty_str == ".png")
                    || (ReceiveFileNameProperty_str == ".bmp")
                    || (ReceiveFileNameProperty_str == ".jpeg")
                    || (ReceiveFileNameProperty_str == ".JPG")
                    || (ReceiveFileNameProperty_str == ".PNG")
                    || (ReceiveFileNameProperty_str == ".BMP")
                    || (ReceiveFileNameProperty_str == ".JPEG"))
                {
                    Image_PlayImage.Source = new BitmapImage(new Uri(path, UriKind.Absolute));//绝对路径
                }
                //btn_StartServer.Content = "接收完成";
            }
          );
        }

        /// <summary>
        /// 服务器给客户端发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendMsg_Click(object sender, RoutedEventArgs e)
        {
            string str = txtSendMsg.Text.Trim();
            byte[] buffer = Encoding.UTF8.GetBytes(str);
            List<byte> list = new List<byte>();
            list.Add(0);
            list.AddRange(buffer);
            //将泛型集合转换为数组
            byte[] newBuffer = list.ToArray();
            //获得用户在下拉框中选中的IP地址
            //string ip = cboUsers.SelectedItem.ToString();
            //dicSocket[ip].Send(newBuffer);
            dicSocket[Receive_IPandPort].Send(newBuffer);
            txtSendMsg.Text = "";
            //发送时,也要在消息显示发送的消息。
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[发送]" + Server.RemoteEndPoint.ToString() + ":" + str);
        }

        /// <summary>
        /// 选择要发送的文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnChooseFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.InitialDirectory = "C:\\Users\\Administrator\\Desktop\\Server发";
            //ofd.Title = "请选择要发送的文件";
            //ofd.Filter = "所有文件|*.*";
            ofd.ShowDialog();
            txtFilePath.Text = ofd.FileName;
            ShowImage(txtFilePath.Text);
        }

        private void btnSendFile_Click(object sender, RoutedEventArgs e)
        {
            
            string path = txtFilePath.Text;//文件的路径
            string fileName = System.IO.Path.GetFileName(path);
            Byte[] fileNameByte = GetFileNameByte(fileName);//文件名的字节数。
            Byte[] fileContentByte = GetfileContentByte(path);//文件内容。
            Byte[] combomArray = GetCombomFileByte(fileNameByte, fileContentByte);//总发送的内容。
            //dicSocket[cboUsers.SelectedItem.ToString()].Send(combomArray, 0, combomArray.Length, SocketFlags.None);
            dicSocket[Receive_IPandPort].Send(combomArray, 0, combomArray.Length, SocketFlags.None);
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), "[发送]" + Server.RemoteEndPoint.ToString() + ":" + "已经发送图片或文件:" + fileName);
        }

        private Byte[] GetFileNameByte(string SendFileName_str)
        {
            byte[] fileNameByteVar = System.Text.Encoding.UTF8.GetBytes(SendFileName_str);
            return fileNameByteVar;
        }

        private Byte[] GetfileContentByte(string path)
        {

            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);//读取文件
            Byte[] fileContentByteVar = new Byte[fs.Length];
            try
            {
                fs.Read(fileContentByteVar, 0, fileContentByteVar.Length);
                fs.Close();
                //return fileContentByteVar;
            }
            catch
            {
                MessageBox.Show("获取文件内容字节失败");
                //return fileContentByteVar;
            }
            return fileContentByteVar;

        }

        /// <summary>
        /// 四部分,第一部分为1B,区分发送类型,即指令或文件。二部分为4B,保存文件名所占的字符串长度。三部分,保存文件名的内容。四部分,文件的正式内容。
        /// </summary>
        /// <param name="fileNameByte"></param>
        /// <param name="fileContentByte"></param>
        /// <returns></returns>
        private Byte[] GetCombomFileByte(Byte[] fileNameByte, Byte[] fileContentByte)
        {
            int fileNameLong = fileNameByte.Length;//将文件的长度保存为一个整数。
            byte[] fileNameLongByte = new byte[4];//将整数放在4个字节中传输。
            fileNameLongByte = BitConverter.GetBytes(fileNameLong);//一个int等于四个字节。
            int combomFileLong = 1 + fileNameByte.Length + fileNameLongByte.Length + fileContentByte.Length;//总字节长度
            Byte[] combomFileByteTemple = new byte[combomFileLong];
            for (int i = 0; i < combomFileLong; i++)
            {
                if (i == 0)//区分发送类型,即指令或文件
                {
                    combomFileByteTemple[0] = 1;// 1表示发送文件
                }
                else if ((i > 0) && (i < (fileNameLongByte.Length + 1)))//确认文件名的长度,四个字节保存。
                {
                    combomFileByteTemple[i] = fileNameLongByte[i-1];
                }
                else if ( (i >= (fileNameLongByte.Length + 1)) &&  (i < (fileNameByte.Length + fileNameLongByte.Length + 1)) )//文件名的内容
                {
                    combomFileByteTemple[i] = fileNameByte[i - fileNameLongByte.Length - 1];//从第0号位置读取fileNameByte
                }
                else
                {
                    combomFileByteTemple[i] = fileContentByte[i - fileNameByte.Length - fileNameLongByte.Length - 1];//从第0号位置读取fileContentByte
                }
            }
            return combomFileByteTemple;
        }

        /// <summary>
        /// 获取文件路径path(path包含了文件夹、文件名、文明类型)、文件内容
        /// </summary>
        /// <param name="fs"></param>
        /// <param name="path"></param>
        /// <param name="fileContext"></param>
        private void GetSavePathAndFileContextByte(Byte[] fs, out string savePath, out byte[] fileContext)
        {
            string folderStr = "C:\\Users\\lanmage2\\Desktop\\Server收\\";//文件夹的位置
            string fileNameStr;//从客户端传输过来的文件名
            int fileNameLong = 0;
            byte[] fileNameLongByte = new byte[4];//固定四个字节,用来保存名字的长度。
            byte[] fileNameByte = new byte[0];//初始化为0
            byte[] fileContextByte = new byte[0];//初始化为0
            int count = 0;
            while (count < fs.Length)
            {
                if (count == 0)
                {
                    //不做任何处理,方便逻辑的理解
                }
                else if ( (count > 0) && (count < 4 + 1 ) )
                {          
                    fileNameLongByte[count - 1] = fs[count];
                    fileNameLong = BitConverter.ToInt32(fileNameLongByte, 0);//将字节转化为整数,该整数位文件名的长度。
                    fileNameByte = new byte[fileNameLong];
                    fileContextByte = new byte[fs.Length - 1 - 4 - fileNameByte.Length];
                }
                else if ((count >= (4 + 1)) && (count < (fileNameByte.Length + 4 + 1)))
                {
       
                    fileNameByte[count - 4 - 1] = fs[count];
                }
                else
                {
                    fileContextByte[count - (fileNameByte.Length + 4 + 1)] = fs[count];
                }
                count++;
            }
            fileNameStr = Encoding.UTF8.GetString(fileNameByte);//将字节数组转为字符串
            savePath = folderStr + fileNameStr;
            fileContext = fileContextByte;
        }

        private void BtnSysShutDown_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }
    }
}

三、Socket异步通信实现

       请参考 https://www.cnblogs.com/liuyong/archive/2011/09/08/1892217.html

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱AI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值