liuyuqi-dellpc 3 years ago
parent
commit
c7facb1b29
47 changed files with 3656 additions and 0 deletions
  1. 17 0
      CSharp/.gitattributes
  2. 158 0
      CSharp/.gitignore
  3. 19 0
      CSharp/README.md
  4. BIN
      CSharp/Screenshots/1.png
  5. BIN
      CSharp/Screenshots/2.png
  6. BIN
      CSharp/Screenshots/3.png
  7. BIN
      CSharp/Screenshots/4.png
  8. BIN
      CSharp/Screenshots/5.png
  9. 6 0
      CSharp/SocketChat.Client/App.config
  10. 17 0
      CSharp/SocketChat.Client/Model/User.cs
  11. 26 0
      CSharp/SocketChat.Client/Program.cs
  12. 36 0
      CSharp/SocketChat.Client/Properties/AssemblyInfo.cs
  13. 71 0
      CSharp/SocketChat.Client/Properties/Resources.Designer.cs
  14. 117 0
      CSharp/SocketChat.Client/Properties/Resources.resx
  15. 30 0
      CSharp/SocketChat.Client/Properties/Settings.Designer.cs
  16. 7 0
      CSharp/SocketChat.Client/Properties/Settings.settings
  17. 111 0
      CSharp/SocketChat.Client/SocketChat.Client.csproj
  18. 323 0
      CSharp/SocketChat.Client/Utils/ConvertJson.cs
  19. 386 0
      CSharp/SocketChat.Client/Utils/JsonHelper.cs
  20. 172 0
      CSharp/SocketChat.Client/Views/ChatClient.Designer.cs
  21. 303 0
      CSharp/SocketChat.Client/Views/ChatClient.cs
  22. 120 0
      CSharp/SocketChat.Client/Views/ChatClient.resx
  23. 129 0
      CSharp/SocketChat.Client/Views/Login.Designer.cs
  24. 88 0
      CSharp/SocketChat.Client/Views/Login.cs
  25. 120 0
      CSharp/SocketChat.Client/Views/Login.resx
  26. 4 0
      CSharp/SocketChat.Client/packages.config
  27. 23 0
      CSharp/SocketChat.Common/Model/ClientMsgModel.cs
  28. 15 0
      CSharp/SocketChat.Common/Model/OnlineUser.cs
  29. 20 0
      CSharp/SocketChat.Common/Model/ServerMsgModel.cs
  30. 36 0
      CSharp/SocketChat.Common/Properties/AssemblyInfo.cs
  31. 61 0
      CSharp/SocketChat.Common/SocketChat.Common.csproj
  32. 4 0
      CSharp/SocketChat.Common/packages.config
  33. 6 0
      CSharp/SocketChat.Server/App.config
  34. 205 0
      CSharp/SocketChat.Server/ChatServer.Designer.cs
  35. 421 0
      CSharp/SocketChat.Server/ChatServer.cs
  36. 120 0
      CSharp/SocketChat.Server/ChatServer.resx
  37. 36 0
      CSharp/SocketChat.Server/Model/Server.cs
  38. 25 0
      CSharp/SocketChat.Server/Model/User.cs
  39. 22 0
      CSharp/SocketChat.Server/Program.cs
  40. 36 0
      CSharp/SocketChat.Server/Properties/AssemblyInfo.cs
  41. 71 0
      CSharp/SocketChat.Server/Properties/Resources.Designer.cs
  42. 117 0
      CSharp/SocketChat.Server/Properties/Resources.resx
  43. 30 0
      CSharp/SocketChat.Server/Properties/Settings.Designer.cs
  44. 7 0
      CSharp/SocketChat.Server/Properties/Settings.settings
  45. 100 0
      CSharp/SocketChat.Server/SocketChat.Server.csproj
  46. 4 0
      CSharp/SocketChat.Server/packages.config
  47. 37 0
      CSharp/SocketChat.sln

+ 17 - 0
CSharp/.gitattributes

@@ -0,0 +1,17 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs     diff=csharp
+
+# Standard to msysgit
+*.doc	 diff=astextplain
+*.DOC	 diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF	 diff=astextplain
+*.rtf	 diff=astextplain
+*.RTF	 diff=astextplain

+ 158 - 0
CSharp/.gitignore

