C#下如何实现服务器+客户端的聊天程序

简介:

最近也在接触SOCKET编程,在当今这样一个网络时代,很多技术都以网络为中心在诞生,至少我认为是这样的,而SOCKET套接字接口,在实现网络通讯上处于关键地位,所以不会SOCKET是不行的。

首先,本文主要是针对那些刚接触SOCKET编程的朋友,如果是高手,就可以不看此文啦,可以去陪陪老婆,比如逛街或看电视...

在开始之前,我们需要预习一些基础知识:
什么是SOCKET套接字?
SOCKET通常有那几种数据格式?
线程的概念?
(以上基本知识我就不讲了,网上这方面资料很多的,大家找资料看下吧)

我要介绍的是一个服务器端+客户端的聊天系统,程序比较简单,我先把程序运行的界面给大家看下:


上面是服务器端运行界面;下面把客户端界面贴给大家看下:



功能比较简单,服务器的端口号可以在“系统菜单”里面的参数配置进行修改的。

看了上面的图,下面我们就给大家把代码贴出来:(因为程序比较简单,所以本人就没有去分层啦)

服务器端代码:

  1  using  System;
  2  using  System.Collections.Generic;
  3  using  System.ComponentModel;
  4  using  System.Data;
  5  using  System.Drawing;
  6  using  System.Text;
  7  using  System.Windows.Forms;
  8 
  9  using  System.Net;
 10  using  System.Net.Sockets;
 11  using  System.Threading;
 12  using  System.Xml;
 13 
 14  namespace  Server
 15  {
 16       public  partial  class  ServerMain : Form
 17      {
 18           public  ServerMain()
 19          {
 20              InitializeComponent();
 21          }
 22 
 23           private   void  ServerMain_Load( object  sender, EventArgs e)
 24          {
 25               this .CmdStar.Enabled  =   true ;
 26               this .CmdStop.Enabled  =   false ;
 27          }
 28 
 29           private   void  配置参数ToolStripMenuItem_Click( object  sender, EventArgs e)
 30          {
 31              Set TSet  =   new  Set();
 32              TSet.ShowDialog();
 33          }
 34 
 35           private   void  关于ToolStripMenuItem_Click( object  sender, EventArgs e)
 36          {
 37              About TAbout  =   new  About();
 38              TAbout.Show();
 39          }
 40           ///   <summary>
 41           ///  获得XML文件中的端口号
 42           ///   </summary>
 43           ///   <returns></returns>
 44           private   int  GetPort()
 45          {
 46               try
 47              {
 48                  XmlDocument TDoc  =   new  XmlDocument();
 49                  TDoc.Load( " Settings.xml " );
 50                   string  TPort  =  TDoc.GetElementsByTagName( " ServerPort " )[ 0 ].InnerXml;
 51                   return  Convert.ToInt32(TPort);
 52 
 53              }
 54               catch  {  return   6600 ; } // 默认是6600
 55          }
 56 
 57           // 声明将要用到的类
 58           private  IPEndPoint ServerInfo; // 存放服务器的IP和端口信息
 59           private  Socket ServerSocket; // 服务端运行的SOCKET
 60           private  Thread ServerThread; // 服务端运行的线程
 61           private  Socket[] ClientSocket; // 为客户端建立的SOCKET连接
 62           private   int  ClientNumb; // 存放客户端数量
 63           private   byte [] MsgBuffer; // 存放消息数据
 64 
 65           private   void  CmdStar_Click( object  sender, EventArgs e)
 66          {
 67              ServerSocket  =   new  Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 68              ServerInfo = new  IPEndPoint(IPAddress.Any, this .GetPort());
 69              ServerSocket.Bind(ServerInfo); // 将SOCKET接口和IP端口绑定
 70              ServerSocket.Listen( 10 ); // 开始监听,并且挂起数为10
 71 
 72              ClientSocket  =   new  Socket[ 65535 ]; // 为客户端提供连接个数
 73              MsgBuffer  =   new   byte [ 65535 ]; // 消息数据大小
 74              ClientNumb  =   0 ; // 数量从0开始统计
 75 
 76              ServerThread  =   new  Thread(RecieveAccept); // 将接受客户端连接的方法委托给线程
 77              ServerThread.Start(); // 线程开始运行
 78 
 79              CheckForIllegalCrossThreadCalls  =   false ; // 不捕获对错误线程的调用
 80 
 81               this .CmdStar.Enabled  =   false ;
 82               this .CmdStop.Enabled  =   true ;
 83               this .StateMsg.Text  =   " 服务正在运行 " + "   运行端口: " + this .GetPort().ToString();
 84               this .ClientList.Items.Add( " 服务于  "   +  DateTime.Now.ToString()  +   "  开始运行. " );
 85          }
 86          
 87           // 接受客户端连接的方法
 88           private   void  RecieveAccept()
 89          {
 90               while  ( true )
 91              {
 92                  ClientSocket[ClientNumb]  =  ServerSocket.Accept();
 93                  ClientSocket[ClientNumb].BeginReceive(MsgBuffer,  0 , MsgBuffer.Length,  0 new  AsyncCallback(RecieveCallBack),ClientSocket[ClientNumb]);
 94                   this .ClientList.Items.Add(ClientSocket[ClientNumb].RemoteEndPoint.ToString() + "  成功连接服务器. " );
 95                  ClientNumb ++ ;
 96              }
 97          }
 98 
 99           // 回发数据给客户端
100           private   void  RecieveCallBack(IAsyncResult AR)
101          {
102               try
103              {
104                  Socket RSocket  =  (Socket)AR.AsyncState;
105                   int  REnd  =  RSocket.EndReceive(AR);
106                   for  ( int  i  =   0 ; i  <  ClientNumb; i ++ )
107                  {
108                       if  (ClientSocket[i].Connected)
109                      {
110                          ClientSocket[i].Send(MsgBuffer,  0 , REnd, 0 );
111                      }
112                      RSocket.BeginReceive(MsgBuffer,  0 , MsgBuffer.Length,  0 new  AsyncCallback(RecieveCallBack), RSocket);
113 
114                  }
115              }
116               catch  { }
117 
118          }
119 
120           private   void  CmdStop_Click( object  sender, EventArgs e)
121          {
122              ServerThread.Abort(); // 线程终止
123              ServerSocket.Close(); // 关闭SOCKET
124 
125               this .CmdStar.Enabled  =   true ;
126               this .CmdStop.Enabled  =   false ;
127               this .StateMsg.Text  =   " 等待运行 " ;
128               this .ClientList.Items.Add( " 服务于  "   +  DateTime.Now.ToString()  +   "  停止运行. " );
129          }
130 
131 
132 
133      }
134  }


