黄巾之乱-网络模块

来源:互联网 发布:网络图片 侵权 编辑:程序博客网 时间:2024/04/27 09:24


 服务器端,对应于一个客户端都有一个对象,这些对象被放到list中,控制了这个list,就控制了整个服务器的通讯。

服务器端的这个对象叫tcpuser, 里面放一些变量,有一个变量比较特别,tcpclient,这个变量以后就是它同客户端建立起通道然后通讯。

public class tcpuser    {        public int id { get; set; }        public TcpClient tcp { get; set; }        public string username { get; set; }        public string password { get; set; }        public string txtin { get; set; }        public string txtout { get; set; }        public string heart { get; set; }    }

tcpuser对象放到一个list对象集中。

public List<tcpuser> tcpuserlist = new List<tcpuser>();

服务器网络模块运行时,需要用一个IP和端口做监听,就是netstat -an中看到的listening, 客户段就用这个端口接入。


既然使用C#,为了最简单,就不去使用socket,直接用tcplistener,很方便。

下面的函数,我放到了一个线程中,这个线程执行起来,它会监听等待用户连接。

 private void server_listen()        {            listener = new TcpListener(IPAddress.Any, 3000);            listener.Start();            while (true)            {                Thread.Sleep(1000);                while (listener.Pending())  //跳开阻塞,用这个在关闭服务器程序时不会出现异常                {                    tcpuser one = new tcpuser();                                 one.tcp = listener.AcceptTcpClient();                    NetworkStream stream = one.tcp.GetStream();                     try {                            if (stream.CanRead) {  stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallBack), one);  }                         }                     catch                         {                                                 }                      one.id = tcpuserid;                    tcpuserlist.Add(one);                    tcpuserid++;                }                           }        }

上面的代码,当一个客户端接入,服务器会新建一个tcpuser对象,把通过客户端通讯的通道连接到tcpuser对象的一个变量TcpClient 上。

通道建立好后,在这个stream中,使用beginread异步来读取客户端传来的数据。同当前线程中对立的是,这个beginread启动了系统回调功能,系统去处理beginread中的代码,再无需我们关心。只是在beginread代码的最后,不能让它就这么结束了。应该要让它一直接跑作,才能一直接收用户的数据,所有最后,我们让beginread再调用自己,这样就可以一直运行下去。

下面代码就是这个一直自己调用自己的回调函数:

public void ReadCallBack(IAsyncResult ar)        {                     try            {                tcpuser one = (tcpuser)ar.AsyncState;                //心跳改变                one.heart = DateTime.Now.ToString();                  NetworkStream stream = (NetworkStream)one.tcp.GetStream();                 int numberOfBytesRead = stream.EndRead(ar);                one.txtin = Encoding.UTF8.GetString(buffer, 0, numberOfBytesRead);                //处理客户端数据                string[] s = one.txtin.Split('|');                //如果是1,1 就是认证用的用户密码数据                if ((int.Parse(s[0]) == 1) && (int.Parse(s[1]) == 1)) { one.username = s[2]; one.password = s[3]; }                                                             stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallBack), one);                //卡了一个写回客户端                 if (one.txtout != null)                {                    wbuffer = Encoding.UTF8.GetBytes(one.txtout);                    if (stream.CanWrite) { stream.BeginWrite(wbuffer, 0, wbuffer.Length, new AsyncCallback(WriteCall), one); }                }            }              catch             {                              }                      }

这个回调函数,一旦跑起来,就可以一直由系统来处理传入的用户数据,这是一个很好的功能。

客户端一次传入的数据,函数中就近就放在txtin变量中,等服务器其它模块来处理,反正网络模块就只能处理到这里了。

其它模块只需要遍历tcpuser 这个list,取出来看看txtin变量,就知道客户端想做什么。

目前我干脆把写回客户端的功能,也放到了里面。 还加入了心跳功能,解析一种客户端数据的功能。

心跳功能,其实就是在回调函数中不停的刷新heart这个变量,

回调函数是基于通道stream的,如果客户端退出,通道就关闭,stream就会无效,回调函数就会停工。

心跳变量就会停止刷新,检查心跳变量,没有刷新的,就是客户端已经退出的对应于服务器的tcpuser对象。



基于上面的设计,先搞了一个原型出来,测试了一下。

模拟了500个客户端连接服务器,每秒发送一串字符。

服务器使用beginread函数,没有为每个连接建立单独的线程,服务器程序才几个线程就可以应付了。


0 0
原创粉丝点击