我的IM - 基础篇[1] - Socket

看到那么多人支持,我很感动,所以临时决定从今天开始讲述如何编写IM。

那么今天开始第一讲, Socket.

  

  为什么上来就讲Socket呢?因为我觉得作为一个对于IM很感兴趣的人,应该瞬间提升对于网络编程的高度认知,只有这样才能快速的学到东西。

那么好了,开始实战!

  Socket又分为异步套接字和同步套接字,我在项目中基本上都是用的同步(当然,您可以使用异步),然后自己New的线程,这样的话,我感觉有几点好处。

  1. 对于线程拥有更加的认知度。

  2. 启用了自己制作的线程池(ThreadPool <-这个我自己写了个)

  

  那么先说线程,Socket监听一般是需要2个While--true的,这个也是通用写法,貌似很多学校也这样教?

  那么下面进入代码时间,我先来粘贴出一段有问题的代码(服务端部分):

 

       


while (_IsListen) //<----监听标示
{
    Listener.Start();  
//一直监听
    
//尝试接受连接
    this.socket = Listener.AcceptSocket();    //获得连接信息
    
//MessageBox.Show("有人连接了:Socket Port:" +    this.socket.RemoteEndPoint.ToString());
    
//如果此套接字已经连接上
    if (this.socket.Connected)
    {
        
while(true)
        {
            
//do sth
        }

     }
}
catch(Exception ex)
{

}
 
 

  上面这段代码减去了没必要的部分,但是足足可以说明问题,上面这段代码运行是没有问题的,而且能监听指定端口。

不知道您看出问题了吗?

  慢慢的,你就会发现,这段代码有问题了,而且有大问题,因为当我有1个用户上线并且成功与服务器连接后,再来一个用户上线,那么第一个

用户便不会再与服务器进行任何响应了,这是为什么呢?

  呵呵 ,可能有人看出来了?服务器第一个While循环为了得到用户的SOCKET,当得到后,传递到第二个While,这样一来,就会一直在第二个While循环中

出不来了……而且整个窗体会卡住。

  么好,我们改下,比如现在把第二个While用多线程处理,看代码(服务端部分):

 

 


public void Lis_Port()
        {
            Listener 
= new TcpListener(Globle._OnLinePort);  //默认上线端口:10000
            try
            {
                Globle._isRuning 
= 1;  //开启启动
                this.Main_f.BeginInvoke(new Pt(Main_f.Update_TM_Run));
                
this.Main_f.BeginInvoke(new Pt(Main_f.Update_State));
                
while (_IsListen)
                {
                    Listener.Start();  
//一直监听
                    
//尝试接受连接
                    this.socket = Listener.AcceptSocket();    //获得连接信息
                    
//MessageBox.Show("有人连接了:Socket Port:" +  this.socket.RemoteEndPoint.ToString());
                    
//如果此套接字已经连接上
                    if (this.socket.Connected)
                    {
                        Thread thread 
= new Thread(new ThreadStart(this.Rev));
                        thread.Start();
                    }
                    
                }
            }
            
catch(Exception ex)
            {
                Globle._isRuning 
= 0;  //停止启动
                this.Main_f.BeginInvoke(new Pt(Main_f.Update_State));
                
//MessageBox.Show("端口监听出现错误:" + ex.Message);
            }
        }

        
#endregion

        
#region 此方法用于监听用户命令

        
/*
         * 此方法用于监听用户命令
         
*/
        
public void Rev()
        {
            
while (_IsListen)
            {
                
try
                {
                    
byte[] bb = new byte[512];
                    
//如果此套接字已经连接上
                    if (this.socket.Connected)
                    {
                        Stream 
= new NetworkStream(this.socket);  //获取用户流信息
                        Stream.Read(bb, 0, bb.Length);  //读取内容
                        Stream.Flush();  //清空
                        
//MessageBox.Show(Encoding.Default.GetString(bb));
                        this.Rev_Order = (Encoding.Default.GetString(bb)).Replace("/0""");  //转换成字符串
                        String[] Orders_sp = this.Rev_Order.Split(new String[] { "$$" }, StringSplitOptions.RemoveEmptyEntries);
                        
//如果得到的命令不是登录后的信息传递
                        if (Orders_sp.Length <= 1)
                        {
                            String[] Orders 
= this.Rev_Order.Split(new String[] { "|**|" }, StringSplitOptions.RemoveEmptyEntries);
                            
if (Orders.Length <= 1)
                            {

                                    String Ip 
= IPAddress.Parse(((IPEndPoint)this.socket.RemoteEndPoint).Address.ToString()).ToString();
                                    Rev2Client_SendMessage Rev 
= new Rev2Client_SendMessage(Stream, this.Rev_Order, Ip);
                                }
                                
catch (Exception ex)
                                {
                                    MessageBox.Show(
"显示用户上线的时候出现错误:" + ex.Message);
                                }
                            }
                            
//QQ注册
                            else
                            {
                                //do sth
                            }
                        }
                        
else
                        {
                            
//do sth                      
          }                        
                    }
                    
else
                    {
                        
//MessageBox.Show("套接字已经断开了");
                    }
                }

            }
        }
 
呵 太多了,我就不精简了,总体来说,这次是用了多线程,将第二个While包在了方法体内,
然后第二个While监听全局的Socket。
那么这样一来,是不是比刚才好了呢???
是我告诉你,这段代码还是有问题,而且是一个大问题,你看出来了吗?
 
那么,这段代码运行后,出现的问题是,虽然窗体不卡了,但是,当第二个用户登录到服务器后,第一个用户同样还是
无法与服务器产生任何响应了!!
 
这是为什么呢??
 
  让我们来仔细研究代码,当地一个用户连接服务器的时候,服务器得到了第一个用户的Socket且把它赋值到全局
然后第二个While就会循环监听这个全局的Socket,那么,当第二个用户连接服务器的时候,还是按照规矩,将第二个用户的Socket
赋值全局,这样问题就来了!!第一个用户的Socket不是就被覆盖了吗?是的!!这个就是问题所在。
 
  那么我现在给出本次讲解中,真正正确的解决方案,同时这个方案也被包含在了我自己的通讯框架中。
我们需要2个类。第一个类负责监听端口,(当然了还是While,但是要运行在多线程中,否则会卡屏哦),当接受了用户连接后,
得到用户连接,开始实例化第二个类,我管第二个类叫做"MessageListener",第一个类叫做"PortListener",那么我的第二个类构造是这样写的(简单版本)

public MessageListener(Socket UserSocket)
{
}
这样一来,实例化的时候,传递当前得到的用户SOCKET,然后调用MessageListener的Start()方法就可以了。
MessageListener的Start()方法(简单版本)无非就是这样:

public void Start()
{
    Thraed thread 
= new Thread(new ThreadStart(this.INNER_LISTEN));
    thread.Start();
}
调用了内部的私有方法private void INNER_LISTENER()来进行While循环。
 
 
以上是简单版本的解决方案。下面提供了我的UML框架部分截图 (我的框架支持UDP和TCP 2种)
大家先看下,作为了解:
 
 
 
好了,截图了我的UML框架作为结尾,是再好不过了,新手作为了解,老鸟作为补习。
 
如果有错的地方 请勿见怪(以上代码实例,来自我的N年前接触网络时的处女作品)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值