客户端代码:

  1  using  System;
  2  using  System.Collections.Generic;
  3  using  System.ComponentModel;
  4  using  System.Data;
  5  using  System.Drawing;
  6  using  System.Text;
  7  using  System.Windows.Forms;
  8 
  9  using  System.Net;
 10  using  System.Net.Sockets;
 11 
 12  namespace  Client
 13  {
 14       public  partial  class  ClientMain : Form
 15      {
 16           public  ClientMain()
 17          {
 18              InitializeComponent();
 19          }
 20 
 21           private  IPEndPoint ServerInfo;
 22           private  Socket ClientSocket;
 23           private  Byte[] MsgBuffer;
 24           private  Byte[] MsgSend;
 25 
 26           private   void  ClientMain_Load( object  sender, EventArgs e)
 27          {
 28               this .CmdSend.Enabled  =   false ;
 29               this .CmdExit.Enabled  =   false ;
 30 
 31              ClientSocket  =   new  Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 32              MsgBuffer  =   new  Byte[ 65535 ];
 33              MsgSend  =   new  Byte[ 65535 ];
 34              CheckForIllegalCrossThreadCalls  =   false ;
 35 
 36              Random TRand = new  Random();
 37               this .UserName.Text  =   " 用户 "   +  TRand.Next( 10000 ).ToString();
 38          }
 39 
 40           private   void  CmdEnter_Click( object  sender, EventArgs e)
 41          {
 42              ServerInfo  =   new  IPEndPoint(IPAddress.Parse( this .ServerIP.Text), Convert.ToInt32( this .ServerPort.Text));
 43 
 44               try
 45              {
 46                  ClientSocket.Connect(ServerInfo);
 47 
 48                  ClientSocket.Send(Encoding.Unicode.GetBytes( " 用户:  "   +   this .UserName.Text  +   "  进入系统!\n " ));
 49 
 50                  ClientSocket.BeginReceive(MsgBuffer,  0 , MsgBuffer.Length,  0 new  AsyncCallback(ReceiveCallBack),  null );
 51 
 52                   this .SysMsg.Text  +=   " 登录服务器成功!\n " ;
 53                   this .CmdSend.Enabled  =   true ;
 54                   this .CmdEnter.Enabled  =   false ;
 55                   this .CmdExit.Enabled  =   true ;
 56              }
 57               catch
 58              {
 59                  MessageBox.Show( " 登录服务器失败,请确认服务器是否正常工作! " );
 60              }
 61          }
 62 
 63           private   void  ReceiveCallBack(IAsyncResult AR)
 64          {
 65               try
 66              {
 67                   int  REnd  =  ClientSocket.EndReceive(AR);
 68                   this .RecieveMsg.AppendText(Encoding.Unicode.GetString(MsgBuffer,  0 , REnd));
 69                  ClientSocket.BeginReceive(MsgBuffer,  0 , MsgBuffer.Length,  0 new  AsyncCallback(ReceiveCallBack),  null );
 70 
 71              }
 72               catch
 73              {
 74                  MessageBox.Show( " 已经与服务器断开连接! " );
 75                   this .Close();
 76              }
 77 
 78          }
 79 
 80           private   void  CmdSend_Click( object  sender, EventArgs e)
 81          {
 82              MsgSend  =  Encoding.Unicode.GetBytes( this .UserName.Text  +   " 说:\n "   +   this .SendMsg.Text  +   " \n " );
 83               if  (ClientSocket.Connected)
 84              {
 85                  ClientSocket.Send(MsgSend);
 86                   this .SendMsg.Text  =   "" ;
 87              }
 88               else
 89              {
 90                  MessageBox.Show( " 当前与服务器断开连接,无法发送信息! " );
 91              }
 92          }
 93 
 94           private   void  CmdExit_Click( object  sender, EventArgs e)
 95          {
 96               if  (ClientSocket.Connected)
 97              {
 98                  ClientSocket.Send(Encoding.Unicode.GetBytes( this .UserName.Text  +   " 离开了房间!\n " ));
 99                  ClientSocket.Shutdown(SocketShutdown.Both);
100                  ClientSocket.Disconnect( false );
101              }
102              ClientSocket.Close();
103 
104               this .CmdSend.Enabled  =   false ;
105               this .CmdEnter.Enabled  =   true ;
106               this .CmdExit.Enabled  =   false ;
107          }
108 
109           private   void  RecieveMsg_TextChanged( object  sender, EventArgs e)
110          {
111               this .RecieveMsg.ScrollToCaret();
112          }
113 
114           private   void  SendMsg_KeyDown( object  sender, KeyEventArgs e)
115          {
116               if  (e.Control  &&  e.KeyValue  ==   13 )
117              {
118                  e.Handled  =   true ;
119                   this .CmdSend_Click( this null );
120              }
121          }
122 
123 
124 
125 
126      }
127  }


