热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

最近也在接触SOCKET编程,在当今这样一个网络时代,很多技术都以网络为中心在诞生,至少我认为是这样的,而SOCKET套接字

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

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

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

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

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

Socket02.JPG

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

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

服务器端代码:

  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         /// 
 41         /// 获得XML文件中的端口号
 42         /// 

 43         /// 
 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 = "服务正在运行dot.gif"+"  运行端口:"+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, 0new 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 &#61; 0; i < ClientNumb; i&#43;&#43;)
107                 {
108                     if (ClientSocket[i].Connected)
109                     {
110                         ClientSocket[i].Send(MsgBuffer, 0, REnd,0);
111                     }
112                     RSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0new 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 &#61; true;
126             this.CmdStop.Enabled &#61; false;
127             this.StateMsg.Text &#61; "等待运行dot.gif";
128             this.ClientList.Items.Add("服务于 " &#43; DateTime.Now.ToString() &#43; " 停止运行.");
129         }
130 
131 
132 
133     }
134 }


客户端代码&#xff1a;

  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 &#61; false;
 29             this.CmdExit.Enabled &#61; false;
 30 
 31             ClientSocket &#61; new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 32             MsgBuffer &#61; new Byte[65535];
 33             MsgSend &#61; new Byte[65535];
 34             CheckForIllegalCrossThreadCalls &#61; false;
 35 
 36             Random TRand&#61;new Random();
 37             this.UserName.Text &#61; "用户" &#43; TRand.Next(10000).ToString();
 38         }
 39 
 40         private void CmdEnter_Click(object sender, EventArgs e)
 41         {
 42             ServerInfo &#61; 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("用户&#xff1a; " &#43; this.UserName.Text &#43; " 进入系统&#xff01;\n"));
 49 
 50                 ClientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0new AsyncCallback(ReceiveCallBack), null);
 51 
 52                 this.SysMsg.Text &#43;&#61; "登录服务器成功&#xff01;\n";
 53                 this.CmdSend.Enabled &#61; true;
 54                 this.CmdEnter.Enabled &#61; false;
 55                 this.CmdExit.Enabled &#61; true;
 56             }
 57             catch
 58             {
 59                 MessageBox.Show("登录服务器失败&#xff0c;请确认服务器是否正常工作&#xff01;");
 60             }
 61         }
 62 
 63         private void ReceiveCallBack(IAsyncResult AR)
 64         {
 65             try
 66             {
 67                 int REnd &#61; ClientSocket.EndReceive(AR);
 68                 this.RecieveMsg.AppendText(Encoding.Unicode.GetString(MsgBuffer, 0, REnd));
 69                 ClientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0new AsyncCallback(ReceiveCallBack), null);
 70 
 71             }
 72             catch
 73             {
 74                 MessageBox.Show("已经与服务器断开连接&#xff01;");
 75                 this.Close();
 76             }
 77 
 78         }
 79 
 80         private void CmdSend_Click(object sender, EventArgs e)
 81         {
 82             MsgSend &#61; Encoding.Unicode.GetBytes(this.UserName.Text &#43; "说&#xff1a;\n" &#43; this.SendMsg.Text &#43; "\n");
 83             if (ClientSocket.Connected)
 84             {
 85                 ClientSocket.Send(MsgSend);
 86                 this.SendMsg.Text &#61; "";
 87             }
 88             else
 89             {
 90                 MessageBox.Show("当前与服务器断开连接&#xff0c;无法发送信息&#xff01;");
 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 &#43; "离开了房间&#xff01;\n"));
 99                 ClientSocket.Shutdown(SocketShutdown.Both);
100                 ClientSocket.Disconnect(false);
101             }
102             ClientSocket.Close();
103 
104             this.CmdSend.Enabled &#61; false;
105             this.CmdEnter.Enabled &#61; true;
106             this.CmdExit.Enabled &#61; 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 &#61;&#61; 13)
117             {
118                 e.Handled &#61; true;
119                 this.CmdSend_Click(thisnull);
120             }
121         }
122 
123 
124 
125 
126     }
127 }


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

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

希望本人给的这个程序可以起到一个抛砖引玉的作用&#xff0c;不明白之处可以加QQ&#xff08;17020415&#xff09;或留言。

备注&#xff1a;

//2007-12-01

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

xml version&#61;"1.0" encoding&#61;"utf-8" ?> 
<Server>
  
<ServerPort>6600ServerPort>
Server>


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



推荐阅读
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 本文介绍了如何使用iptables添加非对称的NAT规则段,以实现内网穿透和端口转发的功能。通过查阅相关文章,得出了解决方案,即当匹配的端口在映射端口的区间内时,可以成功进行端口转发。详细的操作步骤和命令示例也在文章中给出。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
author-avatar
轰炸籹厕所744
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有