@@ -0,0 +1,158 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+
+[Dd]ebug/
+[Rr]elease/
+x64/
+build/
+[Bb]in/
+[Oo]bj/
+
+# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
+!packages/*/build/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+*_i.c
+*_p.c
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.log
+*.scc
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+*.ncrunch*
+.*crunch*.local.xml
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.Publish.xml
+
+# NuGet Packages Directory
+## TODO: If you have NuGet Package Restore enabled, uncomment the next line
+#packages/
+
+# Windows Azure Build Output
+csx
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.[Pp]ublish.xml
+*.pfx
+*.publishsettings
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+App_Data/*.mdf
+App_Data/*.ldf
+
+
+#LightSwitch generated files
+GeneratedArtifacts/
+_Pvt_Extensions/
+ModelManifest.xml
+
+# =========================
+# Windows detritus
+# =========================
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Mac desktop service store files
+.DS_Store
+/.vs
+/packages

+ 19 - 0
CSharp/README.md

@@ -0,0 +1,19 @@
+# SocketChat.Client
+一个基于Socket服务器的点对点聊天小程序。
+------------------------------------
+数据传输使用的Json字符串。
+
+服务器端:<br>
+![Alt 服务器端](https://github.com/Johnnyhuhu/SocketChat.Client/blob/master/Screenshots/1.png)
+
+登录界面:<br>
+![Alt 登录界面](https://github.com/Johnnyhuhu/SocketChat.Client/blob/master/Screenshots/2.png)
+
+聊天界面:<br>
+![Alt 聊天界面](https://github.com/Johnnyhuhu/SocketChat.Client/blob/master/Screenshots/3.png)
+
+对话发送前界面:<br>
+![Alt 对话发送前界面](https://github.com/Johnnyhuhu/SocketChat.Client/blob/master/Screenshots/4.png)
+
+对话发送后界面:<br>
+![Alt 对话发送后界面](https://github.com/Johnnyhuhu/SocketChat.Client/blob/master/Screenshots/5.png)

BIN
CSharp/Screenshots/1.png


BIN
CSharp/Screenshots/2.png


BIN
CSharp/Screenshots/3.png


BIN
CSharp/Screenshots/4.png


BIN
CSharp/Screenshots/5.png


+ 6 - 0
CSharp/SocketChat.Client/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+    </startup>
+</configuration>

+ 17 - 0
CSharp/SocketChat.Client/Model/User.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketChat.Client
+{
+    public class User
+    {
+        public string p_userName { get; set; }
+        public string p_serverIP { get; set; }
+        public string p_serverPort { get; set; }
+        public Socket p_Client { get; set; }
+    }
+}

+ 26 - 0
CSharp/SocketChat.Client/Program.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace SocketChat.Client
+{
+    static class Program
+    {
+        /// <summary>
+        /// 应用程序的主入口点。
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            Login __frmLogin = new Login();
+            if (__frmLogin.ShowDialog() == DialogResult.OK)
+            {
+                Application.Run(new ChatClient(__frmLogin.p_user));
+            }
+        }
+    }
+}

+ 36 - 0
CSharp/SocketChat.Client/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的常规信息通过以下
+// 特性集控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("SocketChat.Client")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("SocketChat.Client")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 使此程序集中的类型
+// 对 COM 组件不可见。  如果需要从 COM 访问此程序集中的类型,
+// 则将该类型上的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("183ffbb1-7f9c-4df9-8297-03ff6777727d")]
+
+// 程序集的版本信息由下面四个值组成: 
+//
+//      主版本
+//      次版本 
+//      生成号
+//      修订号
+//
+// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
+// 方法是按如下所示使用“*”: 
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 71 - 0
CSharp/SocketChat.Client/Properties/Resources.Designer.cs

@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本: 4.0.30319.18408
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace SocketChat.Client.Properties
+{
+
+
+    /// <summary>
+    ///   一个强类型的资源类,用于查找本地化的字符串等。
+    /// </summary>
+    // 此类是由 StronglyTypedResourceBuilder
+    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+    // (以 /str 作为命令选项),或重新生成 VS 项目。
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources
+    {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources()
+        {
+        }
+
+        /// <summary>
+        ///   返回此类使用的、缓存的 ResourceManager 实例。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager
+        {
+            get
+            {
+                if ((resourceMan == null))
+                {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SocketChat.Client.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   为所有资源查找重写当前线程的 CurrentUICulture 属性,
+        ///   方法是使用此强类型资源类。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture
+        {
+            get
+            {
+                return resourceCulture;
+            }
+            set
+            {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 117 - 0
CSharp/SocketChat.Client/Properties/Resources.resx

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 30 - 0
CSharp/SocketChat.Client/Properties/Settings.Designer.cs

@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.18408
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace SocketChat.Client.Properties
+{
+
+
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+    {
+
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+        public static Settings Default
+        {
+            get
+            {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 7 - 0
CSharp/SocketChat.Client/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 111 - 0
CSharp/SocketChat.Client/SocketChat.Client.csproj

@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{79337939-498C-41FE-BEBE-76FCAB5F2B6E}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SocketChat.Client</RootNamespace>
+    <AssemblyName>SocketChat.Client</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Views\ChatClient.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Views\ChatClient.Designer.cs">
+      <DependentUpon>ChatClient.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Utils\ConvertJson.cs" />
+    <Compile Include="Utils\JsonHelper.cs" />
+    <Compile Include="Views\Login.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Views\Login.Designer.cs">
+      <DependentUpon>Login.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Model\User.cs" />
+    <EmbeddedResource Include="Views\ChatClient.resx">
+      <DependentUpon>ChatClient.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Views\Login.resx">
+      <DependentUpon>Login.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <None Include="packages.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\SocketChat.Common\SocketChat.Common.csproj">
+      <Project>{4f0bc491-47fc-40e8-bc36-03f0c936dc72}</Project>
+      <Name>SocketChat.Common</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 323 - 0
CSharp/SocketChat.Client/Utils/ConvertJson.cs

@@ -0,0 +1,323 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketChat.Client
+{
+    public class ConvertJson
+    {
+        #region 私有方法
+        /// <summary>
+        /// 过滤特殊字符
+        /// </summary>
+        private static string String2Json(String s)
+        {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < s.Length; i++)
+            {
+                char c = s.ToCharArray()[i];
+                switch (c)
+                {
+                    case '\"':
+                        sb.Append("\\\""); break;
+                    case '\\':
+                        sb.Append("\\\\"); break;
+                    case '/':
+                        sb.Append("\\/"); break;
+                    case '\b':
+                        sb.Append("\\b"); break;
+                    case '\f':
+                        sb.Append("\\f"); break;
+                    case '\n':
+                        sb.Append("\\n"); break;
+                    case '\r':
+                        sb.Append("\\r"); break;
+                    case '\t':
+                        sb.Append("\\t"); break;
+                    default:
+                        sb.Append(c); break;
+                }
+            }
+            return sb.ToString();
+        }
+
+        /// <summary>
+        /// 格式化字符型、日期型、布尔型
+        /// </summary>
+        private static string StringFormat(string str, Type type)
+        {
+            if (type == typeof(string))
+            {
+                str = String2Json(str);
+                str = "\"" + str + "\"";
+            }
+            else if (type == typeof(DateTime))
+            {
+                str = "\"" + str + "\"";
+            }
+            else if (type == typeof(bool))
+            {
+                str = str.ToLower();
+            }
+            else if (type != typeof(string) && string.IsNullOrEmpty(str))
+            {
+                str = "\"" + str + "\"";
+            }
+            return str;
+        }
+        #endregion
+
+        #region List转换成Json
+        /// <summary>
+        /// List转换成Json
+        /// </summary>
+        public static string ListToJson<T>(IList<T> list)
+        {
+            object obj = list[0];
+            return ListToJson<T>(list, obj.GetType().Name);
+        }
+
+        /// <summary>
+        /// List转换成Json 
+        /// </summary>
+        public static string ListToJson<T>(IList<T> list, string jsonName)
+        {
+            StringBuilder Json = new StringBuilder();
+            if (string.IsNullOrEmpty(jsonName)) jsonName = list[0].GetType().Name;
+            Json.Append("{\"" + jsonName + "\":[");
+            if (list.Count > 0)
+            {
+                for (int i = 0; i < list.Count; i++)
+                {
+                    T obj = Activator.CreateInstance<T>();
+                    PropertyInfo[] pi = obj.GetType().GetProperties();
+                    Json.Append("{");
+                    for (int j = 0; j < pi.Length; j++)
+                    {
+                        Type type = pi[j].GetValue(list[i], null).GetType();
+                        Json.Append("\"" + pi[j].Name.ToString() + "\":" + StringFormat(pi[j].GetValue(list[i], null).ToString(), type));
+
+                        if (j < pi.Length - 1)
+                        {
+                            Json.Append(",");
+                        }
+                    }
+                    Json.Append("}");
+                    if (i < list.Count - 1)
+                    {
+                        Json.Append(",");
+                    }
+                }
+            }
+            Json.Append("]}");
+            return Json.ToString();
+        }
+        #endregion
+
+        #region 对象转换为Json
+        /// <summary> 
+        /// 对象转换为Json 
+        /// </summary> 
+        /// <param name="jsonObject">对象</param> 
+        /// <returns>Json字符串</returns> 
+        public static string ToJson(object jsonObject)
+        {
+            string jsonString = "{";
+            PropertyInfo[] propertyInfo = jsonObject.GetType().GetProperties();
+            for (int i = 0; i < propertyInfo.Length; i++)
+            {
+                object objectValue = propertyInfo[i].GetGetMethod().Invoke(jsonObject, null);
+                string value = string.Empty;
+                if (objectValue is DateTime || objectValue is Guid || objectValue is TimeSpan)
+                {
+                    value = "'" + objectValue.ToString() + "'";
+                }
+                else if (objectValue is string)
+                {
+                    value = "'" + ToJson(objectValue.ToString()) + "'";
+                }
+                else if (objectValue is IEnumerable)
+                {
+                    value = ToJson((IEnumerable)objectValue);
+                }
+                else
+                {
+                    value = ToJson(objectValue.ToString());
+                }
+                jsonString += "\"" + ToJson(propertyInfo[i].Name) + "\":" + value + ",";
+            }
+            jsonString.Remove(jsonString.Length - 1, jsonString.Length);
+            return jsonString + "}";
+        }
+        #endregion
+
+        #region 对象集合转换Json
+        /// <summary> 
+        /// 对象集合转换Json 
+        /// </summary> 
+        /// <param name="array">集合对象</param> 
+        /// <returns>Json字符串</returns> 
+        public static string ToJson(IEnumerable array)
+        {
+            string jsonString = "[";
+            foreach (object item in array)
+            {
+                jsonString += ToJson(item) + ",";
+            }
+            jsonString.Remove(jsonString.Length - 1, jsonString.Length);
+            return jsonString + "]";
+        }
+        #endregion
+
+        #region 普通集合转换Json
+        /// <summary> 
+        /// 普通集合转换Json 
+        /// </summary> 
+        /// <param name="array">集合对象</param> 
+        /// <returns>Json字符串</returns> 
+        public static string ToArrayString(IEnumerable array)
+        {
+            string jsonString = "[";
+            foreach (object item in array)
+            {
+                jsonString = ToJson(item.ToString()) + ",";
+            }
+            jsonString.Remove(jsonString.Length - 1, jsonString.Length);
+            return jsonString + "]";
+        }
+        #endregion
+
+        #region  DataSet转换为Json
+        /// <summary> 
+        /// DataSet转换为Json 
+        /// </summary> 
+        /// <param name="dataSet">DataSet对象</param> 
+        /// <returns>Json字符串</returns> 
+        public static string ToJson(DataSet dataSet)
+        {
+            string jsonString = "{";
+            foreach (DataTable table in dataSet.Tables)
+            {
+                jsonString += "\"" + table.TableName + "\":" + ToJson(table) + ",";
+            }
+            jsonString = jsonString.TrimEnd(',');
+            return jsonString + "}";
+        }
+        #endregion
+
+        #region Datatable转换为Json
+        /// <summary> 
+        /// Datatable转换为Json 
+        /// </summary> 
+        /// <param name="table">Datatable对象</param> 
+        /// <returns>Json字符串</returns> 
+        public static string ToJson(DataTable dt)
+        {
+            StringBuilder jsonString = new StringBuilder();
+            jsonString.Append("[");
+            DataRowCollection drc = dt.Rows;
+            for (int i = 0; i < drc.Count; i++)
+            {
+                jsonString.Append("{");
+                for (int j = 0; j < dt.Columns.Count; j++)
+                {
+                    string strKey = dt.Columns[j].ColumnName;
+                    string strValue = drc[i][j].ToString();
+                    Type type = dt.Columns[j].DataType;
+                    jsonString.Append("\"" + strKey + "\":");
+                    strValue = StringFormat(strValue, type);
+                    if (j < dt.Columns.Count - 1)
+                    {
+                        jsonString.Append(strValue + ",");
+                    }
+                    else
+                    {
+                        jsonString.Append(strValue);
+                    }
+                }
+                jsonString.Append("},");
+            }
+            jsonString.Remove(jsonString.Length - 1, 1);
+            jsonString.Append("]");
+            return jsonString.ToString();
+        }
+
+        /// <summary>
+        /// DataTable转换为Json 
+        /// </summary>
+        public static string ToJson(DataTable dt, string jsonName)
+        {
+            StringBuilder Json = new StringBuilder();
+            if (string.IsNullOrEmpty(jsonName)) jsonName = dt.TableName;
+            Json.Append("{\"" + jsonName + "\":[");
+            if (dt.Rows.Count > 0)
+            {
+                for (int i = 0; i < dt.Rows.Count; i++)
+                {
+                    Json.Append("{");
+                    for (int j = 0; j < dt.Columns.Count; j++)
+                    {
+                        Type type = dt.Rows[i][j].GetType();
+                        Json.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + StringFormat(dt.Rows[i][j].ToString(), type));
+                        if (j < dt.Columns.Count - 1)
+                        {
+                            Json.Append(",");
+                        }
+                    }
+                    Json.Append("}");
+                    if (i < dt.Rows.Count - 1)
+                    {
+                        Json.Append(",");
+                    }
+                }
+            }
+            Json.Append("]}");
+            return Json.ToString();
+        }
+        #endregion
+
+        #region DataReader转换为Json
+        /// <summary> 
+        /// DataReader转换为Json 
+        /// </summary> 
+        /// <param name="dataReader">DataReader对象</param> 
+        /// <returns>Json字符串</returns> 
+        public static string ToJson(DbDataReader dataReader)
+        {
+            StringBuilder jsonString = new StringBuilder();
+            jsonString.Append("[");
+            while (dataReader.Read())
+            {
+                jsonString.Append("{");
+                for (int i = 0; i < dataReader.FieldCount; i++)
+                {
+                    Type type = dataReader.GetFieldType(i);
+                    string strKey = dataReader.GetName(i);
+                    string strValue = dataReader[i].ToString();
+                    jsonString.Append("\"" + strKey + "\":");
+                    strValue = StringFormat(strValue, type);
+                    if (i < dataReader.FieldCount - 1)
+                    {
+                        jsonString.Append(strValue + ",");
+                    }
+                    else
+                    {
+                        jsonString.Append(strValue);
+                    }
+                }
+                jsonString.Append("},");
+            }
+            dataReader.Close();
+            jsonString.Remove(jsonString.Length - 1, 1);
+            jsonString.Append("]");
+            return jsonString.ToString();
+        }
+        #endregion
+    }
+}

+ 386 - 0
CSharp/SocketChat.Client/Utils/JsonHelper.cs

@@ -0,0 +1,386 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using System.Data.Common;
+using System.Data;
+using System.Runtime.Serialization.Json;
+using System.Text.RegularExpressions;
+using System.IO;
+
+namespace SocketChat.Client
+{
+    public class JsonHelper
+    {
+        /////////////////////////////////////////Newtonsoft.Json
+
+        #region Newtonsoft-对象转json序列化
+        /// <summary>
+        /// Newtonsoft-对象转json序列化
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="t"></param>
+        /// <returns></returns>
+        public static string ObjectToJson<T>(T t)
+        {
+            string jsonStr = "";
+            try
+            {
+                jsonStr = JsonConvert.SerializeObject(t);
+            }
+            catch
+            {
+                jsonStr = "";
+            }
+            return jsonStr;
+        }
+        #endregion
+
+        #region Newtonsoft-json转对象反序列化
+        /// <summary>
+        /// Newtonsoft-json转对象反序列化
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="jsonStr"></param>
+        /// <returns></returns>
+        public static T JsonToObject<T>(string jsonStr)
+        {
+            T obj = default(T);
+            try
+            {
+                //JObject jobject = JObject.Parse(jsonStr);
+                //obj = jobject.ToObject<T>();
+                obj = JsonConvert.DeserializeObject<T>(jsonStr);
+            }
+            catch (Exception e)
+            {
+                obj = default(T);
+            }
+            return obj;
+        }
+        #endregion
+
+        #region Newtonsoft-集合转json序列化
+        /// <summary>
+        /// Newtonsoft-集合转json序列化
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="obs"></param>
+        /// <returns></returns>
+        public static string ListToJson<T>(List<T> obs)
+        {
+            string jsonStr = "";
+            try
+            {
+                jsonStr = JsonConvert.SerializeObject(obs);
+            }
+            catch
+            {
+                jsonStr = "";
+            }
+            return jsonStr;
+        }
+        #endregion
+
+        #region Newtonsoft-json转集合反序列化
+        /// <summary>
+        /// Newtonsoft-json转集合反序列化
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="t"></param>
+        /// <param name="jsonStr"></param>
+        /// <returns></returns>
+        public static List<T> JsonToList<T>(string jsonStr)
+        {
+            List<T> obs = new List<T>();
+            try
+            {
+                obs = JsonConvert.DeserializeObject<List<T>>(jsonStr);
+            }
+            catch
+            {
+                obs = new List<T>();
+            }
+            return obs;
+        }
+        #endregion
+
+        /////////////////////////////////////////DataSet、Datatable、DataReader
+
+        #region  DataSet转换为Json
+        /// <summary> 
+        /// DataSet转换为Json 
+        /// </summary> 
+        /// <param name="dataSet">DataSet对象</param> 
+        /// <returns>Json字符串</returns> 
+        public static string DataSetToJson(DataSet dataSet)
+        {
+            string jsonString = "{";
+            foreach (DataTable table in dataSet.Tables)
+            {
+                jsonString += "\"" + table.TableName + "\":" + DatatableToJson(table) + ",";
+            }
+            jsonString = jsonString.TrimEnd(',');
+            return jsonString + "}";
+        }
+        #endregion
+
+        #region Datatable转换为Json
+        /// <summary> 
+        /// Datatable转换为Json 
+        /// </summary> 
+        /// <param name="table">Datatable对象</param> 
+        /// <returns>Json字符串</returns> 
+        public static string DatatableToJson(DataTable dt)
+        {
+            StringBuilder jsonString = new StringBuilder();
+            jsonString.Append("[");
+            DataRowCollection drc = dt.Rows;
+            for (int i = 0; i < drc.Count; i++)
+            {
+                jsonString.Append("{");
+                for (int j = 0; j < dt.Columns.Count; j++)
+                {
+                    string strKey = dt.Columns[j].ColumnName;
+                    string strValue = drc[i][j].ToString();
+                    Type type = dt.Columns[j].DataType;
+                    jsonString.Append("\"" + strKey + "\":");
+                    strValue = StringFormat(strValue, type);
+                    if (j < dt.Columns.Count - 1)
+                    {
+                        jsonString.Append(strValue + ",");
+                    }
+                    else
+                    {
+                        jsonString.Append(strValue);
+                    }
+                }
+                jsonString.Append("},");
+            }
+            jsonString.Remove(jsonString.Length - 1, 1);
+            jsonString.Append("]");
+            return jsonString.ToString();
+        }
+
+        /// <summary>
+        /// DataTable转换为Json 
+        /// </summary>
+        public static string DatatableToJson(DataTable dt, string jsonName)
+        {
+            StringBuilder Json = new StringBuilder();
+            if (string.IsNullOrEmpty(jsonName)) jsonName = dt.TableName;
+            Json.Append("{\"" + jsonName + "\":[");
+            if (dt.Rows.Count > 0)
+            {
+                for (int i = 0; i < dt.Rows.Count; i++)
+                {
+                    Json.Append("{");
+                    for (int j = 0; j < dt.Columns.Count; j++)
+                    {
+                        Type type = dt.Rows[i][j].GetType();
+                        Json.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + StringFormat(dt.Rows[i][j].ToString(), type));
+                        if (j < dt.Columns.Count - 1)
+                        {
+                            Json.Append(",");
+                        }
+                    }
+                    Json.Append("}");
+                    if (i < dt.Rows.Count - 1)
+                    {
+                        Json.Append(",");
+                    }
+                }
+            }
+            Json.Append("]}");
+            return Json.ToString();
+        }
+        #endregion
+
+        #region DataReader转换为Json
+        /// <summary> 
+        /// DataReader转换为Json 
+        /// </summary> 
+        /// <param name="dataReader">DataReader对象</param> 
+        /// <returns>Json字符串</returns> 
+        public static string DataReaderToJson(DbDataReader dataReader)
+        {
+            StringBuilder jsonString = new StringBuilder();
+            jsonString.Append("[");
+            while (dataReader.Read())
+            {
+                jsonString.Append("{");
+                for (int i = 0; i < dataReader.FieldCount; i++)
+                {
+                    Type type = dataReader.GetFieldType(i);
+                    string strKey = dataReader.GetName(i);
+                    string strValue = dataReader[i].ToString();
+                    jsonString.Append("\"" + strKey + "\":");
+                    strValue = StringFormat(strValue, type);
+                    if (i < dataReader.FieldCount - 1)
+                    {
+                        jsonString.Append(strValue + ",");
+                    }
+                    else
+                    {
+                        jsonString.Append(strValue);
+                    }
+                }
+                jsonString.Append("},");
+            }
+            dataReader.Close();
+            jsonString.Remove(jsonString.Length - 1, 1);
+            jsonString.Append("]");
+            return jsonString.ToString();
+        }
+        #endregion
+
+        #region 私有方法
+        /// <summary>
+        /// 过滤特殊字符
+        /// </summary>
+        private static string String2Json(String s)
+        {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < s.Length; i++)
+            {
+                char c = s.ToCharArray()[i];
+                switch (c)
+                {
+                    case '\"':
+                        sb.Append("\\\""); break;
+                    case '\\':
+                        sb.Append("\\\\"); break;
+                    case '/':
+                        sb.Append("\\/"); break;
+                    case '\b':
+                        sb.Append("\\b"); break;
+                    case '\f':
+                        sb.Append("\\f"); break;
+                    case '\n':
+                        sb.Append("\\n"); break;
+                    case '\r':
+                        sb.Append("\\r"); break;
+                    case '\t':
+                        sb.Append("\\t"); break;
+                    default:
+                        sb.Append(c); break;
+                }
+            }
+            return sb.ToString();
+        }
+
+        /// <summary>
+        /// 格式化字符型、日期型、布尔型
+        /// </summary>
+        private static string StringFormat(string str, Type type)
+        {
+            if (type == typeof(string))
+            {
+                str = String2Json(str);
+                str = "\"" + str + "\"";
+            }
+            else if (type == typeof(DateTime))
+            {
+                str = "\"" + str + "\"";
+            }
+            else if (type == typeof(bool))
+            {
+                str = str.ToLower();
+            }
+            else if (type != typeof(string) && string.IsNullOrEmpty(str))
+            {
+                str = "\"" + str + "\"";
+            }
+            return str;
+        }
+        #endregion
+
+        /////////////////////////////////////////微软自带json解析类
+
+        #region 微软-JSON序列化
+        /// <summary>
+        /// JSON序列化
+        /// </summary>
+        public static string JsonSerializer<T>(T t)
+        {
+            string jsonString = "";
+            try
+            {
+                DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
+                MemoryStream ms = new MemoryStream();
+                ser.WriteObject(ms, t);
+                jsonString = Encoding.UTF8.GetString(ms.ToArray());
+                ms.Close();
+                //替换Json的Date字符串
+                string p = @"\\/Date\((\d+)\+\d+\)\\/";
+                MatchEvaluator matchEvaluator = new MatchEvaluator(ConvertJsonDateToDateString);
+                Regex reg = new Regex(p);
+                jsonString = reg.Replace(jsonString, matchEvaluator);
+            }
+            catch
+            {
+                jsonString = "";
+            }
+            return jsonString;
+        }
+        #endregion
+
+        #region 微软-JSON反序列化
+        /// <summary>
+        /// JSON反序列化
+        /// </summary>
+        public static T JsonDeserialize<T>(string jsonString)
+        {
+            T obj = default(T);
+            try
+            {
+                //将"yyyy-MM-dd HH:mm:ss"格式的字符串转为"\/Date(1294499956278+0800)\/"格式
+                string p = @"\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}";
+                MatchEvaluator matchEvaluator = new MatchEvaluator(ConvertDateStringToJsonDate);
+                Regex reg = new Regex(p);
+                jsonString = reg.Replace(jsonString, matchEvaluator);
+                DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
+                MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
+                obj = (T)ser.ReadObject(ms);
+            }
+            catch
+            {
+                obj = default(T);
+            }
+            return obj;
+        }
+        #endregion
+
+        #region 将Json序列化的时间由/Date(1294499956278+0800)转为字符串
+        /// <summary>
+        /// 将Json序列化的时间由/Date(1294499956278+0800)转为字符串
+        /// </summary>
+        private static string ConvertJsonDateToDateString(Match m)
+        {
+            string result = string.Empty;
+            DateTime dt = new DateTime(1970, 1, 1);
+            dt = dt.AddMilliseconds(long.Parse(m.Groups[1].Value));
+            dt = dt.ToLocalTime();
+            result = dt.ToString("yyyy-MM-dd HH:mm:ss");
+            return result;
+        }
+        #endregion
+
+        #region 将时间字符串转为Json时间
+        /// <summary>
+        /// 将时间字符串转为Json时间
+        /// </summary>
+        private static string ConvertDateStringToJsonDate(Match m)
+        {
+            string result = string.Empty;
+            DateTime dt = DateTime.Parse(m.Groups[0].Value);
+            dt = dt.ToUniversalTime();
+            TimeSpan ts = dt - DateTime.Parse("1970-01-01");
+            result = string.Format("\\/Date({0}+0800)\\/", ts.TotalMilliseconds);
+            return result;
+        }
+        #endregion
+    }
+}

+ 172 - 0
CSharp/SocketChat.Client/Views/ChatClient.Designer.cs

@@ -0,0 +1,172 @@
+namespace SocketChat.Client
+{
+    partial class ChatClient
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.btnClose = new System.Windows.Forms.Button();
+            this.btnSend = new System.Windows.Forms.Button();
+            this.txtSendMsg = new System.Windows.Forms.RichTextBox();
+            this.txtChatMsg = new System.Windows.Forms.RichTextBox();
+            this.lb_online = new System.Windows.Forms.Label();
+            this.lbx_online = new System.Windows.Forms.ListBox();
+            this.TcpInfo = new System.Windows.Forms.RichTextBox();
+            this.lb_receive = new System.Windows.Forms.Label();
+            this.lb_log = new System.Windows.Forms.Label();
+            this.lb_send = new System.Windows.Forms.Label();
+            this.SuspendLayout();
+            // 
+            // btnClose
+            // 
+            this.btnClose.Location = new System.Drawing.Point(495, 294);
+            this.btnClose.Name = "btnClose";
+            this.btnClose.Size = new System.Drawing.Size(75, 32);
+            this.btnClose.TabIndex = 11;
+            this.btnClose.Text = "关闭";
+            this.btnClose.UseVisualStyleBackColor = true;
+            this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
+            // 
+            // btnSend
+            // 
+            this.btnSend.Location = new System.Drawing.Point(495, 245);
+            this.btnSend.Name = "btnSend";
+            this.btnSend.Size = new System.Drawing.Size(75, 35);
+            this.btnSend.TabIndex = 10;
+            this.btnSend.Text = "发送";
+            this.btnSend.UseVisualStyleBackColor = true;
+            this.btnSend.Click += new System.EventHandler(this.btnSend_Click);
+            // 
+            // txtSendMsg
+            // 
+            this.txtSendMsg.Location = new System.Drawing.Point(167, 245);
+            this.txtSendMsg.Name = "txtSendMsg";
+            this.txtSendMsg.Size = new System.Drawing.Size(322, 81);
+            this.txtSendMsg.TabIndex = 9;
+            this.txtSendMsg.Text = "";
+            // 
+            // txtChatMsg
+            // 
+            this.txtChatMsg.Location = new System.Drawing.Point(167, 25);
+            this.txtChatMsg.Name = "txtChatMsg";
+            this.txtChatMsg.Size = new System.Drawing.Size(403, 193);
+            this.txtChatMsg.TabIndex = 8;
+            this.txtChatMsg.Text = "";
+            this.txtChatMsg.WordWrap = false;
+            // 
+            // lb_online
+            // 
+            this.lb_online.AutoSize = true;
+            this.lb_online.Location = new System.Drawing.Point(9, 10);
+            this.lb_online.Name = "lb_online";
+            this.lb_online.Size = new System.Drawing.Size(65, 12);
+            this.lb_online.TabIndex = 7;
+            this.lb_online.Text = "在线列表:";
+            // 
+            // lbx_online
+            // 
+            this.lbx_online.FormattingEnabled = true;
+            this.lbx_online.ItemHeight = 12;
+            this.lbx_online.Location = new System.Drawing.Point(9, 25);
+            this.lbx_online.Name = "lbx_online";
+            this.lbx_online.Size = new System.Drawing.Size(125, 304);
+            this.lbx_online.TabIndex = 6;
+            // 
+            // TcpInfo
+            // 
+            this.TcpInfo.Location = new System.Drawing.Point(600, 25);
+            this.TcpInfo.Name = "TcpInfo";
+            this.TcpInfo.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
+            this.TcpInfo.Size = new System.Drawing.Size(234, 193);
+            this.TcpInfo.TabIndex = 12;
+            this.TcpInfo.Text = "";
+            this.TcpInfo.WordWrap = false;
+            // 
+            // lb_receive
+            // 
+            this.lb_receive.AutoSize = true;
+            this.lb_receive.Location = new System.Drawing.Point(165, 9);
+            this.lb_receive.Name = "lb_receive";
+            this.lb_receive.Size = new System.Drawing.Size(65, 12);
+            this.lb_receive.TabIndex = 13;
+            this.lb_receive.Text = "接收消息:";
+            // 
+            // lb_log
+            // 
+            this.lb_log.AutoSize = true;
+            this.lb_log.Location = new System.Drawing.Point(616, 9);
+            this.lb_log.Name = "lb_log";
+            this.lb_log.Size = new System.Drawing.Size(35, 12);
+            this.lb_log.TabIndex = 14;
+            this.lb_log.Text = "Log:";
+            // 
+            // lb_send
+            // 
+            this.lb_send.AutoSize = true;
+            this.lb_send.Location = new System.Drawing.Point(165, 229);
+            this.lb_send.Name = "lb_send";
+            this.lb_send.Size = new System.Drawing.Size(65, 12);
+            this.lb_send.TabIndex = 15;
+            this.lb_send.Text = "发送消息:";
+            // 
+            // ChatClient
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(846, 350);
+            this.Controls.Add(this.lb_send);
+            this.Controls.Add(this.lb_log);
+            this.Controls.Add(this.lb_receive);
+            this.Controls.Add(this.TcpInfo);
+            this.Controls.Add(this.btnClose);
+            this.Controls.Add(this.btnSend);
+            this.Controls.Add(this.txtSendMsg);
+            this.Controls.Add(this.txtChatMsg);
+            this.Controls.Add(this.lb_online);
+            this.Controls.Add(this.lbx_online);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+            this.Name = "ChatClient";
+            this.Text = "客户端";
+            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ChatClient_FormClosing);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Button btnClose;
+        private System.Windows.Forms.Button btnSend;
+        private System.Windows.Forms.RichTextBox txtSendMsg;
+        private System.Windows.Forms.RichTextBox txtChatMsg;
+        private System.Windows.Forms.Label lb_online;
+        private System.Windows.Forms.ListBox lbx_online;
+        private System.Windows.Forms.RichTextBox TcpInfo;
+        private System.Windows.Forms.Label lb_receive;
+        private System.Windows.Forms.Label lb_log;
+        private System.Windows.Forms.Label lb_send;
+    }
+}

+ 303 - 0
CSharp/SocketChat.Client/Views/ChatClient.cs

@@ -0,0 +1,303 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+using System.Linq;
+using System.Net.Sockets;
+using System.Threading;
+using System.Windows.Forms;
+using Newtonsoft.Json;
+using SocketChat.Common;
+
+namespace SocketChat.Client
+{
+    public partial class ChatClient : Form
+    {
+        #region 用户定义
+
+        private Socket _clientSocket;
+        private User _user;
+
+        private bool linkFlag = false; //对话状态
+        private bool uw = false; //写入对话内容
+        private string getMess; //从服务端接受的数据
+        private bool isExit = false; //连接状态 是否退出
+        private TcpClient client; //客户端
+        private NetworkStream networkStream; //传输流
+        private BinaryReader br; //读传输流
+        private BinaryWriter bw; //写传输流
+
+        private delegate void AddListBoxItemCallBack(string str);
+
+        private AddListBoxItemCallBack _addList;
+
+        private delegate void AppendChatMsgTextCallBack(string str);
+
+        private AppendChatMsgTextCallBack _appendChatMsgText;
+
+        private delegate void AppendTcpInfoCallBack(string str);
+
+        private AppendTcpInfoCallBack _appendTcpInfo;
+
+        //在线人数
+        private List<OnlineUser> _onlineUser = new List<OnlineUser>();
+
+        #endregion 用户定义
+
+        #region 系统函数
+
+        public ChatClient()
+        {
+            InitializeComponent();
+        }
+
+        public ChatClient(User v_user)
+        {
+            InitializeComponent();
+            this._user = v_user;
+            //建立网络通信
+            try
+            {
+                this.client = new TcpClient(this._user.p_serverIP, int.Parse(this._user.p_serverPort));//定义服务器端ip地址和端口,与服务器端定义要一致
+
+                this.linkFlag = true;
+            }
+            catch
+            {
+                return;
+            }
+            this._addList = new AddListBoxItemCallBack(this.AddListBoxItem);
+            this._appendChatMsgText = new AppendChatMsgTextCallBack(this.AppendChatMsgText);
+            if (this.linkFlag)
+            {
+                //获取网络流
+                networkStream = client.GetStream();
+                //将网络作为二进制读写对象,使用utf8编码
+                br = new BinaryReader(networkStream);
+                bw = new BinaryWriter(networkStream);
+
+                //发送套接字、编辑的内容、用户名、发送时间。
+                ClientMsgModel msg = new ClientMsgModel()
+                {
+                    IP = this._user.p_serverIP,
+                    Port = this._user.p_serverPort,
+                    Msg = this.txtSendMsg.Text,
+                    NowDate = DateTime.Now.ToString(),
+                    Type = "1",
+                    UserName = this._user.p_userName
+                };
+                //string sendMsg = ConvertJson.ToJson(msg);
+                string sendMsg = JsonConvert.SerializeObject(msg);
+
+                SendString(sendMsg);
+
+                ThreadStart ts = new ThreadStart(ReceiveData);
+                Thread thRece = new Thread(ts);
+                thRece.Start();
+            }
+            this.Text = this.Text + " 当前登录名:" + this._user.p_userName;
+        }
+
+        #endregion 系统函数
+
+        #region 用户函数
+
+        private void ReceiveData()
+        {
+            while (this.isExit == false)
+            {
+                string receiveString = null;
+                try
+                {
+                    //从网络流中读出字符串
+                    //此方法会自动判断字符串长度前缀,并根据长度前缀读出字符串
+                    receiveString = br.ReadString();
+                }
+                catch
+                {
+                    //底层套接字不存在时会出现异常
+                    //提示数据接收失败
+                    //TcpInfo.AppendText("接收数据失败");
+                    this.AppendTcpInfo("接收数据失败");
+                }
+                if (receiveString == null)
+                {
+                    if (isExit == false)
+                    {
+                        MessageBox.Show("与服务器失去联系!");
+                    }
+                    break;
+                }
+                uw = true;
+                getMess = receiveString;
+                //提示接收到的数据
+                //TcpInfo.AppendText(Environment.NewLine + "接收服务器数据:" + Environment.NewLine + receiveString);
+                ServerMsgModel __serverMsg = (ServerMsgModel)JsonConvert.DeserializeObject(receiveString, typeof(ServerMsgModel));
+
+                switch (__serverMsg.SendType)
+                {
+                    case "1":
+                        this._onlineUser = __serverMsg.OnlineUser;
+                        for (int i = 0; i < __serverMsg.OnlineUser.Count; i++)
+                        {
+                            this.AddListBoxItem(__serverMsg.OnlineUser[i].UserName);
+                        }
+                        break;
+
+                    case "2":
+                        this.AppendChatMsgText(__serverMsg.SendMsg);
+                        break;
+
+                    default: break;
+                }
+            }
+        }
+
+        private void SendString(string str)
+        {
+            try
+            {
+                //将字符串写入网络,此方法会自动附加字符串长度前缀
+                bw.Write(str);
+                bw.Flush();
+
+                //提示发送成功
+                TcpInfo.AppendText("发送:" + str);
+            }
+            catch
+            {
+                //提示发送失败
+                TcpInfo.AppendText("发送失败!");
+            }
+        }
+
+        public void AddListBoxItem(string str)
+        {
+            if (this.lbx_online.InvokeRequired)
+            {
+                if (this._addList != null)
+                {
+                    this.Invoke(_addList, str);
+                }
+            }
+            else
+            {
+                if (!this.lbx_online.Items.Contains(str))
+                {
+                    this.lbx_online.Items.Add(str);
+                }
+            }
+        }
+
+        public void AppendChatMsgText(string str)
+        {
+            if (this.txtChatMsg.InvokeRequired)
+            {
+                if (this._appendChatMsgText != null)
+                {
+                    this.txtChatMsg.Invoke(this._appendChatMsgText, str);
+                }
+            }
+            else
+            {
+                this.txtChatMsg.AppendText(str + Environment.NewLine);
+            }
+        }
+
+        public void AppendTcpInfo(string str)
+        {
+            if (this.TcpInfo.InvokeRequired)
+            {
+                if (this._appendTcpInfo != null)
+                {
+                    this.TcpInfo.Invoke(this._appendTcpInfo, str);
+                }
+            }
+            else
+            {
+                this.TcpInfo.AppendText(str);
+            }
+        }
+
+        #endregion 用户函数
+
+        #region 窗体事件
+
+        private void btnSend_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                //Func<OnlineUser, bool> b =
+                OnlineUser ol = this._onlineUser.Where(delegate (OnlineUser olUser)
+                {
+                    return olUser.UserName == this.lbx_online.SelectedItem.ToString();
+                }).FirstOrDefault();
+                //发送套接字、编辑的内容、用户名、发送时间。
+                ClientMsgModel msg = new ClientMsgModel()
+                {
+                    IP = this._user.p_serverIP,
+                    Port = this._user.p_serverPort,
+                    Msg = this.txtSendMsg.Text,
+                    NowDate = DateTime.Now.ToString(),
+                    Type = "2",
+                    UserName = this._user.p_userName,
+                    ReceiveIP = ol.IP,
+                    ReceivePort = ol.Port,
+                };
+                //string sendMsg = ConvertJson.ToJson(msg);
+                string sendMsg = JsonConvert.SerializeObject(msg);
+                SendString(sendMsg);
+                this.txtSendMsg.Clear();
+            }
+            catch (Exception ex)
+            {
+                throw ex;
+            }
+        }
+
+        private void btnClose_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                this.Close();
+            }
+            catch (Exception ex)
+            {
+                throw ex;
+            }
+        }
+
+        private void ChatClient_FormClosing(object sender, FormClosingEventArgs e)
+        {
+            try
+            {
+                if (this.client != null)
+                {
+                    //发送套接字、编辑的内容、用户名、发送时间。
+                    ClientMsgModel msg = new ClientMsgModel()
+                    {
+                        IP = this._user.p_serverIP,
+                        Port = this._user.p_serverPort,
+                        Msg = "SignOut",
+                        NowDate = DateTime.Now.ToString(),
+                        Type = "3",
+                        UserName = this._user.p_userName
+                    };
+                    //string sendMsg = ConvertJson.ToJson(msg);
+                    string sendMsg = JsonConvert.SerializeObject(msg);
+
+                    isExit = true;
+                    br.Close();
+                    bw.Close();
+                    this.client.Close();
+                }
+            }
+            catch (Exception ex)
+            {
+                throw ex;
+            }
+        }
+
+        #endregion 窗体事件
+    }
+}

+ 120 - 0
CSharp/SocketChat.Client/Views/ChatClient.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 129 - 0
CSharp/SocketChat.Client/Views/Login.Designer.cs

@@ -0,0 +1,129 @@
+namespace SocketChat.Client
+{
+    partial class Login
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.lblPort = new System.Windows.Forms.Label();
+            this.txtPort = new System.Windows.Forms.TextBox();
+            this.btnLogin = new System.Windows.Forms.Button();
+            this.txtIP = new System.Windows.Forms.TextBox();
+            this.txtLoginName = new System.Windows.Forms.TextBox();
+            this.lblIP = new System.Windows.Forms.Label();
+            this.lblLoginName = new System.Windows.Forms.Label();
+            this.SuspendLayout();
+            // 
+            // lblPort
+            // 
+            this.lblPort.AutoSize = true;
+            this.lblPort.Location = new System.Drawing.Point(43, 69);
+            this.lblPort.Name = "lblPort";
+            this.lblPort.Size = new System.Drawing.Size(35, 12);
+            this.lblPort.TabIndex = 13;
+            this.lblPort.Text = "Port:";
+            // 
+            // txtPort
+            // 
+            this.txtPort.Location = new System.Drawing.Point(77, 66);
+            this.txtPort.Name = "txtPort";
+            this.txtPort.Size = new System.Drawing.Size(100, 21);
+            this.txtPort.TabIndex = 12;
+            this.txtPort.Text = "51888";
+            // 
+            // btnLogin
+            // 
+            this.btnLogin.Location = new System.Drawing.Point(102, 93);
+            this.btnLogin.Name = "btnLogin";
+            this.btnLogin.Size = new System.Drawing.Size(75, 23);
+            this.btnLogin.TabIndex = 11;
+            this.btnLogin.Text = "登录";
+            this.btnLogin.UseVisualStyleBackColor = true;
+            this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click);
+            // 
+            // txtIP
+            // 
+            this.txtIP.Location = new System.Drawing.Point(77, 39);
+            this.txtIP.Name = "txtIP";
+            this.txtIP.Size = new System.Drawing.Size(100, 21);
+            this.txtIP.TabIndex = 10;
+            this.txtIP.Text = "127.0.0.1";
+            // 
+            // txtLoginName
+            // 
+            this.txtLoginName.Location = new System.Drawing.Point(77, 12);
+            this.txtLoginName.Name = "txtLoginName";
+            this.txtLoginName.Size = new System.Drawing.Size(100, 21);
+            this.txtLoginName.TabIndex = 9;
+            // 
+            // lblIP
+            // 
+            this.lblIP.AutoSize = true;
+            this.lblIP.Location = new System.Drawing.Point(43, 42);
+            this.lblIP.Name = "lblIP";
+            this.lblIP.Size = new System.Drawing.Size(23, 12);
+            this.lblIP.TabIndex = 8;
+            this.lblIP.Text = "IP:";
+            // 
+            // lblLoginName
+            // 
+            this.lblLoginName.AutoSize = true;
+            this.lblLoginName.Location = new System.Drawing.Point(19, 12);
+            this.lblLoginName.Name = "lblLoginName";
+            this.lblLoginName.Size = new System.Drawing.Size(47, 12);
+            this.lblLoginName.TabIndex = 7;
+            this.lblLoginName.Text = "登录名:";
+            // 
+            // Login
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(202, 128);
+            this.Controls.Add(this.lblPort);
+            this.Controls.Add(this.txtPort);
+            this.Controls.Add(this.btnLogin);
+            this.Controls.Add(this.txtIP);
+            this.Controls.Add(this.txtLoginName);
+            this.Controls.Add(this.lblIP);
+            this.Controls.Add(this.lblLoginName);
+            this.Name = "Login";
+            this.Text = "登录";
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Label lblPort;
+        private System.Windows.Forms.TextBox txtPort;
+        private System.Windows.Forms.Button btnLogin;
+        private System.Windows.Forms.TextBox txtIP;
+        private System.Windows.Forms.TextBox txtLoginName;
+        private System.Windows.Forms.Label lblIP;
+        private System.Windows.Forms.Label lblLoginName;
+    }
+}

+ 88 - 0
CSharp/SocketChat.Client/Views/Login.cs

@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace SocketChat.Client
+{
+    public partial class Login : Form
+    {
+        #region 用户定义
+        public Socket ClientSocket { get; set; }
+        public User p_user { get; set; } 
+        #endregion
+
+
+        #region 用户函数
+        public Login()
+        {
+            InitializeComponent();
+        } 
+        #endregion
+
+        #region 用户函数
+        #region 暂时不用
+        /// <summary>
+        /// 客户端接收数据
+        /// </summary>
+        /// <param name="socket"></param>
+        public void fnReceiveData(object socket)
+        {
+            var __clientSocket = socket as Socket;
+            byte[] __data = new byte[1024 * 1024];
+            while (true)
+            {
+                int __iLen = 0;
+                try
+                {
+                    __iLen = __clientSocket.Receive(__data, 0, __data.Length, SocketFlags.None);
+                }
+                catch (Exception)
+                {
+
+                    this.StopConnect();
+                    return;
+                }
+            }
+        }
+
+        private void StopConnect()
+        {
+            if (ClientSocket.Connected)
+            {
+                ClientSocket.Shutdown(SocketShutdown.Both);
+                ClientSocket.Close(100);
+            }
+        } 
+        #endregion
+        #endregion
+
+        #region 窗体事件
+        private void btnLogin_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                User __user = new User();
+
+                __user.p_serverIP = this.txtIP.Text;
+                __user.p_userName = this.txtLoginName.Text;
+                __user.p_serverPort = this.txtPort.Text;
+                this.p_user = __user;
+                this.DialogResult = DialogResult.OK;
+
+            }
+            catch (Exception ex)
+            {
+
+                throw ex;
+            }
+        } 
+        #endregion
+    }
+}

+ 120 - 0
CSharp/SocketChat.Client/Views/Login.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 4 - 0
CSharp/SocketChat.Client/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
+</packages>

+ 23 - 0
CSharp/SocketChat.Common/Model/ClientMsgModel.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketChat.Common
+{
+    /// <summary>
+    /// 客户端消息
+    /// </summary>
+    public class ClientMsgModel
+    {
+        public string IP { get; set; }
+        public string Port { get; set; }
+        public string UserName { get; set; }
+        public string Msg { get; set; }
+        public string Type { get; set; }//消息类型 1 登录 2 talk 3 退出
+        public string NowDate { get; set; }
+        public string ReceiveIP { get; set; }
+        public string ReceivePort { get; set; }
+    }
+}

+ 15 - 0
CSharp/SocketChat.Common/Model/OnlineUser.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketChat.Common
+{
+    public class OnlineUser
+    {
+        public string IP { get; set; }
+        public string Port { get; set; }
+        public string UserName { get; set; }
+    }
+}

+ 20 - 0
CSharp/SocketChat.Common/Model/ServerMsgModel.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketChat.Common
+{
+    public class ServerMsgModel
+    {
+        public string SendIP { get; set; }
+        public string SendPort { get; set; }
+        public string SendUserName { get; set; }
+        public string SendMsg { get; set; }
+        public string SendType { get; set; }//消息类型 1 登录 2 talk 3 退出
+        public string ReceiveIP { get; set; }
+        public string ReceivePort { get; set; }
+        public List<OnlineUser> OnlineUser { get; set; }
+    }
+}

+ 36 - 0
CSharp/SocketChat.Common/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的常规信息通过以下
+// 特性集控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("SocketChat.Common")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("SocketChat.Common")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 使此程序集中的类型
+// 对 COM 组件不可见。  如果需要从 COM 访问此程序集中的类型,
+// 则将该类型上的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("7c1dfb10-9c53-4835-955b-f85427a3cbee")]
+
+// 程序集的版本信息由下面四个值组成: 
+//
+//      主版本
+//      次版本 
+//      生成号
+//      修订号
+//
+// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
+// 方法是按如下所示使用“*”: 
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 61 - 0
CSharp/SocketChat.Common/SocketChat.Common.csproj

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{4F0BC491-47FC-40E8-BC36-03F0C936DC72}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SocketChat.Common</RootNamespace>
+    <AssemblyName>SocketChat.Common</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Model\ClientMsgModel.cs" />
+    <Compile Include="Model\OnlineUser.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Model\ServerMsgModel.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 4 - 0
CSharp/SocketChat.Common/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
+</packages>

+ 6 - 0
CSharp/SocketChat.Server/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+    </startup>
+</configuration>

+ 205 - 0
CSharp/SocketChat.Server/ChatServer.Designer.cs

@@ -0,0 +1,205 @@
+namespace SocketChat.Server
+{
+    partial class ChatServer
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.listBoxStatus = new System.Windows.Forms.RichTextBox();
+            this.label1 = new System.Windows.Forms.Label();
+            this.comboBoxReceiver = new System.Windows.Forms.ComboBox();
+            this.buttonSend = new System.Windows.Forms.Button();
+            this.lblPort = new System.Windows.Forms.Label();
+            this.txtPort = new System.Windows.Forms.TextBox();
+            this.txtIP = new System.Windows.Forms.TextBox();
+            this.lblIP = new System.Windows.Forms.Label();
+            this.btnStop = new System.Windows.Forms.Button();
+            this.btnStart = new System.Windows.Forms.Button();
+            this.txtSendMsg = new System.Windows.Forms.RichTextBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.label3 = new System.Windows.Forms.Label();
+            this.SuspendLayout();
+            // 
+            // listBoxStatus
+            // 
+            this.listBoxStatus.Location = new System.Drawing.Point(11, 26);
+            this.listBoxStatus.Name = "listBoxStatus";
+            this.listBoxStatus.Size = new System.Drawing.Size(358, 172);
+            this.listBoxStatus.TabIndex = 29;
+            this.listBoxStatus.Text = "";
+            this.listBoxStatus.WordWrap = false;
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(9, 302);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(53, 12);
+            this.label1.TabIndex = 28;
+            this.label1.Text = "接收方:";
+            // 
+            // comboBoxReceiver
+            // 
+            this.comboBoxReceiver.FormattingEnabled = true;
+            this.comboBoxReceiver.Location = new System.Drawing.Point(62, 299);
+            this.comboBoxReceiver.Name = "comboBoxReceiver";
+            this.comboBoxReceiver.Size = new System.Drawing.Size(227, 20);
+            this.comboBoxReceiver.TabIndex = 27;
+            // 
+            // buttonSend
+            // 
+            this.buttonSend.Location = new System.Drawing.Point(295, 298);
+            this.buttonSend.Name = "buttonSend";
+            this.buttonSend.Size = new System.Drawing.Size(75, 23);
+            this.buttonSend.TabIndex = 26;
+            this.buttonSend.Text = "发送";
+            this.buttonSend.UseVisualStyleBackColor = true;
+            this.buttonSend.Click += new System.EventHandler(this.buttonSend_Click);
+            // 
+            // lblPort
+            // 
+            this.lblPort.AutoSize = true;
+            this.lblPort.Location = new System.Drawing.Point(55, 357);
+            this.lblPort.Name = "lblPort";
+            this.lblPort.Size = new System.Drawing.Size(35, 12);
+            this.lblPort.TabIndex = 25;
+            this.lblPort.Text = "Port:";
+            // 
+            // txtPort
+            // 
+            this.txtPort.Location = new System.Drawing.Point(95, 354);
+            this.txtPort.Name = "txtPort";
+            this.txtPort.Size = new System.Drawing.Size(100, 21);
+            this.txtPort.TabIndex = 24;
+            this.txtPort.Text = "51888";
+            // 
+            // txtIP
+            // 
+            this.txtIP.Location = new System.Drawing.Point(95, 327);
+            this.txtIP.Name = "txtIP";
+            this.txtIP.Size = new System.Drawing.Size(100, 21);
+            this.txtIP.TabIndex = 23;
+            this.txtIP.Text = "127.0.0.1";
+            // 
+            // lblIP
+            // 
+            this.lblIP.AutoSize = true;
+            this.lblIP.Location = new System.Drawing.Point(55, 330);
+            this.lblIP.Name = "lblIP";
+            this.lblIP.Size = new System.Drawing.Size(23, 12);
+            this.lblIP.TabIndex = 22;
+            this.lblIP.Text = "IP:";
+            // 
+            // btnStop
+            // 
+            this.btnStop.Location = new System.Drawing.Point(214, 357);
+            this.btnStop.Name = "btnStop";
+            this.btnStop.Size = new System.Drawing.Size(75, 23);
+            this.btnStop.TabIndex = 21;
+            this.btnStop.Text = "停止服务";
+            this.btnStop.UseVisualStyleBackColor = true;
+            this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
+            // 
+            // btnStart
+            // 
+            this.btnStart.Location = new System.Drawing.Point(214, 325);
+            this.btnStart.Name = "btnStart";
+            this.btnStart.Size = new System.Drawing.Size(75, 23);
+            this.btnStart.TabIndex = 20;
+            this.btnStart.Text = "启动服务";
+            this.btnStart.UseVisualStyleBackColor = true;
+            this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
+            // 
+            // txtSendMsg
+            // 
+            this.txtSendMsg.Location = new System.Drawing.Point(12, 231);
+            this.txtSendMsg.Name = "txtSendMsg";
+            this.txtSendMsg.Size = new System.Drawing.Size(358, 61);
+            this.txtSendMsg.TabIndex = 30;
+            this.txtSendMsg.Text = "";
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(9, 213);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(65, 12);
+            this.label2.TabIndex = 31;
+            this.label2.Text = "发送消息:";
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(9, 9);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(35, 12);
+            this.label3.TabIndex = 32;
+            this.label3.Text = "Log:";
+            // 
+            // ChatServer
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(405, 400);
+            this.Controls.Add(this.label3);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.txtSendMsg);
+            this.Controls.Add(this.listBoxStatus);
+            this.Controls.Add(this.label1);
+            this.Controls.Add(this.comboBoxReceiver);
+            this.Controls.Add(this.buttonSend);
+            this.Controls.Add(this.lblPort);
+            this.Controls.Add(this.txtPort);
+            this.Controls.Add(this.txtIP);
+            this.Controls.Add(this.lblIP);
+            this.Controls.Add(this.btnStop);
+            this.Controls.Add(this.btnStart);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+            this.Name = "ChatServer";
+            this.Text = "服务器";
+            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ChatServer_FormClosing);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.RichTextBox listBoxStatus;
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.ComboBox comboBoxReceiver;
+        private System.Windows.Forms.Button buttonSend;
+        private System.Windows.Forms.Label lblPort;
+        private System.Windows.Forms.TextBox txtPort;
+        private System.Windows.Forms.TextBox txtIP;
+        private System.Windows.Forms.Label lblIP;
+        private System.Windows.Forms.Button btnStop;
+        private System.Windows.Forms.Button btnStart;
+        private System.Windows.Forms.RichTextBox txtSendMsg;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.Label label3;
+    }
+}

+ 421 - 0
CSharp/SocketChat.Server/ChatServer.cs

@@ -0,0 +1,421 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using Newtonsoft.Json;
+using SocketChat.Common;
+
+namespace SocketChat.Server
+{
+    public partial class ChatServer : Form
+    {
+        #region 用户定义
+
+        private List<Socket> _ClientProxSocketList = new List<Socket>();
+
+        //连接的用户
+        private List<User> userList = new List<User>();
+
+        private List<OnlineUser> onlineUser = new List<OnlineUser>();
+
+        private IPAddress localAddress;
+        private int port = 51888;
+
+        private delegate void SetListBoxCallBack(string str);
+
+        private SetListBoxCallBack setListBoxCallback;
+
+        private delegate void SetComboBoxCallBack(User user);
+
+        private SetComboBoxCallBack setComboBoxCallback;
+
+        private TcpListener myListener;
+        private ArrayList MessList = new ArrayList();
+        private int MessCount = 0;
+
+        #endregion 用户定义
+
+        #region 系统函数
+
+        public ChatServer()
+        {
+            InitializeComponent();
+            this.setListBoxCallback = new SetListBoxCallBack(SetListBox);
+            this.setComboBoxCallback = new SetComboBoxCallBack(AddComboBoxItem);
+            IPAddress[] addrIP = Dns.GetHostAddresses(this.txtIP.Text); //ip地址
+            localAddress = addrIP[0];
+        }
+
+        #endregion 系统函数
+
+        #region 用户函数
+
+        public void fnConnection()
+        {
+            #region 另一种监听方式 已注释
+
+            ////1.创建Socket对象
+            //Socket __serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+            ////2.绑定端口IP
+            //IPAddress __ip = IPAddress.Parse(this.txtIP.Text);
+            //__serverSocket.Bind(new IPEndPoint(__ip,int.Parse(this.txtPort.Text)));
+            ////3.开始侦听
+            ////连接等待的队列,最大为10个。
+            ////如果同时来的100个连接请求,只能处理1个,队列中放着10个等待连接的客户端,其他的返回错误消息。
+            //__serverSocket.Listen(100);
+            ////4.开始接收客户端的连接
+            //ThreadPool.QueueUserWorkItem(new WaitCallback(this.fnAcceptClientConnect), __serverSocket);
+
+            #endregion 另一种监听方式 已注释
+
+            myListener = new TcpListener(localAddress, int.Parse(this.txtPort.Text));
+            myListener.Start();
+            this.SetListBox(string.Format("开始在{0}:{1}监听客户连接", this.localAddress, this.port));
+            //创建一个线程监听客户端连接请求
+            ThreadStart ts = new ThreadStart(ListenClientConnect);
+            Thread myThread = new Thread(ts);
+            myThread.Start();
+            btnStart.Enabled = false;
+            btnStop.Enabled = true;
+        }
+
+        /// <summary>
+        /// 接收客户端连接
+        /// </summary>
+        private void ListenClientConnect()
+        {
+            while (true)
+            {
+                TcpClient newClient = null;
+                try
+                {
+                    //等待用户进入
+                    newClient = myListener.AcceptTcpClient();
+                }
+                catch (Exception)
+                {
+                    //当单击“停止监听”或者退出此窗体时AcceptTcpClient()会产生异常
+                    //因此可以利用此异常退出循环
+                    break;
+                }
+
+                //每接受一个客户端连接,就创建一个对应的线程循环接收该客户端发来的信息
+                ParameterizedThreadStart pts = new ParameterizedThreadStart(ReceiveData);
+                Thread threadReceive = new Thread(pts);
+                User user = new User(newClient);
+                threadReceive.Start(user);
+                userList.Add(user);
+                AddComboBoxItem(user);
+                SetListBox(string.Format("[{0}]进入", newClient.Client.RemoteEndPoint));
+                SetListBox(string.Format("当前连接用户数:{0}", userList.Count));
+            }
+        }
+
+        /// <summary>
+        /// 接收、处理客户端信息,每客户1个线程,参数用于区分是哪个客户
+        /// </summary>
+        /// <param name="obj"></param>
+        public void ReceiveData(object obj)
+        {
+            User user = obj as User;
+            TcpClient client = user.client;
+            //是否正常退出接收线程
+            bool normalExit = false;
+            //用于控制是否退出循环
+            bool exitWhile = false;
+            while (exitWhile == false)
+            {
+                string receiveString = null;
+                try
+                {
+                    //从网络流中读出字符串
+                    //此方法会自动判断字符串长度前缀,并根据长度前缀读出字符串
+                    receiveString = user.br.ReadString();
+                }
+                catch (Exception)
+                {
+                    //底层套接字不存在时会出现异常
+                    SetListBox("接收数据失败");
+                }
+
+                if (receiveString == null)
+                {
+                    if (normalExit == false)
+                    {
+                        //如果停止了监听,Connected为false
+                        if (client.Connected == true)
+                        {
+                            SetListBox(string.Format("与[{0}]失去联系,已终止接收该用户信息", client.Client.RemoteEndPoint));
+                        }
+                    }
+                    break;
+                }
+                SetListBox(string.Format("来自[{0}]:{1}", user.client.Client.RemoteEndPoint, receiveString));
+                ClientMsgModel msg = new ClientMsgModel();
+                msg = (ClientMsgModel)JsonConvert.DeserializeObject(receiveString, typeof(ClientMsgModel));
+
+                switch (msg.Type)
+                {
+                    case "1":
+                        this.SendLoginMsg(msg, user);
+                        break;
+
+                    case "2":
+                        User userReceive = this.userList.Where(delegate (User u)
+                        {
+                            return u.client.Client.RemoteEndPoint.ToString() == msg.ReceiveIP + ":" + msg.ReceivePort;
+                        }).FirstOrDefault();
+
+                        ServerMsgModel __serMsgModel = new ServerMsgModel();
+                        __serMsgModel.SendIP = (user.client.Client.RemoteEndPoint as IPEndPoint).Address.ToString();
+                        __serMsgModel.SendPort = (user.client.Client.RemoteEndPoint as IPEndPoint).Port.ToString();
+                        __serMsgModel.SendUserName = msg.UserName;
+                        __serMsgModel.SendType = msg.Type;
+                        __serMsgModel.SendMsg = msg.Msg;
+                        string __sendSerMsg = JsonConvert.SerializeObject(__serMsgModel);
+
+                        SendToClient(userReceive, __sendSerMsg);
+                        break;
+
+                    default: break;
+                }
+            }
+            userList.Remove(user);
+            client.Close();
+            SetListBox(string.Format("当前连接用户数:{0}", userList.Count));
+        }
+
+        private void SendTalkMsg()
+        {
+        }
+
+        /// <summary>
+        /// 服务器向客户端发消息
+        /// </summary>
+        /// <param name="msg">消息</param>
+        /// <param name="user">客户端</param>
+        public void SendLoginMsg(ClientMsgModel msg, User user)
+        {
+            //IPAddress ip = IPAddress.Parse(user.client.Client.RemoteEndPoint.ToString());
+
+            OnlineUser __onlineUser = new OnlineUser();
+            __onlineUser.IP = (user.client.Client.RemoteEndPoint as IPEndPoint).Address.ToString();
+            __onlineUser.Port = (user.client.Client.RemoteEndPoint as IPEndPoint).Port.ToString();
+            __onlineUser.UserName = msg.UserName;
+            if (!this.onlineUser.Contains(__onlineUser))
+            {
+                this.onlineUser.Add(__onlineUser);
+            }
+
+            SetListBox(string.Format("登录IP:{0},端口:{1}", msg.IP, msg.Port));
+
+            ServerMsgModel __serMsgModel = new ServerMsgModel();
+            __serMsgModel.SendIP = msg.IP;
+            __serMsgModel.SendPort = msg.Port;
+            __serMsgModel.SendUserName = msg.UserName;
+            __serMsgModel.SendType = msg.Type;
+            __serMsgModel.OnlineUser = this.onlineUser;
+            string __sendSerMsg = JsonConvert.SerializeObject(__serMsgModel);
+
+            for (int i = 0; i < this.userList.Count; i++)
+            {
+                SendToClient(userList[i], __sendSerMsg);
+            }
+        }
+
+        public void SendToClient(User user, string str)
+        {
+            try
+            {
+                user.bw.Write(str);
+                user.bw.Flush();
+                SetListBox(string.Format("向[{0}]发送:{1}", user.client.Client.RemoteEndPoint, str));
+            }
+            catch
+            {
+                SetListBox(string.Format("向[{0}]发送信息失败", user.client.Client.RemoteEndPoint));
+            }
+        }
+
+        private void SetListBox(string str)
+        {
+            if (this.listBoxStatus.InvokeRequired)
+            {
+                this.Invoke(this.setListBoxCallback, str);
+            }
+            else
+            {
+                this.listBoxStatus.AppendText(str + Environment.NewLine);
+            }
+        }
+
+        private void AddComboBoxItem(User user)
+        {
+            if (this.comboBoxReceiver.InvokeRequired)
+            {
+                this.Invoke(setComboBoxCallback, user);
+            }
+            else
+            {
+                this.comboBoxReceiver.Items.Add(user.client.Client.RemoteEndPoint);
+            }
+        }
+
+        #region 暂时不用
+
+        /// <summary>
+        /// 接收客户端连接  暂不用
+        /// </summary>
+        public void fnAcceptClientConnect(object v_socket)
+        {
+            var __serverSocket = v_socket as Socket;
+            Server.SetListBox("服务器端开始接收客户端链接");
+            while (true)
+            {
+                Socket __proxSocket = __serverSocket.Accept();
+                Server.SetListBox(string.Format("客户端:{0}连接上了", __proxSocket.RemoteEndPoint.ToString()));
+
+                this._ClientProxSocketList.Add(__proxSocket);
+
+                //不停的接收当前链接的客户端发送来的消息
+                ThreadPool.QueueUserWorkItem(new WaitCallback(this.fnReceiveData), __proxSocket);
+            }
+        }
+
+        /// <summary>
+        /// 接收客户端的消息
+        /// </summary>
+        /// <param name="v_socket"></param>
+        public void fnReceiveData(object v_socket)
+        {
+            var __proxSocket = v_socket as Socket;
+            byte[] data = new byte[1024 * 1024];
+            while (true)
+            {
+                int __iLen = 0;
+                try
+                {
+                    __iLen = __proxSocket.Receive(data, 0, data.Length, SocketFlags.None);
+                }
+                catch (Exception)
+                {
+                    //异常退出
+                    Server.SetListBox(string.Format("客户端:{0}非正常退出",
+                        __proxSocket.RemoteEndPoint.ToString()));
+                    this._ClientProxSocketList.Remove(__proxSocket);
+
+                    this.fnStopConnect(__proxSocket);
+                    return;
+                }
+
+                if (__iLen <= 0)
+                {
+                    //客户端正常退出
+                    Server.SetListBox(string.Format("客户端:{0}正常退出",
+                        __proxSocket.RemoteEndPoint.ToString()));
+
+                    this._ClientProxSocketList.Remove(__proxSocket);
+                    this.fnStopConnect(__proxSocket);//停止连接
+                    return;//让方法结束,终结当前接收客户端数据的异步线程。
+                }
+            }
+        }
+
+        private void fnStopConnect(Socket proxSocket)
+        {
+            try
+            {
+                if (proxSocket.Connected)
+                {
+                    proxSocket.Shutdown(SocketShutdown.Both);
+                    proxSocket.Close(100);
+                }
+            }
+            catch (Exception ex)
+            {
+                throw;
+            }
+        }
+
+        private void StopServer()
+        {
+            try
+            {
+                SetListBox(string.Format("目前连接用户数:{0}", userList.Count));
+                SetListBox("开始停止服务,并依次使用户退出!");
+                for (int i = 0; i < userList.Count; i++)
+                {
+                    comboBoxReceiver.Items.Remove(userList[i].client.Client.RemoteEndPoint);
+                    userList[i].br.Close();
+                    userList[i].bw.Close();
+                    userList[i].client.Close();
+                }
+                //通过停止监听让myListener.AcceptTcpClient()产生异常退出监听线程
+                myListener.Stop();
+                this.btnStart.Enabled = true;
+                this.btnStop.Enabled = false;
+            }
+            catch (Exception ex)
+            {
+                throw ex;
+            }
+        }
+
+        #endregion 暂时不用
+
+        #endregion 用户函数
+
+        #region 窗体事件
+
+        private void buttonSend_Click(object sender, EventArgs e)
+        {
+            User clientUser = new User(new TcpClient());
+            foreach (User u in userList)
+            {
+                if (u.client.Client.RemoteEndPoint.ToString() == comboBoxReceiver.SelectedText)
+                {
+                    clientUser = u;
+                }
+            }
+            if (clientUser != null)
+            {
+                SendToClient(clientUser, txtSendMsg.Text.Trim());
+            }
+        }
+
+        private void ChatServer_FormClosing(object sender, FormClosingEventArgs e)
+        {
+            //关闭server线程
+            StopServer();
+            Application.Exit();
+        }
+
+        private void btnStart_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                this.fnConnection();
+            }
+            catch (Exception ex)
+            {
+                throw ex;
+            }
+        }
+
+        private void btnStop_Click(object sender, EventArgs e)
+        {
+            StopServer();
+        }
+
+        #endregion 窗体事件
+    }
+}

+ 120 - 0
CSharp/SocketChat.Server/ChatServer.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 36 - 0
CSharp/SocketChat.Server/Model/Server.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace SocketChat.Server
+{
+    public class Server
+    {
+        private static ListBox _listbox;
+        public delegate void ListBoxCallBack(string str);
+        public Server(ListBox v_listbox)
+        {
+            _listbox = v_listbox;
+        }
+
+
+        public static void SetListBox(string str)
+        {
+            if (_listbox.InvokeRequired)
+            {
+                ListBoxCallBack __listCallBack = new ListBoxCallBack(SetListBox);
+                _listbox.Invoke(__listCallBack, str);
+            }
+            else
+            {
+                _listbox.Items.Add(str);
+                _listbox.SelectedIndex = _listbox.Items.Count - 1;
+                _listbox.ClearSelected();
+            }
+
+        }
+    }
+}

+ 25 - 0
CSharp/SocketChat.Server/Model/User.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketChat.Server
+{
+    public class User
+    {
+        public TcpClient client;
+        public BinaryReader br;
+        public BinaryWriter bw;
+
+        public User(TcpClient client)
+        {
+            this.client = client;
+            NetworkStream networkStream = this.client.GetStream();
+            br = new BinaryReader(networkStream);
+            bw = new BinaryWriter(networkStream);
+        }
+    }
+}

+ 22 - 0
CSharp/SocketChat.Server/Program.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace SocketChat.Server
+{
+    static class Program
+    {
+        /// <summary>
+        /// 应用程序的主入口点。
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            Application.Run(new ChatServer());
+        }
+    }
+}

+ 36 - 0
CSharp/SocketChat.Server/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的常规信息通过以下
+// 特性集控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("SocketChat.Server")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("SocketChat.Server")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 使此程序集中的类型
+// 对 COM 组件不可见。  如果需要从 COM 访问此程序集中的类型,
+// 则将该类型上的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("2f751c98-9317-41ca-ade8-1283a56d487b")]
+
+// 程序集的版本信息由下面四个值组成: 
+//
+//      主版本
+//      次版本 
+//      生成号
+//      修订号
+//
+// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
+// 方法是按如下所示使用“*”: 
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 71 - 0
CSharp/SocketChat.Server/Properties/Resources.Designer.cs

@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本: 4.0.30319.18408
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace SocketChat.Server.Properties
+{
+
+
+    /// <summary>
+    ///   一个强类型的资源类,用于查找本地化的字符串等。
+    /// </summary>
+    // 此类是由 StronglyTypedResourceBuilder
+    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+    // (以 /str 作为命令选项),或重新生成 VS 项目。
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources
+    {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources()
+        {
+        }
+
+        /// <summary>
+        ///   返回此类使用的、缓存的 ResourceManager 实例。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager
+        {
+            get
+            {
+                if ((resourceMan == null))
+                {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SocketChat.Server.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   为所有资源查找重写当前线程的 CurrentUICulture 属性,
+        ///   方法是使用此强类型资源类。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture
+        {
+            get
+            {
+                return resourceCulture;
+            }
+            set
+            {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 117 - 0
CSharp/SocketChat.Server/Properties/Resources.resx

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 30 - 0
CSharp/SocketChat.Server/Properties/Settings.Designer.cs

@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.18408
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace SocketChat.Server.Properties
+{
+
+
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+    {
+
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+        public static Settings Default
+        {
+            get
+            {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 7 - 0
CSharp/SocketChat.Server/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 100 - 0
CSharp/SocketChat.Server/SocketChat.Server.csproj

@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{B28AB997-6D41-415F-BD7A-60972FE27555}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SocketChat.Server</RootNamespace>
+    <AssemblyName>SocketChat.Server</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ChatServer.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="ChatServer.Designer.cs">
+      <DependentUpon>ChatServer.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Model\Server.cs" />
+    <Compile Include="Model\User.cs" />
+    <EmbeddedResource Include="ChatServer.resx">
+      <DependentUpon>ChatServer.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <None Include="packages.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\SocketChat.Common\SocketChat.Common.csproj">
+      <Project>{4f0bc491-47fc-40e8-bc36-03f0c936dc72}</Project>
+      <Name>SocketChat.Common</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 4 - 0
CSharp/SocketChat.Server/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
+</packages>

+ 37 - 0
CSharp/SocketChat.sln

@@ -0,0 +1,37 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.1145
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketChat.Client", "SocketChat.Client\SocketChat.Client.csproj", "{79337939-498C-41FE-BEBE-76FCAB5F2B6E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketChat.Server", "SocketChat.Server\SocketChat.Server.csproj", "{B28AB997-6D41-415F-BD7A-60972FE27555}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketChat.Common", "SocketChat.Common\SocketChat.Common.csproj", "{4F0BC491-47FC-40E8-BC36-03F0C936DC72}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{79337939-498C-41FE-BEBE-76FCAB5F2B6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{79337939-498C-41FE-BEBE-76FCAB5F2B6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{79337939-498C-41FE-BEBE-76FCAB5F2B6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{79337939-498C-41FE-BEBE-76FCAB5F2B6E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B28AB997-6D41-415F-BD7A-60972FE27555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B28AB997-6D41-415F-BD7A-60972FE27555}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B28AB997-6D41-415F-BD7A-60972FE27555}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B28AB997-6D41-415F-BD7A-60972FE27555}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4F0BC491-47FC-40E8-BC36-03F0C936DC72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4F0BC491-47FC-40E8-BC36-03F0C936DC72}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4F0BC491-47FC-40E8-BC36-03F0C936DC72}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4F0BC491-47FC-40E8-BC36-03F0C936DC72}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {10758A48-CD72-46E5-9DF4-A210C55EAF3C}
+	EndGlobalSection
+EndGlobal