我只对服务器端的代码做了注释,客户端就没有写注释了,因为代码是差不多的。区别在于客户端不需要监听,也不需要启用线程进行委托。
关于 ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
这句代码,我想给初学者解释一下,这里“AddressFamily.InterNetwork”表示的是使用IPV4地址,“SocketType.Stream”表示使用的是流格式(另外还有数据包格式和原始套接字格式),“ProtocolType.Tcp”表示使用TCP协议(另外还有很多其它协议,例如大家常看到的UDP协议)。

另外关于SOCKET类中的BeginReceive方法,请大家参考MSDN,里面有详细说明。

希望本人给的这个程序可以起到一个抛砖引玉的作用,不明白之处可以加QQ(17020415)或留言。

备注:

//2007-12-01

//今天有朋友加我QQ问我有关服务端“Settings.xml”文件的内容部分,我现在把内容贴出来,其实很简单,就是方便服务端修改端口的。

<? xml version="1.0" encoding="utf-8"  ?>  
< Server >
  
< ServerPort > 6600 </ ServerPort >
</ Server >


完整的源码我已经放在CSDN上面共享了,地址:http://download.csdn.net/user/lixyvip




本文转自黄聪博客园博客,原文链接:http://www.cnblogs.com/huangcong/archive/2010/03/27/1698434.html,如需转载请自行联系原作者

