博文
从这里开始认识 MS Workflow Foundation(即 WF)(2007-11-23 9:26:00)
从这里开始认识 MS Workflow Foundation(即 WF)
最近正在学习WF,收集了一些资料,再此也提供出来供有兴趣的朋友参考。
1.系统必备
—我们需要安装WinFX(下载)和Visual Studio 2005 extensions for .NET Framework 3.0 (Windows Workflow Foundation)。这是我们必备的开发组件。
—WF作为net3.0的一部分发布,要运行需要安装net3.0
— WF提供了一个在VS2005中可视化设计工作流的设计器,Visual Studio 2005 Extensions for Windows Workflow Foundation,只有安装后才能在VS2005中建立WF项目。
2.WF是什么?
—WF本身并不是一个业务平台,它只是为开发人员提供创建工作流软件的一个平台,也就是说WF不是工作流应用平台,而是一个开发工作流应用的平台。
—MS将WF作为Vista的一个基本API发布,而不是一个独立的软件(如BizTalk,Office)。
3.WF适合做什么?
—使用WF可以开发具有以下特点的工作流平台:
1.业务流程辅助办公软件
2.软件内部工作的顺序控制
3.自动筛选查询类系统
4.自动化控制中
4.WF的工作模式
—WF提供了两类工作流模式,流模式(Sequential)与状态机模式(State Machine ):
—顺序工作流 非常适合以下类型的操作,即该操作由依次执行直至最后一个活动完成的步骤的管线表示。但是,顺序工作流的执行并非完全是顺序的。它们仍然可以接收外部事件或者启动并行任务,在这种情况下,确切的执行顺序可能有所不同。
—状态机工作流 由一组状态、转换和操作组成。首先,将一个状态表示为起始状态,然后,基于事件执行向另一个状态的转换。状态机工作流可以具有确定工作流结束的最终状态。
—可以简单的将状态机模式看成是有交互点与Goto指令的流模式。设计工作流,其实没有什么应该的模式,用状态机模式作主流程,管理业务状态,流模式作子流程,完成具体的业务操作是一个不错的方案。
5.工作流活动的分类
—WF为工作流设计提供了一系列的Activity控件,从型式上分为两类:
简单活动类:(System.Workflow.ComponentModel.Activity)
活动容器类:(System.Workflow.ComponentModel.CompositeActivity)
Activity <- CompositeActivity
—包括以下具体控件:
CodeActivity:可以添加代码,
IfElseActivity:类似于if语句,
SuspendActivity:类似于线程挂起语句,
DelayActivity:类似于线程休眠语句 ,
CallExternalMethodActivity:类似于调用方法语句,
HandleExternalEventActivity:类似于触发事件语句,
WhileActivity:类似于While语句,
terminateActivity: 类似于终止语句,
ThrowActivity:类似于自定义异常语句,
ConditionedActivityGroup:有点像foreach语句,
replicatorActivity:有点像for语句
PolicyActivity:有点像职责链
— 可以用InvokeWebServiceActivity调用WebService
—可以用WebServiceInputActivity ,WebServiceOutputActivity 将工作流发布成WebService
—可以用InvokeWorkflowActivity调用子流程
还有一些用于控制流程、控制状态、事务处理、异常处理的控件
6.工作流模版和实例
—WF的工作流模板可以是一个编译成DLL的NET类库文件,也可以是一个用XML描绘结点关系的字符串,工作流实例是每个用户依据模板定义的轨道实现具体业务的一组状态数据,实例要在引擎中运行,实例可以保存到数据库或磁盘文件中。
—工作流提供了一个运行实例的引擎,引擎不是以一个独立服务的方式发布的,而是可以独立线程的方式寄生在任意进程中。被寄生的程序叫宿主,宿主与引擎可以通过接口进行交互,控制引擎以及与引擎中的工作流实例通信。引擎中可以同时运行多个实例,宿主中可以同时运行多个引擎。
7. WF提供了的服务
—1.持久化服务
WF提供了SqlWorkflowPersistenceService,用于将实例持久化到一数据库中,SqlWorkflowPersistenceService的数据库生成文件在[系统盘\Windows\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\EN]下SqlPersistenceService_Schema.sql与SqlPersistenceService_Logic.sql
—也可派生PersistenceService,建立自定义持久化服务
—2.监听(跟踪)服务
WF提供了SqlTrackingService,用于监听实例的运行状态,并将状态保存到数据中,SqlTrackingService的数据库生成文件在[系统盘\Windows\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\EN]下Tracking_Schema.sql与Tracking_Logic.sql 使用SqlTrackingQuery,可以从数据库中查询实例的状态。
—也可派生TrackingService,建立自定义监听服务对要监听的内容,可以自定义 TrackingProfile,进行筛选
—3.数据通信服务
WF提供了ExternalDataExchangeService,用于实现宿主与实例的通信,CallExternalMethodActivity与HandleExternalEventActivity依赖于该服务。
—4.还有其他一些服务,如ManualWorkflowSchedulerService,以单程方式运行实例,在实例有与外部设备交互时非常有用.
8. WF高级特性
—1.可以自定义流程设计器
WF提供了System.Workflow.ComponentModel.Design.WorkflowView控件,该控件可以用图形的方式显示工作流结构。
—2.可以用WorkflowChanges在实例运行时使用,动态添加删除结点。
—3.可以用WorkflowMarkupSerializer将工作流模板类转换为XML字串格式
—4.WF为流程控制类Activity提供了规则对象。
—5.WF提供了WorkflowRole对象,可以自定义角色,使用AD角色、使用ASP.NET2.0角色。
9.关于通信
—很多人有一个习惯就是从引擎中得到实例,然后直接操作,这个方式是单线程开发中常用的,不用考虑资源访问冲突,但在WF中实例与宿主、引擎的关系,就像操作多线程资源一样,实例被引擎线程专控,宿主要与实例通信不能直接进行,要通过一个专用通道。
— 1.可以使用启动参数,动态结点,插入队列的方式实现宿主与引擎中的实例推方式通信;
— 2.也可以在设计时使用CallExternalMethodActivity,HandleExternalEventActivity,或自定义状态点的方式实现宿主与引擎中的实例拉模式的通信。
10.参考(非常值得一看)
—入门简介:http://www.microsoft.com/china/MSDN/library/Windev/WindowsVista/WWFGetStart.mspx?mfr=true
—性能描述:http://msdn2.microsoft.com/en-us/netframework/aa973808.aspx
—业务方面:http://www.microsoft.com/china/MSDN/library/Windev/WindowsVista/WWFGetStart.mspx?mfr=true
阅读全文(3803) | 评论:0 | 复制链接
WF持久化机制剖析(2007-11-23 9:26:00)
1 为什么要持久化状态?
1.1 内存是有限的,将等待或者休眠的流程暂时从内存中卸载有利于提供性能
1.2 运行时可能会出现不稳定因素导致流程崩溃,持久化可以提供流程恢复的可能
1.3 流程中的事务或者补偿机制需要隔离的流程状态来辅助完成
2 什么时候会发生流程被持久化?
2.1 在流程中加入的活动被标有PersistOnClose属性,这是人为设定的强制持久化策略, 即流程执行到该步骤必须要保存一下,不表示从内从中卸载。
2.2 活动的运行时状态被设置为Executing时(即运行中)(如果DelayActivity),下一个活动必须在将来的某个时候到达, 这时引擎会决定使流程进行持久化。
2.3 当在流程实例之外手动调用卸载流程的方法时会导致持久化
2.4 当具有补偿特性的活动(如CompensatableSequenceActivity)被创建多于一个执行上下文时会发生持久化(请原谅这话说的有点专业,因为解释它需要费长篇大论)
3 什么时候会发生流程被反持久化?
流程被保存后是不具有自我激活的功能的,必须通过外部方法去激活它。
3.1 当调用引擎的获取一个工作流实例的方法时, 如果实例不在内存中,引擎会自动向已注册的持久化服务发出装载其状态的指令
4 什么是流程阻塞态?
4.1 在流程执行过程中,当遇到例如Delay这样的活动时,流程被指示进入休眠状态(钝化),即被挂起, 此时流程根活动被标记为阻塞态,引擎发出持久化指令。
4.2 什么时候出现非阻塞态持久化化行为?
5 什么是流程锁定态?
5.1 当流程执行从事务范围(TransacationScope)退出或结束了一个具有持久点标记的活动时, 引擎自动持久化该流程状态,此时流程被标记为锁定态,即独占的。
5.2 为什么需要这个状态?
6 如何唤醒流程?
6.1 流程被持久化以后就变成了数据,而不具有任何行为,所以它不具有自我唤醒的功能
6.2 流程可以通过定时器轮询唤醒
6.3 当要获取指定的流程实例而该流程不在内存中时,流程会引擎唤醒
6.4 可以通过从流程的根活动获取下一个执行周期的时间点,并定时唤醒该流程
7 流程与消息队列
7.1 流程是怎么被驱动的?
7.2 执行计划中的计划项是依靠什么实现的?
7.3 为什么消息队列服务只能从活动的执行上下文(AEC)中获得?
阅读全文(2160) | 评论:0 | 复制链接
用javascript实现html页面之间的参数传递的四种方法 (2007-6-26 15:10:00)
我们知道,在服务器端asp,jsp等程序可以接受html页面上的form传来的参数。那么,可不可以传递参数给html页面呢。可以。
原理:通过window.location.href中的分割符获得各个参数
方法一:
/*
*函数功能:从href获得参数
*sHref: http://www.cscenter.com.cn/arg.htm?arg1=d&arg2=re
*sArgName:arg1, arg2
*return: the value of arg. d, re
*/
function GetArgsFromHref(sHref, sArgName)
{
var args = sHref.split("?");
var retval = "";
if(args[0] == sHref) /*参数为空*/
{
return retval; /*无需做任何处理*/
}
var str = args[1];
args = str.split("&");
for(var i = 0; i < args.length; i ++)
{
str = args[i];
var arg = str.split("=");
if(arg.length <= 1) continue;
if(arg[0] == sArgName) retval = arg[1];
}
return retval;
}
{
var str=window.location.search;
if (str.indexOf(name)!=-1)
{
var pos_start=str.indexOf(name)+name.length+1;
var pos_end=str.indexOf("&",pos_start);
if (pos_end==-1)
{
return str.substring(pos_start);
}
else
{
return str.substring(pos_start,pos_end)
}
}
else
{
return "没有这个name值";
}
}
alert(getvalue(name));
QueryString : function(item){
var svalue = location.search.match(new RegExp("[\?\&]" + item + "=([^\&]*)(\&?)","i"));
return svalue ? svalue[1] : svalue;
}
}
alert(Request.QueryString("id"));
var Request = new Object();
if(url.indexOf("?")!=-1)
{
var str = url.substr(1); //去掉?号
strs = str.toLowerCase();
strs = strs.split("&");
for(var i=0;i<strs.length;i++)
{
Request[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
}
}
var mapWidth = Request["w"];
var mapHeight = Request["h"];
阅读全文(2379) | 评论:1 | 复制链接
C#中对Excel的操作 (2007-6-18 19:13:00)
Excel表格文档由于其简单易用,普遍应用于不少单位或部门,因而在编写应用程序过程中,有时会需要对Excel文档进行操作,最简单的情况通常有两种:(1)需要获取文档中一些单元格的值;(2)将文档导入至数据库。
1).在操作Excel文档之前,需要添加对Excel对象库的引用:
引用—COM—Microsoft Excel 11.0 Object Library(版本号可能不同)
2).以下代码示意打开一个已有的Excel文档的第一个sheet页,获取单元格“B
private void Operate(string pFileName)
{
Excel.Application app = new Excel.Application();//打开一个Excel应用
if (app == null)
{
return;
}
Workbooks wbs = app.Workbooks;
_Workbook wb = wbs.Add(pFileName);//打开一个现有的工作薄
Sheets shs = wb.Sheets;
_Worksheet sh = (_Worksheet)shs.get_Item(1);//选择第一个Sheet页
if (sh == null)
{
return;
}
Range r = sh.get_Range("B1", Missing.Value);
if (System.Convert.ToString(r.Value2).Trim().Equals("my"))
{
//Do Something.
}
}
3).
不少人在打开Excel应用后,对如何在使用完成后释放它大感头痛,在网上我找到一种方法,经过实验证明是OK的:
private void ReleaseCOM(object pObj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(pObj);
}
catch
{
throw new Exception("释放资源时发生错误!");
}
finally
{
pObj = null;
}
}
有了该方法,则只要在步骤2)代码之后添加以下代码,就可以完成对资源的释放:
private void Operate(string pFileName)
{
……
//释放资源
ReleaseCOM(sh);
ReleaseCOM(shs);
ReleaseCOM(wb);
ReleaseCOM(wbs);
app.Quit();
ReleaseCOM(app);
}
4).
string count = sh.UsedRange.Rows.Count.ToString();
Range r = sh.get_Range("B100",Missing.Value);
r.Formula = "=SUM(IF(FREQUENCY(MATCH(B1:B" + count + ",B1:B" + count + ",0),MATCH(B1:B" + count + ",B1:B" + count + ",0))>0,1))";
Console.WriteLine(System.Convert.ToString(r.Value2));
5).
private DataTable LoadExcel(string pPath)
{
string connString = "Driver={Driver do Microsoft Excel(*.xls)};DriverId=790;SafeTransactions=0;ReadOnly=1;MaxScanRows=16;Threads=3;MaxBufferSize=2048;UserCommitSync=Yes;FIL=excel 8.0;PageTimeout=5;";
connString += "DBQ=" + pPath;
OdbcConnection conn = new OdbcConnection(connString);
OdbcCommand cmd = new OdbcCommand();
cmd.Connection = conn;
//获取Excel中第一个Sheet名称,作为查询时的表名
string sheetName = this.GetExcelSheetName(pPath);
string sql = "select * from [" + sheetName.Replace('.', '#') + "$]";
cmd.CommandText = sql;
OdbcDataAdapter da = new OdbcDataAdapter(cmd);
DataSet ds = new DataSet();
try
{
da.Fill(ds);
return ds.Tables[0];
}
catch (Exception x)
{
ds = null;
throw new Exception("从Excel文件中获取数据时发生错误!");
}
finally
{
cmd.Dispose();
cmd = null;
da.Dispose();
da = null;
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
conn = null;
}
}
private string GetExcelSheetName(string pPath)
{
//打开一个Excel应用
Excel.Application app = new Excel.Application();
if (app == null)
{
throw new Exception("打开Excel应用时发生错误!");
}
Excel.Workbooks wbs = app.Workbooks;
//打开一个现有的工作薄
Excel._Workbook wb = wbs.Add(pPath);
Excel.Sheets shs = wb.Sheets;
//选择第一个Sheet页
Excel._Worksheet sh = (Excel._Worksheet)shs.get_Item(1);
string sheetName = sh.Name;
ReleaseCOM(sh);
ReleaseCOM(shs);
ReleaseCOM(wb);
ReleaseCOM(wbs);
app.Quit();
ReleaseCOM(app);
return sheetName;
}
(“string sql = "select * from [" + sheetName.Replace('.', '#') + "$]";”如果sheet页名称中含有“.”,则要将其替换为“#”,具体原因,没研究出来-_-。另外贴一张界面截图:)

6).关于从Excel导入数据,网上应该有很多例子,这里补充一个关于数据导入时的效率问题。最初在从Excel导入数据到MySql中时,当文件达到50000行时,逐行导入花了10余分钟的时间。大量执行插入操作,逐条执行很慢原因可能在于:
(1)每执行一条插入语句,客户端需要与服务器交互一次,这要有代价;
(2)一些数据库对每一条插入操作都执行事务,这也要有代价
所以在大量执行插入操作时,应该尽量先多个insert语句拼接好,例如每次拼接好200个insert语句,然后再一次执行它,这样可以大幅提高效率。
阅读全文(3372) | 评论:0 | 复制链接
.Net 机制下委托(一) 事件委托(2007-6-15 10:22:00)
一、 认识委托
一个简单的例子:
张三看到餐桌上有一个桔子,由于自己怕动(主要是出于自己要玩游戏,走不开),立刻就对着他妈妈喊:“我要吃桔子,妈妈帮我拿过来。”,接着,他妈妈听到乖儿子要吃桔子,就立刻送去给儿子了。
从某种意义上来说,把儿子发出消息要桔子的动作与妈妈送桔子给儿子的动作相关联的过程就称为委托,也就是说儿子发出消息要桔子这个事件委派妈妈根据他的消息内容去完成他想要做的事。
二、 理解.net机制下的委托概念
关于委托的声明:
delegate 返回类型 方法声明( 参数1,参数2,参数3… );
例如:delegate void ButtonTextChangeEvent( object sender, EventArgs e );
注意:声明一个ButtonTextChangeEvent,就是创建了一个“委托类”,在编译期间,就会产生个委托类在代码区,因而它可以在类里面声明,也可以跟类在同一域(即命名空间中定义),享受与类同等待遇,那么在这个类里面会有一个最关键的函数就是
virtual 返回类型 方法声明(参数1,参数2,参数3…);
图示:
那么这个Ivoke虚函数是用做干什么的呢?它主要是用来调用某个绑定了的函数,说白了,delegate充其量就是一个中间件(相当于我们所见的媒婆)。
namespace ConsoleApplicationTest
{
//声明一个委托(如果您学过C++那么它很像C++里面的typedef.)
public delegate int FunPtr();
public class A
{
//定义一个委托类型的变量(类型:引用)
public FunPtr ptr;
}
public class Controller
{
public void AdelegateTest()
{
A a = new A();
a.Ptr = new FunPtr(Fun); //对a实例中的ptr进行委托绑定
}
public int Fun(){}
}
}
声明的委托它是类类型(请记住),定义一个委托类型的变量,主要是为了指向某个静态函数的首地址或是实例的中某个函数首地址。,
委托是面向对象的、类型安全的和保险的,它是对对象的引用以及对该对象内一个或多个方法的引用组成(即多路广播),那么何为多路广播呢?多路广播就是它可以把委托叠加到一起,形成一个委托链表的形式,进行依次调用。
形式:对象.委托变量 += new 委托类( 委托类中invoke的函数声明形式的函数式 )
举例:button. ptr += new FunPtr (Fun);
关键是“ += ” 形成了一个委托链 ,也可以把它理解成是向委托数据表中添加函数地址,调用时按委托数据表中的顺序依次转至函数式地址进行执行(顺序:先进先出),当然您也可以用“ -= ”来对委托数据表中的函数地址进行移除操作。
**.委托在 .net下面,虽然类似于C++下面的函数指针,但它比C++更安全,更有效,更容易使用。
三、事件委托
好,现在我们对委托有一定的了解了,现在开始事件委托的历程。
简单的说,事件委托就是在对象内进行事件声明,而在该对象之外用事件处理函数与对象声明的事件进行绑定,然后,该对象事件的引发,继而执行相应的事件处理函数的一个过程,例如:Button的OnClick事件。
好,既然说到Button的事件,那我们就一起来模仿一个Button类及Button值改变后引发事件并执行自定义的ButtonTextChangeEvent事件处理函数的过程,从而以事件数据得到该Button赋值过多少次。
我们先来看看这个过程是如何传递的:
传递过程:
Button –> 赋值 –> 引发ButtonTextChangeEvent –> 调用相应的处理程序
好,现在这流程已经有了,那么,我们一步步来做:
namespace ConsoleApplicationTest
{
public class Button
{
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
}
}
到目前为止,一个基本的Button类设计完成(这个就不解释了,谁都知道是啥意思。),它已经具有了赋值的功能,接下来,我们来看第三步,既然要引发事件,那么我们必须得有事件的声明,让事件处理委派于外部处理函数(事件绑定),当对Button.Text赋值时进行引发事件.
namespace ConsoleApplicationTest
{
public delegate void ButtonTextChangeEventHander( object sender , System.EventArgs e ); //事件委托的声明。
public class Button
{
public event ButtonTextChangeEventHander TxtChange; //定义一个事件委托的引用,以备外部进行事件的绑定.
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
}
}
好,现在事件的声明与Button的事件的引用也定义了,为了达到Button.Text赋值时引发事件,进行那么现在就到需要在set{…}里面进行事件调用的代码编写:
namespace ConsoleApplicationTest
{
public delegate void ButtonTextChangeEventHander ( object sender , System.EventArgs e ); //事件委托的声明。
public class Button
{
public event ButtonTextChangeEventHander TxtChange; //定义一个事件委托的引用,以备外部进行事件的绑定.
private string _Text;
public string Text
{
get { return _Text; }
set
{
_Text = value;
System.EventArgs e = new EventArgs(); //这里是事件的基类实例.
ChangeTxt(e); //调用事件处理函数.
}
}
private void ChangeTxt(System.EventArgs e)
{
if( TxtChange != null )
TxtChange(this,e);//真正调用外部指派的处理函数.
}
}
}
到此为止,可能有人会问,为什么要这样定义:
delegate ButtonTextChangeEventHander ( object sender , System.EventArgs e )
如果不用,那不也一样可以吗?
原因也很简单,为了符合.net framework CLS的约定,所有的事件都得遵守 M S 的规定,要不然,嘿嘿,你就麻烦了,事件得不到统一的、规范的处理,所以既然用了MS.Net就遵守她的标准吧,当然在上面的那段程序中不用这样麻烦的定义也是可行的。
例如: delegate ButtonTextChangeEventHander (); 或者
delegate ButtonTextChangeEventHander ( int changeCount ); //加上事件数据
ButtonTextChangeEventHander( object sender , System.EventArgs e );//它应该是你经常看到的吧,这种形式就是MS的一种规定,事件处理函数的一种规定,如果您喜欢,可以再加上N个参数在里面也是可以的,比如:异步回调,但这样做唯的一缺点就是不能与MS的事件相统一符合CLS的规定。
Sender :引发事件的对象 ; e:事件所带的数据
需要指出的是:
if( TxtChange != null )
TxtChange(this,e);
如果不加 if( TxtChange != null ) 如果TxtChange并没有绑定事件处理函数,那么它将会发“未将对象引用到实例”的Exception。
到目前为止,一个无事件数据的Button设计成型了,那么,现在我们得加上一些绑定代码,进行事件的绑定,得让Button执行赋值时,调用相应的处理函数。
using System;
using System.Threading;
namespace ConsoleApplicationTest
{
public delegate void ButtonTextChangeEventHander( object sender , System.EventArgs e ); //事件委托的声明。
public class Button
{
public event ButtonTextChangeEventHander TxtChange; //定义一个事件委托的引用,以备外部进行事件的绑定.
private string _Text;
public string Text
{
get { return _Text; }
set
{
_Text = value;
System.EventArgs e = new EventArgs(); //这里是事件的基类实例.
ChangeTxt(e); //调用事件处理函数.
}
}
private void ChangeTxt(System.EventArgs e)
{
if( TxtChange != null )
TxtChange(this,e);//真正调用外部指派的处理函数.
}
}
/**//**//**//// <summary>
/// 事件处理类
/// </summary>
public class DelegateDemo
{
//定义一个实例
private Button button;
public DelegateDemo()
{
InitializeComponent();
//构造函数调用时,就开始操作button进行赋值。
SetValues();
}
public void InitializeComponent()
{
button = new Button();
//对button对象的TxtChange事件引用,进行事件与处理函数的绑定。
//前面已经说过了,EventHandler是一个类,所以要用new进行创建它的实例,其主要是把button_TxtChange事件处理函数的地址传至这个委托实例。
button.TxtChange += new ButtonTextChangeEventHander(button_TxtChange);
}
public void SetValues()
{
string[] values = {"AAA","BBB","CCC","DDD","EEE"};
for( int i = 0; i < 5; i++ )
{
//主要是让程序有个阻塞输出,可以停留一会儿,让你一次看个够J,对理解委托没什么意义。
Thread.Sleep(2000);
//进行赋值,从而引发事件而直接调用button_TxtChange事件处理函数。
button.Text = values[i];
}
}
private void button_TxtChange(object sender, System.EventArgs e)
{
Console.WriteLine( " Button实例新值。其值为:" + ((Button)sender).Text );
}
}
public class MyMain
{
public static void Main()
{
DelegateDemo delegateDemo = new DelegateDemo();
Console.Read();
}
}
}
其执行结果:
Button实例新值。其值为:AAA
Button实例新值。其值为:BBB
Button实例新值。其值为:CCC
Button实例新值。其值为:DDD
Button实例新值。其值为:EEE
先总结一下,上面说的都是对于无事件数据的情况,但如果你需要有事件数据了,怎么办?比如:我们大家都比较熟悉的DataGrid它有DataGridCommandEventArgs事件,并在事件中带有CommandArgument,Item,CommandName等事件数据,使得我们可以操作更方便。那么我们这个Button模拟对象中也要有类似的功能怎么办?那就必须实现自己的EventArgs事件类,下面我们一起来实现我们这个Button的EventArgs类。
先休息一下,偶到现在还没睡呢,一下就到早上5:30了,幸好是星期六,不上班J.
喝了口水,再来,继续。。。
在上面的例子,我们要加一个事件数据,用于将Button的赋值次数做为事件数据传给事件处理函数,因此我们自已定义一个EventArgs类来实现。
/**//// <summary>
///事件数据对象
/// </summary>
public class ButtonTextChangeEvent : System.EventArgs //继承系统的事件基类
{
private readonly int _Num;
public ButtonTextChangeEvent( int num )
{
_Num = num;
}
public int Num
{
get{ return _Num; } //赋值次数(主要是让事件处理函数进行调用)
}
}
上面的这个类,就是一个包含事件数据的类,继承了系统的EventArgs,添加了一个Num属性,即可从Button中传递赋值次数至事件数据类中,从而让事件处理类进行调用。
当然,委托也得稍改动一下参数的声明:
public delegate void ButtonTextChangeEventHander( object sender , ButtonTextChangeEvent e );
然后改动相应的委托处理函数的声明等。具体请看完整的实现:
using System;
using System.Threading;
namespace ConsoleApplicationTest
{
/**//// <summary>
/// 包含事件数据的类
/// </summary>
public class ButtonTextChangeEvent : System.EventArgs
{
private readonly int _Num;
public ButtonTextChangeEvent( int num )
{
_Num = num;
}
public int Num
{
get{ return _Num; }
}
}
public delegate void ButtonTextChangeEventHander( object sender , ButtonTextChangeEvent e );
/**//// <summary>
/// 对象模型
/// </summary>
public class Button
{
public event ButtonTextChangeEventHander TxtChange;
private int _setValueCount;
private string _Text;
private void ChangeTxt(ButtonTextChangeEvent e)
{
if( TxtChange != null )
TxtChange(this,e);
}
public string Text
{
get
{
return _Text;
}
set
{
_Text = value;
_setValueCount += 1;
ButtonTextChangeEvent e = new ButtonTextChangeEvent(_setValueCount);
ChangeTxt(e);
}
}
}
/**//// <summary>
/// 实例类
/// </summary>
public class DelegateDemo
{
private Button button;
public DelegateDemo()
{
InitializeComponent();
SetValues();
}
public void InitializeComponent()
{
button = new Button();
button.TxtChange += new ButtonTextChangeEventHander(button_TxtChange);//是不是感觉很像我们在实际中使用的那样,嘿嘿。
}
public void SetValues()
{
string[] values = {"AAA","BBB","CCC","DDD","EEE"};
for( int i = 0; i < 5; i++ )
{
Thread.Sleep(2000);
button.Text = values[i];
}
}
private void button_TxtChange(object sender, ButtonTextChangeEvent e)//这里也很像
{
Console.WriteLine("第"+e.Num.ToString()+"次重新设定Button实例新值。其值为:" + ((Button)sender).Text );
}
}
public class MyMain
{
public static void Main()
{
DelegateDemo delegateDemo = new DelegateDemo();
Console.Read();
}
}
}
好,现在我来把上面的这个例子主要流程说一下:
1. 对象模型,主要是为了完了一个Button的设计,并加入事件引用的定义,以备外部其它类对象进行事件的绑定。
2. ButtonTextChangeEvent 它就是一个包含事件数据的一个类,主要是为了可以让外部事件处理函数进行调用。
3. 实例类,它里面的InitializeComponent()函数就实现了Button中TxtChange事件委派到具体的哪一个处理函数,这个函数,可以把它看成一个媒婆,专做三八事儿的J.
4. 然后,直接在构函数中,执行SetValues()函数,对button进行赋值,从而引发ButtonTextChangeEvent。
阅读全文(2069) | 评论:0 | 复制链接
用C#编写发手机中文短信息Windows服务(2007-6-11 11:48:00)
最近在电脑城上买了一根NOKIA3210的数据线,玩了几天改LOGO、改铃声后也将数据线扔在一边。直到前几天在Http://oxygensoftware.com上看到有发手机短信息的二次开发控件,才想起多日不用的数据线,而且最近在学C#,觉得用C#做个发短信息的程序也不错,经过多天的测试,终于实现用电脑+数据线+手机的模式,实现在单位的局域网平台上发送短信息了。
由于在单位使用到发手机短信息的地方有很多,可能是从网页、可能是OUTLOOK中的窗体、也可能是某台非Windows操作系统的主机的某个系统,所以经过思考探讨,觉得最好的解决方案是采用Windows的“服务”,定时从一个目录中固定格式的文本文件中读取出相应的信息,发送出去。而其它客户端只需往该目录写入文本信息即可。思路定下来后就让我们开始吧!
先交待一下开发平台:Windows 2000 Advance Server操作系统、Visual Studio .Net 、Oxygen Sms ActiveX Control V2.3 (Share Ware)、 Nokia 3210手机通过数据线接在COM1上。运行Visual Studio .Net,新建一个C#的项目,选择“Windows Server”类型的项目,命名为“SmsServer”。在Server1的设计画面,将“ServerName”命名为“SmsServer”。点击“视图设计器按钮”切换到设计画面,在“Windows Forms”工具箱中拖一时钟控件,命名为“SmsTimer”,在“Components”工具箱中拖一“EventLog”控件。命名为“eventLog1”。在“项目”菜单中点击“添加引用”,选择“COM”页,浏览到安装Oxygen Sms ActiveX Control V2.3程序的目录,找到SMSControl.ocx添加到“选定的组件”中。
将Server1.cs代码替换为
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.IO;
using System.Text ;
namespace SmsServer
{
public class SmsServer : System.ServiceProcess.ServiceBase
{
private System.Timers.Timer SmsTimer;
private System.Diagnostics.EventLog eventLog1;
public O2SMSXControl.O2SMSX SmsX1;//定义手机短信对象
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public SmsServer()
{
// This call is required by the Windows.Forms Component Designer.
InitializeComponent();
// TODO: Add any initialization after the InitComponent call
}
// The main entry point for the process
static void Main()
{
System.ServiceProcess.ServiceBase[] ServicesToRun;
// More than one user Service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = New System.ServiceProcess.ServiceBase[] {new Service1(), new MySecondUserService()};
//
ServicesToRun = new System.ServiceProcess.ServiceBase[] { new SmsServer() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.SmsTimer = new System.Timers.Timer();
this.eventLog1 = new System.Diagnostics.EventLog();
((System.ComponentModel.ISupportInitialize)(this.SmsTimer)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();
//
// SmsTimer
//
this.SmsTimer.Enabled = true;
this.SmsTimer.Elapsed += new System.Timers.ElapsedEventHandler(this.SmsTimer_Elapsed);
//
// SmsServer
//
this.ServiceName = "SmsServer";
((System.ComponentModel.ISupportInitialize)(this.SmsTimer)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
/// <summary>
/// Set things in motion so your service can do its work.
/// </summary>
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
//开始服务时初始化手机.
SmsX1 = new O2SMSXControl.O2SMSXClass ();
SmsX1.ConnectionMode = 0; //联线类型cable
SmsX1.ComNumber = 1; //联接端口为com 1
SmsX1.Model = 0; //手机类型3210
SmsX1.Open (); //联接手机
SmsX1.SetSMSCNumber ("+8613800754500");//信息中心号码
}
/// <summary>
/// Stop this service.
/// </summary>
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
SmsX1.Close ();
}
private void SmsTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
//当f:\sms\data\filetosend有文件时,先关闭时钟,将其发送出去,并删除掉文件再启动时钟
this.SmsTimer.Enabled =false;
//目录对象
DirectoryInfo cd = new System.IO.DirectoryInfo("F:\\Sms\\Data\\FileToSend");
//数据库记录变量
string rsId;
string rsPhoneNum;
string rsSmsText;
string StrSql;
//首先,在当前目录中列举当前的所有SMS文件
foreach(FileInfo FileSend in cd.GetFiles ())
{
try
{
//依次打开每个文件读取文件内容
FileStream fs = new FileStream (cd.FullName + "\\" + FileSend.Name ,FileMode.Open,FileAccess.Read );
StreamReader sr;
sr = new StreamReader(fs,UnicodeEncoding.GetEncoding ("GB2312"));
rsId = FileSend.Name .ToString ();
rsId = rsId.Replace (".sms","");
rsId = rsId.Trim ();
rsPhoneNum = sr.ReadLine ();
rsPhoneNum = rsPhoneNum.Trim ();
if (rsPhoneNum.Length >11)
rsPhoneNum = rsPhoneNum.Substring (0,10);
rsSmsText = sr.ReadToEnd();
rsSmsText = rsSmsText.Trim ();
if (rsSmsText.Length >50)
rsSmsText.Substring (0,49);
fs.Close ();
sr.Close ();
//发送短信息
SmsX1.SendUnicodeSMSMessage (rsPhoneNum.ToString (),rsSmsText.ToString (),6,false,"");
//备份并删除文件
FileSend.CopyTo ("F:\\Sms\\Data\\HadBeenSend\\" + FileSend.Name ,true);
FileSend.Delete ();
}
catch(System.Exception E)
{
//出错写LOG文件
eventLog1.WriteEntry (E.Message.ToString ());
}
}
//重新启动时钟
this.SmsTimer.Enabled =true;
}
}
}
在 Server1.cs切换设计画面,在属性窗口下点击“Add Installer”,系统自动增加ProjectInstaller.cs文件,点击serviceInstaller1,设置“Server Name”设置为“SmsServer”,点击“serviceProcessInstaller1”,设置Account为“LocalSystem”。
选择菜单“生成”中的“生成SmsServer”,改正可能有的错误。进行DOS命令行,进行项目目录的\bin\debug目录下,执行“installutil SmsServer”,如果找不到installutil程序,就先Path一下。这时,在管理工具的“服务”下可以找到“SmsServer”服务了。启动该服务。这里默认源为目录F:\Sms\Data\FileToSend,如果这个目录有.SMS文件,就读取其第一行为发送的手机号码,第二行到文本结束为短信息内容,然后发送短信息,再将文本备份到F:\Sms\Data\HadBeenSend\。
让我们再回头看一下Server1.cs中的代码。首先在命令空间要增加“using System.IO; using System.Text ; ”方便处理文件及文本对象,在命名类时
public class SmsServer : System.ServiceProcess.ServiceBase
{
private System.Timers.Timer SmsTimer;
private System.Diagnostics.EventLog eventLog1;
public O2SMSXControl.O2SMSX SmsX1;//定义手机短信对象
......
引用Oxygen控件中的定义SmsX1对象,然后在启动服务时初始化手机对象
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
//开始服务时初始化手机.
SmsX1 = new O2SMSXControl.O2SMSXClass ();
SmsX1.ConnectionMode = 0; //联线类型cable
SmsX1.ComNumber = 1; //联接端口为com 1
SmsX1.Model = 0; //手机类型3210
SmsX1.Open (); //联接手机
SmsX1.SetSMSCNumber ("+8613800754500");//信息中心号码
}
其中要注意的是要初始化信息中心号码,如果不初始化,经常有发不去的情况。然后当时钟触发时要注意先将时钟关掉,再列举当前目录中的.SMS文件,逐一发送出去,再将时钟打开,同时在读文件时,要注意文件的编码 “sr=new StreamReader(fs,UnicodeEncoding.GetEncoding ("GB2312"));”采用GB2312编码读取才不会读出乱码出来,最后发送信息即可,“SmsX1.SendUnicodeSMSMessage (rsPhoneNum.ToString (),rsSmsText.ToString (),6,false,""); ”其中各个参数的含义可以参照Oxygen的帮助。最后在服务停止时释放短信息对象“SmsX1.Close ();” 如果出错,则写出错服务LOG文件“eventLog1.WriteEntry (E.Message.ToString ());”这样,在Windows的“事件查看器”就可以看到出错的信息了。
但是这里有个小小的遗憾,通过OCX控件发出的短信息前面有一串该网站的英文,但是注册版不会有这串字,注册“只需”¥399就可以了。
阅读全文(2093) | 评论:1 | 复制链接
C#编写IP_MAC防欺骗程序(2007-6-11 11:43:00)
单位的学生成绩管理系统是ASP.NET做的,很担心非法人员修改。为了方便管理,单位的每台机子IP是固定的。由于只是允许办公计算机访问成绩修改的页面,计算机数量并不多,所以我把所有办公计算机的IP-MAC专门做一张数据库表,且一一对应。操作之前判断来访的IP-MAC地址,且IP-MAC成对限制。两个地址必须同时正确才可以访问,若不一致则拒绝访问,并且记录该信息,方便我们找出非法操作者。这是我们最初方案,但是程序做好以后,却发现了新的问题。当有人要做非法操作,会出现以下情况:只修改IP、只修改MAC、同时修改IP-MAC。对于前两种情况,我们的服务器端已全可以判断、限制了。但是对于情况三,非法操作者成对修改IP-MAC,而且该IP-MAC对是其他被授权的主机的正确IP-MAC对,在服务器端我们已经无力限制。于是便有了方案二:做一个判断MAC地址是否被修改的Windows服务,这个服务安装在每个客户机上,如果MAC地址被修改,关闭客户端的IE,从而限制访问,
方案一
服务器端实现,实现的关键代码如下:
using System.Runtime.InteropServices ;//请注意引用动态链接库,则需要此命名空间,它提供各种各样支持 COM interop 及平台调用服务的成员。其中最重要的属性有 DllImportAttribute(可以用来定义用于访问非托管 API 的平台调用方法)
[DllImport("iphlpapi.dll", ExactSpelling=true)] //在.net中使用动态链接库
public static int SendARP( int DestIP, int SrcIP, [Out] byte[] pMacAddr, ref int PhyAddrLen );
[DllImport("Ws2_32.dll")]
private static extern Int32 inet_addr(string ip);
private void Button1_Click(object sender, System.EventArgs e)
{
string client_ip=Request.UserHostAddress ;//获得客户端IP
Label_ip.Text =client_ip;
Int32 ldest= inet_addr(client_ip);//将客户端IP转换成目的地的ip
Int32 lhost= inet_addr("192.168.1.80");//本地的ip,也可以自动生成
try
{
byte[] macinfo=new byte[6]; //做一个数组,准备存储MAC地址
int len=macinfo.Length; //得到数组长度
int ii=SendARP(ldest,lhost, macinfo,ref len);//①macinfo就是返回的MAC地址指针
Label_mac.Text ="Mac Add:"+BitConverter.ToString(macinfo,0,len);//将获得的MAC地址转换成易读 的形式
}
catch(Exception err)
{
Label_ip.Text=err.ToString ();//获取系统给出的错误信息
}
}
SendARPSendARP的函数原型如下:
DWORD SendARP(
IPAddr DestIP, // 目标IP地址
IPAddr SrcIP, // 源IP地址
PULONG pMacAddr, // 返回MAC地址指针
PULONG PhyAddrLen // 返回MAC地址长度
);
当有人进行非法操作,会出现如下情况:
1. 只修改IP
2. 只修改MAC
3. 同时修改IP和MAC
情况1,非法操作者做如下修改:选择“本地连接”的属性,进入Internet协议,修改自己IP。
情况2,非法操作者做如下修改:选择“本地连接”的属性。选择“配置”,修改网卡属性。
在“高级”选项卡选中“Network Address”,默认“值”是“不存在”,非法操作者在“值”中随便填入一个MAC值,达到修改MAC的目的。MAC地址存储在网卡的EEPROM中并且唯一确定,但网卡驱动在发送Ethernet报文时,并不从EEPROM中读取MAC地址,而是在内存中来建立一块缓存区,Ethernet报文从中读取源MAC地址。而且,用户可以通过上述方式或者三方软件修改实际发送的Ethernet报文中的源MAC地址。
方案二
它作为一个服务安装在每个客户机上,把服务设置为随Windows启动而启动,禁止“暂停”和“停止”该服务。
加入一个Timer控件(timer_watchmac),用于确定监控MAC地址值的时间间隔。注意,该控件一定是“工具箱/组件”中的Timer,C#中有3个Timer控件, System.Timers.Timer(基于服务器的计时器);System.Windows.Forms.Timer(基于 Windows 的标准计时器,位于“工具箱”的“Windows 窗体”选项卡);;System.Threading.Timer(仅可在编程时使用的线程计时器)。它们是不一样的。
服务监控的关键代码如下:
using System.Management ;//要操作WMI需要此命名空间
public string mymac1;//记录原始MAC
public string mymac2;//服务监控当前MAC
初始化服务的时候,就读取本机MAC地址:
protected override void OnStart(string[] args)
{ // TODO: 在此处添加代码以启动服务。
timer_watchmac.Enabled =true;
ManagementClass mcMAC = new anagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection mocMAC = mcMAC.GetInstances();
foreach(ManagementObject m in mocMAC)
{
if((bool)m["IPEnabled"])
{
mymac1= m["MacAddress"].ToString();
break;
}
}
}
在Timer控件的Elapsed(达到间隔时发生)事件添加如下代码:
private void timer_watchmac_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{ //WMI在c#中的使用
ManagementClass mcMAC = new anagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection mocMAC = mcMAC.GetInstances();
foreach(ManagementObject m in mocMAC)//循环实例化管理对象
{
if((bool)m["IPEnabled"])
{ mymac2= m["MacAddress"].ToString();//管理对象(NIC)的当前mac地址
if (mymac2!=mymac1)//发现mac地址被修改
{ //关闭ie浏览器
Process[] myProcesses = Process.GetProcesses();
string test="";
foreach(Process myProcess in myProcesses)//枚举系统当前进程
{ test =myProcess.ProcessName ;
if (test=="IEXPLORE")//发现ie进程,还可以添加其他浏览器进程名称
{ myProcess.Kill (); //关闭ie进程 }
}
}
break;
}
}
}
WMI中Win32_NetworkAdapterConfiguration的定义:
MACAddress
Data type: string
Access type: Read-only //mac地址是只读的
Media Access Control (MAC) address of the network adapter. A MAC address is assigned by the manufacturer to uniquely identify the network adapter.
Example: "00:80:C7:8F:6C:96"
总结一下我自己的防范方式:在服务器上和客户机上作了双重限制,更为保险的方式是找一个能够直接读取EEPROM地址的三方软件,将它读取的值作为初始值提供给监视服务,再和当前MAC做比较。这样的话,非法操作者就只有通过修改数据包的方式来进行破坏了。当然现在流行的做法是把IP-MAC和交换机端口绑定,只是我们公司没有用这种能绑定端口的交换机。因此只能靠软件了,上帝保佑,阿门!
阅读全文(2800) | 评论:0 | 复制链接
c#中异步基于消息通信的完成端口的TCP/IP协议的组件实现(源代码) 服务器端(2007-6-11 11:42:00)
using System;
using System.IO;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace MyKJ
...{
/**//// <summary>
/// MyTcpIpClient 提供在Net TCP_IP 协议上基于消息的服务端
/// </summary>
public class MyTcpIpServer : System.ComponentModel.Component
...{
private int bufferSize=2048;
private string tcpIpServerIP="";
private int tcpIpServerPort=11000;
private Socket listener=null;
private ManualResetEvent allDone = new ManualResetEvent(false);
private ManualResetEvent sendDone = new ManualResetEvent(false);
private Thread thread=null;
private void StartListening()
...{
try
...{
listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress;
if(tcpIpServerIP.Trim()=="")
...{
ipAddress=IPAddress.Any;
}
else
...{
ipAddress=IPAddress.Parse(tcpIpServerIP);
}
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, tcpIpServerPort);
listener.Bind(localEndPoint);
listener.Listen(10);
while (true)
...{
allDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
allDone.WaitOne();
}
}
catch (Exception e)
...{
OnErrorServerEvent(new ErrorServerEventArgs(e,listener));
}
}
private void ReadCallback(IAsyncResult ar)
...{
Socket handler=null;
try
...{
lock(ar)
...{
StateObject state = (StateObject) ar.AsyncState;
handler = state.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
...{
int ReadPiont=0;
while(ReadPiont<bytesRead)
...{
if(state.Cortrol==0 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<24)&0xff000000;
state.packSize=bi1;
ReadPiont++;
state.Cortrol=1;
}
if(state.Cortrol==1 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<16)&0x00ff0000;
state.packSize=state.packSize+bi1;
ReadPiont++;
state.Cortrol=2;
}
if(state.Cortrol==2 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<8)&0x0000ff00;
state.packSize=state.packSize+bi1;
ReadPiont++;
state.Cortrol=3;
}
if(state.Cortrol==3 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=bi1&0xff;
state.packSize=state.packSize+bi1-4;
ReadPiont++;
state.Cortrol=4;
}
if(state.Cortrol==4 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<24)&0xff000000;
state.residualSize=bi1;
ReadPiont++;
state.Cortrol=5;
state.packSize-=1;
}
if(state.Cortrol==5 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<16)&0x00ff0000;
state.residualSize=state.residualSize+bi1;
ReadPiont++;
state.Cortrol=6;
state.packSize-=1;
}
if(state.Cortrol==6 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<8)&0x0000ff00;
state.residualSize=state.residualSize+bi1;
ReadPiont++;
state.Cortrol=7;
state.packSize-=1;
}
if(state.Cortrol==7 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=bi1&0xff;
state.residualSize=state.residualSize+bi1;
state.Datastream.SetLength(0);
state.Datastream.Position=0;
ReadPiont++;
state.Cortrol=8;
state.packSize-=1;
}
if(state.Cortrol==8 && ReadPiont<bytesRead)
...{
int bi1=bytesRead-ReadPiont;
int bi2=(int)(state.residualSize-state.Datastream.Length);
if(bi1>=bi2)
...{
state.Datastream.Write(state.buffer,ReadPiont,bi2);
ReadPiont+=bi2;
OnInceptServerEvent(new InceptServerEventArgs(state.Datastream,state.workSocket,this));
state.Cortrol=9;
state.packSize-=bi2;
}
else
...{
state.Datastream.Write(state.buffer,ReadPiont,bi1);
ReadPiont+=bi1;
state.packSize-=bi1;
}
}
if(state.Cortrol==9 && ReadPiont<bytesRead)
...{
int bi1=bytesRead-ReadPiont;
if(bi1<state.packSize)
...{
state.packSize=state.packSize-bi1;
ReadPiont+=bi1;
}
else
...{
state.Cortrol=0;
ReadPiont+=(int)state.packSize;
}
}
}
if(handler.Connected==true)
...{
handler.BeginReceive(state.buffer,0,bufferSize,0,
new AsyncCallback(ReadCallback), state);
}
}
else
...{
handler.Shutdown(SocketShutdown.Both);
handler.Close();
//throw(new Exception("读入的数据小于1bit"));
}
}
}
catch (Exception e)
...{
OnErrorServerEvent(new ErrorServerEventArgs(e,handler));
}
}
private void SendCallback(IAsyncResult ar)
...{
Socket client = (Socket) ar.AsyncState;
try
...{
int bytesSent = client.EndSend(ar);
}
catch (Exception e)
...{
OnErrorServerEvent(new ErrorServerEventArgs(e,client));
}
finally
...{
sendDone.Set();
}
}
private void AcceptCallback(IAsyncResult ar)
...{
Socket handler=null;
try
...{
Socket listener = (Socket) ar.AsyncState;
handler= listener.EndAccept(ar);
StateObject state = new StateObject(bufferSize,handler);
state.workSocket = handler;
handler.BeginReceive(state.buffer,0,bufferSize,0,
new AsyncCallback(ReadCallback), state);
}
catch (Exception e)
...{
OnErrorServerEvent(new ErrorServerEventArgs(e,handler));
}
finally
...{
allDone.Set();
}
}
/**//// <summary>
/// 析构
/// </summary>
/// <param name="disposing">不知道</param>
protected override void Dispose(bool disposing)
...{
Abort();
}
/**//// <summary>
/// 引发接收事件
/// </summary>
/// <param name="e">数据</param>
protected virtual void OnInceptServerEvent(InceptServerEventArgs e)
...{
if (InceptServer != null)
...{
InceptServer(this, e);
}
}
/**//// <summary>
/// 引发错误事件
/// </summary>
/// <param name="e">数据</param>
protected virtual void OnErrorServerEvent(ErrorServerEventArgs e)
...{
if (ErrorServer != null)
...{
ErrorServer(this, e);
}
}
/**//// <summary>
/// 开始监听访问
/// </summary>
public void Listening()
...{
//StartListening();
thread=new Thread(new ThreadStart(StartListening));
thread.Name="MyTcpIpServer.Listening";
thread.Start();
}
/**//// <summary>
/// 异常中止服务
/// </summary>
public void Abort()
...{
if(thread!=null)
...{
thread.Abort();
listener.Close();
}
}
/**//// <summary>
///构造
/// </summary>
/// <param name="container">父控件</param>
public MyTcpIpServer(System.ComponentModel.IContainer container)
...{
container.Add(this);
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/**//// <summary>
/// 构造
/// </summary>
public MyTcpIpServer()
...{
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
Component Designer generated code#region Component Designer generated code
/**//// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
...{
}
#endregion
/**//// <summary>
/// 要连接的服务器IP地址
/// </summary>
public string TcpIpServerIP
...{
get
...{
return tcpIpServerIP;
}
set
...{
tcpIpServerIP=value;
}
}
/**//// <summary>
/// 要连接的服务器所使用的端口
/// </summary>
public int TcpIpServerPort
...{
get
...{
return tcpIpServerPort;
}
set
...{
tcpIpServerPort=value;
}
}
/**//// <summary>
/// 缓冲器大小
/// </summary>
public int BufferSize
...{
get
...{
return bufferSize;
}
set
...{
bufferSize=value;
}
}
/**//// <summary>
/// 连接的活动状态
/// </summary>
public bool Activ
...{
get
...{
return listener.Connected;
}
//set
//{
// activ=value;
//}
}
/**//// <summary>
/// 发送一个流数据
/// </summary>
public void Send(Socket ClientSocket,Stream Astream)
...{
try
...{
if(ClientSocket.Connected==false)
...{
throw(new Exception("没有连接客户端不可以发送信息!"));
}
Astream.Position=0;
byte[] byteData=new byte[bufferSize];
int bi1=(int)((Astream.Length+8)/bufferSize);
int bi2=(int)Astream.Length;
if(((Astream.Length+8)%bufferSize)>0)
...{
bi1=bi1+1;
}
bi1=bi1*bufferSize;
byteData[0]=System.Convert.ToByte(bi1>>24);
byteData[1]=System.Convert.ToByte((bi1&0x00ff0000)>>16);
byteData[2]=System.Convert.ToByte((bi1&0x0000ff00)>>8);
byteData[3]=System.Convert.ToByte((bi1&0x000000ff));
byteData[4]=System.Convert.ToByte(bi2>>24);
byteData[5]=System.Convert.ToByte((bi2&0x00ff0000)>>16);
byteData[6]=System.Convert.ToByte((bi2&0x0000ff00)>>8);
byteData[7]=System.Convert.ToByte((bi2&0x000000ff));
int n = Astream.Read(byteData, 8, byteData.Length-8);
while (n>0)
...{
ClientSocket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), ClientSocket);
sendDone.WaitOne();
byteData=new byte[bufferSize];
n = Astream.Read(byteData,0,byteData.Length);
}
}
catch (Exception e)
...{
OnErrorServerEvent(new ErrorServerEventArgs(e,ClientSocket));
}
}
/**//// <summary>
/// 接收到数据事件
/// </summary>
public event InceptServerEvent InceptServer;
/**//// <summary>
/// 发生错误事件
/// </summary>
public event ErrorServerEvent ErrorServer;
}
/**//// <summary>
/// 状态对象
/// </summary>
public class StateObject
...{
/**//// <summary>
/// 构造
/// </summary>
/// <param name="bufferSize">缓存</param>
/// <param name="WorkSocket">工作的插座</param>
public StateObject(int bufferSize,Socket WorkSocket)
...{
buffer = new byte[bufferSize];
workSocket=WorkSocket;
}
/**//// <summary>
/// 缓存
/// </summary>
public byte[] buffer = null;
/**//// <summary>
/// 工作插座
/// </summary>
public Socket workSocket = null;
/**//// <summary>
/// 数据流
/// </summary>
public Stream Datastream=new MemoryStream();
/**//// <summary>
/// 剩余大小
/// </summary>
public long residualSize=0;
/**//// <summary>
/// 数据包大小
/// </summary>
public long packSize=0;
/**//// <summary>
/// 计数器
/// </summary>
public int Cortrol=0;
}
/**//// <summary>
/// 接收事件
/// </summary>
public class InceptServerEventArgs : EventArgs
...{
private readonly Stream datastream;
private readonly Socket serverSocket;
private readonly MyTcpIpServer tcpIpServer;
/**//// <summary>
/// 构造
/// </summary>
/// <param name="Astream">数据</param>
/// <param name="ServerSocket">工作插座</param>
/// <param name="TcpIpServer">提供服务的TCP/IP对象</param>
public InceptServerEventArgs(Stream Astream,Socket ServerSocket,MyTcpIpServer TcpIpServer)
...{
datastream=Astream;
serverSocket=ServerSocket;
tcpIpServer=TcpIpServer;
}
/**//// <summary>
/// 数据
/// </summary>
public Stream Astream
...{
get ...{ return datastream;}
}
/**//// <summary>
/// 工作插座
/// </summary>
public Socket ServerSocket
...{
get ...{ return serverSocket;}
}
/**//// <summary>
/// 提供TCP/IP服务的服务器对象.
/// </summary>
public MyTcpIpServer TcpIpServer
...{
get ...{ return tcpIpServer;}
}
}
/**//// <summary>
/// 接收数据委托
/// </summary>
public delegate void InceptServerEvent(object sender, InceptServerEventArgs e);
/**//// <summary>
/// 错误事件委托
/// </summary>
public class ErrorServerEventArgs : EventArgs
...{
private readonly Exception error;
private readonly Socket serverSocket;
/**//// <summary>
/// 构造
/// </summary>
/// <param name="Error">数据</param>
/// <param name="ServerSocket">问题插座</param>
public ErrorServerEventArgs(Exception Error,Socket ServerSocket)
...{
error=Error;
serverSocket=ServerSocket;
}
/**//// <summary>
/// 数据
/// </summary>
public Exception Error
...{
get ...{ return error;}
}
/**//// <summary>
/// 问题插座
/// </summary>
public Socket ServerSocket
...{
get ...{ return serverSocket;}
}
}
/**//// <summary>
///错误事件委托
/// </summary>
public delegate void ErrorServerEvent(object sender, ErrorServerEventArgs e);
}
阅读全文(2409) | 评论:0 | 复制链接
c#中异步基于消息通信的完成端口的TCP/IP协议的组件实现(源代码) 客户端 (2007-6-11 11:41:00)
using System;
using System.IO;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace MyKJ
...{
/**//// <summary>
/// MyTcpIpClient 提供在Net TCP_IP 协议上基于消息的客户端
/// </summary>
public class MyTcpIpClient : System.ComponentModel.Component
...{
private int bufferSize=2048;
private string tcpIpServerIP="127.0.0.1";
private int tcpIpServerPort=11000;
private Socket ClientSocket=null;
private ManualResetEvent connectDone = new ManualResetEvent(false);
private ManualResetEvent sendDone = new ManualResetEvent(false);
private void ConnectCallback(IAsyncResult ar)
...{
try
...{
Socket client = (Socket) ar.AsyncState;
client.EndConnect(ar);
}
catch (Exception e)
...{
OnErrorEvent(new ErrorEventArgs(e));
}
finally
...{
connectDone.Set();
}
}
private void SendCallback(IAsyncResult ar)
...{
try
...{
Socket client = (Socket) ar.AsyncState;
int bytesSent = client.EndSend(ar);
//Console.WriteLine(bytesSent);
}
catch (Exception e)
...{
OnErrorEvent(new ErrorEventArgs(e));
}
finally
...{
sendDone.Set();
}
}
private void ReceiveCallback(IAsyncResult ar)
...{
Socket handler=null;
try
...{
lock(ar)
...{
StateObject state = (StateObject) ar.AsyncState;
handler = state.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
...{
int ReadPiont=0;
while(ReadPiont<bytesRead)
...{
if(state.Cortrol==0 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<24)&0xff000000;
state.packSize=bi1;
ReadPiont++;
state.Cortrol=1;
}
if(state.Cortrol==1 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<16)&0x00ff0000;
state.packSize=state.packSize+bi1;
ReadPiont++;
state.Cortrol=2;
}
if(state.Cortrol==2 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<8)&0x0000ff00;
state.packSize=state.packSize+bi1;
ReadPiont++;
state.Cortrol=3;
}
if(state.Cortrol==3 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=bi1&0xff;
state.packSize=state.packSize+bi1-4;
ReadPiont++;
state.Cortrol=4;
}
if(state.Cortrol==4 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<24)&0xff000000;
state.residualSize=bi1;
ReadPiont++;
state.Cortrol=5;
state.packSize-=1;
}
if(state.Cortrol==5 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<16)&0x00ff0000;
state.residualSize=state.residualSize+bi1;
ReadPiont++;
state.Cortrol=6;
state.packSize-=1;
}
if(state.Cortrol==6 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=(bi1<<8)&0x0000ff00;
state.residualSize=state.residualSize+bi1;
ReadPiont++;
state.Cortrol=7;
state.packSize-=1;
}
if(state.Cortrol==7 && ReadPiont<bytesRead)
...{
long bi1=state.buffer[ReadPiont];
bi1=bi1&0xff;
state.residualSize=state.residualSize+bi1;
state.Datastream.SetLength(0);
state.Datastream.Position=0;
ReadPiont++;
state.Cortrol=8;
state.packSize-=1;
}
if(state.Cortrol==8 && ReadPiont<bytesRead)
...{
int bi1=bytesRead-ReadPiont;
int bi2=(int)(state.residualSize-state.Datastream.Length);
if(bi1>=bi2)
...{
state.Datastream.Write(state.buffer,ReadPiont,bi2);
ReadPiont+=bi2;
OnInceptEvent(new InceptEventArgs(state.Datastream,handler));
state.Cortrol=9;
state.packSize-=bi2;
}
else
...{
state.Datastream.Write(state.buffer,ReadPiont,bi1);
ReadPiont+=bi1;
state.packSize-=bi1;
}
}
if(state.Cortrol==9 && ReadPiont<bytesRead)
...{
int bi1=bytesRead-ReadPiont;
if(bi1<state.packSize)
...{
state.packSize=state.packSize-bi1;
ReadPiont+=bi1;
}
else
...{
state.Cortrol=0;
ReadPiont+=(int)state.packSize;
}
}
}
}
else
...{
throw(new Exception("读入的数据小于1bit"));
}
if(handler.Connected==true)
...{
handler.BeginReceive(state.buffer,0,bufferSize,0,
new AsyncCallback(ReceiveCallback), state);
}
}
}
catch (Exception e)
...{
OnErrorEvent(new ErrorEventArgs(e));
}
}
/**//// <summary>
/// 连接服务器
/// </summary>
public void Conn()
...{
try
...{
ClientSocket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse(tcpIpServerIP);
IPEndPoint remoteEP = new IPEndPoint(ipAddress, tcpIpServerPort);
connectDone.Reset();
ClientSocket.BeginConnect(remoteEP,new AsyncCallback(ConnectCallback),ClientSocket);
connectDone.WaitOne();
StateObject state = new StateObject(bufferSize,ClientSocket);
ClientSocket.BeginReceive(state.buffer,0,bufferSize,0,
new AsyncCallback(ReceiveCallback), state);
}
catch(Exception e)
...{
OnErrorEvent(new ErrorEventArgs(e));
}
}
/**//// <summary>
/// 断开连接
/// </summary>
public void Close()
...{
try
...{
if(ClientSocket.Connected==true)
...{
ClientSocket.Shutdown(SocketShutdown.Both);
ClientSocket.Close();
}
}
catch(Exception e)
...{
OnErrorEvent(new ErrorEventArgs(e));
}
}
/**//// <summary>
/// 发送一个流数据
/// </summary>
/// <param name="Astream">数据流</param>
public void Send(Stream Astream)
...{
try
...{
if(ClientSocket.Connected==false)
...{
throw(new Exception("没有连接服务器不可以发送信息!"));
}
Astream.Position=0;
byte[] byteData=new byte[bufferSize];
int bi1=(int)((Astream.Length+8)/bufferSize);
int bi2=(int)Astream.Length;
if(((Astream.Length+8)%bufferSize)>0)
...{
bi1=bi1+1;
}
bi1=bi1*bufferSize;
byteData[0]=System.Convert.ToByte(bi1>>24);
byteData[1]=System.Convert.ToByte((bi1&0x00ff0000)>>16);
byteData[2]=System.Convert.ToByte((bi1&0x0000ff00)>>8);
byteData[3]=System.Convert.ToByte((bi1&0x000000ff));
byteData[4]=System.Convert.ToByte(bi2>>24);
byteData[5]=System.Convert.ToByte((bi2&0x00ff0000)>>16);
byteData[6]=System.Convert.ToByte((bi2&0x0000ff00)>>8);
byteData[7]=System.Convert.ToByte((bi2&0x000000ff));
int n = Astream.Read(byteData, 8, byteData.Length-8);
while (n>0)
...{
ClientSocket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), ClientSocket);
sendDone.WaitOne();
byteData=new byte[bufferSize];
n = Astream.Read(byteData,0,byteData.Length);
}
}
catch (Exception e)
...{
OnErrorEvent(new ErrorEventArgs(e));
}
}
/**//// <summary>
/// 构造
/// </summary>
/// <param name="container">父控件</param>
public MyTcpIpClient(System.ComponentModel.IContainer container)
...{
container.Add(this);
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/**//// <summary>
/// 构造
/// </summary>
public MyTcpIpClient()
...{
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
Component Designer generated code#region Component Designer generated code
/**//// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
...{
}
#endregion
/**//// <summary>
/// 要连接的服务器IP地址
/// </summary>
public string TcpIpServerIP
...{
get
...{
return tcpIpServerIP;
}
set
...{
tcpIpServerIP=value;
}
}
/**//// <summary>
/// 要连接的服务器所使用的端口
/// </summary>
public int TcpIpServerPort
...{
get
...{
return tcpIpServerPort;
}
set
...{
tcpIpServerPort=value;
}
}
/**//// <summary>
/// 缓冲器大小
/// </summary>
public int BufferSize
...{
get
...{
return bufferSize;
}
set
...{
bufferSize=value;
}
}
/**//// <summary>
/// 连接的活动状态
/// </summary>
public bool Activ
...{
get
...{
if(ClientSocket==null)
...{
return false;
}
return ClientSocket.Connected;
}
}
/**//// <summary>
/// 接收到数据引发的事件
/// </summary>
public event InceptEvent Incept;
/**//// <summary>
/// 引发接收数据事件
/// </summary>
/// <param name="e">接收数据</param>
protected virtual void OnInceptEvent(InceptEventArgs e)
...{
if (Incept != null)
...{
Incept(this, e);
}
}
/**//// <summary>
/// 发生错误引发的事件
/// </summary>
public event ErrorEvent Error;
/**//// <summary>
/// 引发错误事件
/// </summary>
/// <param name="e">错误数据</param>
protected virtual void OnErrorEvent(ErrorEventArgs e)
...{
if (Error != null)
...{
Error(this, e);
}
}
}
/**//// <summary>
/// 接收数据事件
/// </summary>
public class InceptEventArgs : EventArgs
...{
private readonly Stream datastream;
private readonly Socket clientSocket;
/**//// <summary>
/// 构造
/// </summary>
/// <param name="Astream">接收到的数据</param>
/// <param name="ClientSocket">接收的插座</param>
public InceptEventArgs(Stream Astream,Socket ClientSocket)
...{
datastream=Astream;
clientSocket=ClientSocket;
}
/**//// <summary>
/// 接受的数据流
/// </summary>
public Stream Astream
...{
get ...{ return datastream;}
}
/**//// <summary>
/// 接收的插座
/// </summary>
public Socket ClientSocket
...{
get ...{ return clientSocket;}
}
}
/**//// <summary>
/// 定义接收委托
/// </summary>
public delegate void InceptEvent(object sender, InceptEventArgs e);
/**//// <summary>
/// 错处事件
/// </summary>
public class ErrorEventArgs : EventArgs
...{
private readonly Exception error;
/**//// <summary>
/// 构造
/// </summary>
/// <param name="Error">错误信息对象</param>
public ErrorEventArgs(Exception Error)
...{
error=Error;
}
/**//// <summary>
/// 错误信息对象
/// </summary>
public Exception Error
...{
get ...{ return error;}
}
}
/**//// <summary>
/// 错误委托
/// </summary>
public delegate void ErrorEvent(object sender, ErrorEventArgs e);
}
阅读全文(2145) | 评论:0 | 复制链接
我的 C#+flash socket 聊天程序(C#源代码)(2007-6-11 11:39:00)
此篇文章为roading所有,欢迎转载,但请注明原处.
看见还有很多人在研究flash的socket,其中经常会出现一些问题,所以将我以前写的一个程序代码拿出来给大家参考...
这是c#的代码,经过测试的,本来想把源程序都放上来,可以我用的是vs2005(而且现在又坏了,系统出问题了),下面是程序的主要源代码,不包含一些自动生成的代码.这些代码是根据一个开源的C#socket程序改编的,而且我已经写了比较详细的注释了,如果你看了这些代码还是发现有问题,可以向我索取完整的源程序:
把源文件传上来,大家可以下载(gmail又打不开了,不能给留email的同学发了,自己下载吧):
点击下载此文件
//--------------------------------
//---------------------------------------------------------------------------------------------------------------
//form1.cs
using System;
using System.IO;
using System.Drawing;
using System.Collections;//ArrayList引用到这个命名空间的类
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace ChatServer//服务器端命名空间
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private int listenport = 9050;//监听端口
private TcpListener listener;//监听者
private ArrayList clients;//所有的client
private Thread processor;//处理线程
private Socket clientsocket;//client套接字
private Thread clientservice;//client的服务
private System.Windows.Forms.ListBox lbClients;
private System.Windows.Forms.Label label1;//显示在线人员的List控件
private TcpClient tclient;
private NetworkStream ns;
private System.Windows.Forms.Button button1;
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
Thread.CurrentThread.IsBackground = true;
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
clients = new ArrayList();//新建一个ArrayList类型用以存储所有的client
processor = new Thread(new ThreadStart(StartListening));//新建一个处理线程
processor.Start();//线程开始
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
int c = clients.Count;
for(int n=0; n<c; n++)
{
Client cl = (Client)clients[n];
cl.Sock.Close();
cl.CLThread.Abort();
}
//client.Close();
listener.Stop();
processor.Abort();
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
/// <summary>
/// 开始监听
/// </summary>
private void StartListening()
{
IPAddress ipAddress = Dns.Resolve(Dns.GetHostName()).AddressList[0];
//IPAddress ipAddress = IPAddress.Parse("192.168.0.132");
label1.Text=ipAddress.ToString();
IPEndPoint ipLocalEndPoint = new IPEndPoint(ipAddress, listenport);
listener = new TcpListener(ipLocalEndPoint);
listener.Start();
while (true)
{
try
{
Socket s = listener.AcceptSocket();//接收一个套接字
clientsocket = s;//赋值给clientsocket
clientservice = new Thread(new ThreadStart(ServiceClient));//为新进client服
务建立线程
clientservice.Start();//线程开始
}
catch(Exception e)//如果出现异常则打控制台打印到屏幕
{
Console.WriteLine(e.ToString() );
}
}
}
/// <summary>
/// 给一个客户提供服务
/// </summary>
private void ServiceClient()
{
Socket client = clientsocket;//赋值给client
bool keepalive = true;
bool s;
while (keepalive)
{
Byte[] buffer = new Byte[1024];//一个1024bits的缓存区
try
{
client.Receive(buffer);
}
catch(SocketException ex)
{
//客户端退出
//MessageBox.Show(ex.ToString());
string leaveName="";
int remove = 0;
bool found = false;
int c = clients.Count;
//从client的ArrayList中查找有没有相符的client
//有的话就作好删掉的准备
for(int n=0; n<c; n++)
{
Client cl = (Client)clients[n];
if(cl.Host==client.RemoteEndPoint)
{
leaveName = cl.Name;
remove = n;
found = true;
lbClients.Items.Remove(cl);//List控件中删除这个client
break;
}
}
if(found)
{
for(int n=0; n<c; n++)
{
Client cl = (Client)clients[n];
//MessageBox.Show( "GONE|"+leaveName);
SendToClient(cl, "GONE|"+leaveName);
}
clients.RemoveAt(remove);//从clients的ArrayList里面删掉要当前要退出
的client
client.Close();//关闭套接口
keepalive = false;//keepalive=false则这个线程服务完毕
}
}
string clientcommand = System.Text.Encoding.UTF8.GetString(buffer);//把得到的数据用
ASCII的编码形式读出解决中文的显示问题
label1.Text=clientcommand;
string[] tokens = clientcommand.Split(new Char[]{'|'});//以|号划分的命令数据
Console.WriteLine(clientcommand);
if (tokens[0] == "CONN")//连接命令消息
{
//给现有的client发送新进一个client的消息
for(int n=0; n<clients.Count; n++)
{
Client cl = (Client)clients[n];
SendToClient(cl, "JOIN|" + tokens[1]);
}
//新加一个client
EndPoint ep = client.RemoteEndPoint;
Client c = new Client(tokens[1], ep, clientservice, client);
clients.Add(c);
//给每个client发一个当前所有client的列表消息
//string message = "LIST|" + GetChatterList();
//new byte(0)
//byte b = 0;
string message = "LIST|"+"asdasd";
//MessageBox.Show(message.Length +"="+message);
SendToClient(c, message);
//MessageBox.Show(message);
//服务器List控件新加这个client
lbClients.Items.Add(c);
}
else
if (tokens[0] == "CHAT")//聊天命令消息
{
//给每个client发送聊天消息
for(int n=0; n<clients.Count; n++)
{
Client cl = (Client)clients[n];
SendToClient(cl, clientcommand);
}
}
else
if (tokens[0] == "PRIV") //私聊命令消息
{
string destclient = tokens[2];//目标client
for(int n=0; n<clients.Count; n++)
{
Client cl = (Client)clients[n];
if(cl.Name.CompareTo(tokens[2]) == 0)//给目标client发聊天消息
SendToClient(cl, clientcommand);
if(cl.Name.CompareTo(tokens[1]) == 0)//给自己发聊天消息
SendToClient(cl, clientcommand);
}
}
else
if (tokens[0] == "GONE")//离开命令消息
{
int remove = 0;
bool found = false;
int c = clients.Count;
//从client的ArrayList中查找有没有相符的client
//有的话就作好删掉的准备
for(int n=0; n<c; n++)
{
Client cl = (Client)clients[n];
SendToClient(cl, clientcommand);
if(cl.Name.CompareTo(tokens[1]) == 0)
{
remove = n;
found = true;
lbClients.Items.Remove(cl);//List控件中删除这个client
}
}
if(found)
clients.RemoveAt(remove);//从clients的ArrayList里面删掉要当前要退出
的client
client.Close();//关闭套接口
keepalive = false;//keepalive=false则这个线程服务完毕
}
else
{
//MessageBox.Show(clientcommand);
for(int n=0; n<clients.Count; n++)
{
Client cl = (Client)clients[n];
SendToClient(cl, "-");
}
}
}
}
/// <summary>
/// 发送消息到指定的client
/// </summary>
/// <param name="cl">client</param>
/// <param name="message">消息</param>
private void SendToClient(Client cl, string message)
{
try
{
//MessageBox.Show(message);
message += "0";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(message.ToCharArray());
buffer[buffer.Length -2] = 0;
cl.Sock.Send(buffer,buffer.Length,0);
}
catch(Exception)//如果有异常则退出
{
//MessageBox.Show(message);
clients.Remove(cl);
lbClients.Items.Remove(cl.Name + " : " + cl.Host.ToString());
for(int n=0; n<clients.Count; n++)
{
Client cl1 = (Client)clients[n];
SendToClient(cl1, "GONE|"+cl.Name);
}
cl.Sock.Close();
cl.CLThread.Abort();
}
}
/// <summary>
/// 获得所有聊天者的列表
/// 中间以"|"符号间隔
/// </summary>
/// <returns></returns>
private string GetChatterList()
{
string chatters = "";
for(int n=0; n<clients.Count; n++)
{
Client cl = (Client)clients[n];
chatters += cl.Name;
//MessageBox.Show(cl.Name.Length +"=" +cl.Name);
//chatters += "welcome";
chatters += "|";
//MessageBox.Show(cl.Name);
}
//chatters += "欢迎你的到来";
//MessageBox.Show(chatters);
chatters.Trim(new char[]{'|'});
return chatters;
}
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
//管理员消息```
private void button1_Click(object sender, System.EventArgs e)
{
//initClient();
if(clients.Count > 0)
{
int c = clients.Count;
for(int n=0; n<c; n++)
{
string message = "CHAT|..";
Client cl = (Client)clients[n];
SendToClient(cl, message);
}
}
}
}
}
//--------------------------------------------------------------------------------------------------------------
//Client.cs
using System;
using System.Threading;
namespace ChatServer //定义命名空间
{
using System.Net.Sockets;
using System.Net;
/// <summary>
/// Client 的摘要说明。
/// </summary>
public class Client
{
private Thread clthread;//client的线程
private EndPoint endpoint;//终端
private string name;//client的名称
private Socket sock;//套接口
/// <summary>
/// 构造函数,初始化所有的私有变量
/// </summary>
/// <param name="_name">名称</param>
/// <param name="_endpoint">终端</param>
/// <param name="_thread">线程</param>
/// <param name="_sock">套接口</param>
public Client(string _name, EndPoint _endpoint, Thread _thread, Socket _sock)
{
// TODO: 在此处添加构造函数逻辑
clthread = _thread;
endpoint = _endpoint;
name = _name;
sock = _sock;
}
/// <summary>
/// 重载:转成字串
/// </summary>
/// <returns>返回终端加上client名称</returns>
public override string ToString()
{
return endpoint.ToString()+ " : " + name;
}
/// <summary>
/// 获取和设置线程
/// </summary>
public Thread CLThread
{
get{return clthread;}
set{clthread = value;}
}
/// <summary>
/// 获取和设置终端
/// </summary>
public EndPoint Host
{
get{return endpoint;}
set{endpoint = value;}
}
/// <summary>
/// 获取和设置client名称
/// </summary>
public string Name
{
get{return name;}
set{name = value;}
}
/// <summary>
/// 获取和设置套接口
/// </summary>
public Socket Sock
{
get{return sock;}
set{sock = value;}
}
}
}
阅读全文(2649) | 评论:1 | 复制链接


最新评论