相关文章
|
21天前
|
设计模式 IDE API
C# 一分钟浅谈:GraphQL 客户端调用
本文介绍了如何在C#中调用GraphQL API,涵盖基本步骤、常见问题及解决方案。首先,通过安装`GraphQL.Client`库并创建客户端实例,连接到GraphQL服务器。接着,展示了如何编写查询和突变,以及处理查询语法错误、变量类型不匹配等常见问题。最后,通过具体案例(如管理用户和订单)演示了如何在实际项目中应用这些技术,帮助开发者更高效地利用GraphQL。
62 38
C# 一分钟浅谈:GraphQL 客户端调用
|
7天前
|
存储 人工智能 自然语言处理
ChatMCP:基于 MCP 协议开发的 AI 聊天客户端,支持多语言和自动化安装 MCP 服务器
ChatMCP 是一款基于模型上下文协议(MCP)的 AI 聊天客户端,支持多语言和自动化安装。它能够与多种大型语言模型(LLM)如 OpenAI、Claude 和 OLLama 等进行交互,具备自动化安装 MCP 服务器、SSE 传输支持、自动选择服务器、聊天记录管理等功能。
68 14
ChatMCP:基于 MCP 协议开发的 AI 聊天客户端,支持多语言和自动化安装 MCP 服务器
|
24天前
|
设计模式 API 数据处理
C# 一分钟浅谈:GraphQL 客户端调用
本文介绍了如何在C#中使用`GraphQL.Client`库调用GraphQL API,涵盖基本查询、变量使用、批量请求等内容,并详细说明了常见问题及其解决方法,帮助开发者高效利用GraphQL的强大功能。
94 57
|
27天前
|
开发框架 .NET C#
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
38 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
|
25天前
|
设计模式 API C#
C# 一分钟浅谈:GraphQL 服务器端实现
本文通过C#语言从零开始构建一个简单的GraphQL服务器端实现,介绍了环境准备、项目创建、定义Schema、配置GraphQL等步骤。同时,探讨了常见问题如数据源问题、类型定义不一致、性能问题和权限控制,提供了相应的解决方法。希望帮助读者更好地理解和应用GraphQL。
25 3
|
2月前
|
Python
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
使用Python的socket库实现客户端到服务器端的图片传输,包括客户端和服务器端的代码实现,以及传输结果的展示。
164 3
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
|
2月前
|
网络协议 Unix Linux
一个.NET开源、快速、低延迟的异步套接字服务器和客户端库
一个.NET开源、快速、低延迟的异步套接字服务器和客户端库
|
2月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
66 1
|
2月前
|
安全 区块链 数据库
|
1月前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
35 3
下一篇
DataWorks