302Soft—302软件技术联盟

ASP.NET / C# / PowerBuilder 技术交流
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

局域网QQ(转)

Posted on 2007-01-09 13:28  寒夜听雨  阅读(2686)  评论(4编辑  收藏  举报
局域网QQ,无客户端和服务端之分,局域网的计算机运行本程序就可以互相看见,可以自由聊天和传文件。

本版较之1.0版的改进之处:
     使用数据结构类型传送数据;
     增加传文件功能(有进度条);

考虑大家重现本程序方便,本程序一直没有用到任何额外的控件和子窗体
版本依然是在VS2003下编译,只要贴进编译器中就可以重现。
在VS2005下编译只需要在“窗体设计器生成的代码”里面加一句:
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.IO;

namespace myQQ
{
 
/// <summary>
 
/// Form1 的摘要说明。
 
/// </summary>

 public class Form1 : System.Windows.Forms.Form
 
{
  
public bool runing=false//标志
  public UdpClient listen=new UdpClient(2525);
  
public IPEndPoint End;
  
public IPAddress groupAddress=IPAddress.Parse("255.255.255.255"); //广播地址
  public int groupPort=2525;  //广播端口
  public IPAddress myIP;
  
public string myName;
  
public string romeName;
  
public IPAddress romeIP;
  
public string romeCon;
  
public sendData mySendData = new sendData(); //本地的0号命令发送包
  public bool isfile;//传送文件标志
  public string savePath;//传文件的保存地址
  public string romeFileName;//远程文件名
  public string romeFilel;//远程文件用单位表示的长度,格式为 43M或437K
  public FileStream sendfilestream;//发送文件流
  
  
private System.Windows.Forms.ListBox box;
  
private System.Windows.Forms.Label label1;
  
private System.Windows.Forms.Label label2;
  
private System.Windows.Forms.Button button1;
  
private System.Windows.Forms.Button button3;
  
private System.Windows.Forms.TextBox t_rec;
  
private System.Windows.Forms.TextBox t_send;
  
private System.Windows.Forms.Button button2;
  
private System.Windows.Forms.Button newmsg;
  
private System.Windows.Forms.Label online;
  
private System.Windows.Forms.ToolTip toolTip1;
  
private System.Windows.Forms.ImageList imageList1;
  
private System.Windows.Forms.LinkLabel linkLabel1;
  
private System.Windows.Forms.Label label3;
  
private System.Windows.Forms.Button button4;
  
private System.Windows.Forms.OpenFileDialog openFileDialog1;
  
private System.Windows.Forms.TextBox t_filep;
  
private System.Windows.Forms.SaveFileDialog saveFileDialog1;
  
private System.Windows.Forms.Panel processA;
  
private System.Windows.Forms.ProgressBar progressBar1;
  
private System.Windows.Forms.Panel processB;
  
private System.Windows.Forms.ProgressBar progressBar2;
  
private System.ComponentModel.IContainer components;

  
public Form1()
  
{
   
//
   
// Windows 窗体设计器支持所必需的
   
//
   InitializeComponent();

   
//
   
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
   
//
  }


  
/// <summary>
  
/// 清理所有正在使用的资源。
  
/// </summary>

  protected override void Dispose( bool disposing )
  
{
   
if( disposing )
   
{
    
if (components != null
    
{
     components.Dispose();
    }

   }

   
base.Dispose( disposing );
  }


  
Windows 窗体设计器生成的代码

  
/// <summary>
  
/// 应用程序的主入口点。
  
/// </summary>

  [STAThread]
  
static void Main() 
  
{
   Application.Run(
new Form1());
  }

  
//数据包格式
  public struct sendData
  
{
   
public byte commandNo;//命令号 1字节
   public IPAddress fromIP;//发送端IP 4字节
   public int nameLength;  //名字的字节数  4字节
   public string computerName;//计算机名  x字节
   public IPAddress toIP;//接收端IP; 4字节
   public string content;//内容  y字节
  }

  
private void Form1_Load(object sender, System.EventArgs e)
  

            
   End
=new IPEndPoint(groupAddress,groupPort);
   
//初始化计算机名和端口
   IPHostEntry myentry=Dns.GetHostByName(Dns.GetHostName());
   myIP 
= new IPAddress(myentry.AddressList[0].Address);
   myName 
= Dns.GetHostName();

   box.Items.Add(
"IP            主机名");

   
//开启监听线程
   runing=true;
   Thread myThread
=new Thread(new ThreadStart(this.ListenPort));
   myThread.IsBackground 
= false;
   myThread.Start();

   
//发送上线信息
   mySendData.commandNo = 0;
   mySendData.fromIP 
= myIP;
   mySendData.computerName 
= myName;
   mySendData.toIP 
= myIP;
   mySendData.content 
= "上线提示";
   
byte[] sendb = StructToBytes(mySendData);
   SendPack(sendb);

   
//初始化窗体位置
   t_send.Height=120;
   t_send.Top
=224;
   linkLabel1.Text
="传文件";
            processA.Visible
=false;//发送的进度
   processB.Visible=false;//接收的进度
  }


//侦听指定端口的广播地址UDP包
  public void ListenPort()
  

   IPEndPoint tempEnd
=new IPEndPoint(IPAddress.Any,2525);
   
while(runing)
   
{
    Application.DoEvents();
    
try
    
{
     
byte[] recb=listen.Receive(ref tempEnd);

     
// 检查所接收到的信息and处理之
     checkMessage(recb);
    }

    
catch(Exception e)
    
{
     MessageBox.Show(
"出现错误:"+e.Message.ToString());
     
break;
    }

    
   }

   listen.Close();
   box.Items.Add(
"线程已经退出!");
   runing
=false;
  }


//循环接收包
  public void checkMessage(byte[] recbb)
  
{
   sendData recData
=BytesToStruct(recbb);
   romeName
=recData.computerName;
   romeIP
=recData.fromIP;
   romeCon
=recData.content;
   
switch(recData.commandNo)
   
{
    
case 0x00//刷新
     if (recData.toIP.Equals(myIP))
     
{
      
if(box.FindString(recData.fromIP.ToString()+"  "+recData.computerName)<=0)
       box.Items.Add(recData.fromIP.ToString() 
+ "  " + recData.computerName);
      online.Text
="在线用户:"+(box.Items.Count-1)+"";
     }

     
else if(recData.fromIP.Equals(recData.toIP))
     
{
      
//从其他机器发送过来的刷新请求
      
//返回自己的信息
      mySendData.commandNo = 0x00;
      mySendData.toIP 
= recData.toIP;
      mySendData.content 
= "上线提示";
      
byte[] sendb = StructToBytes(mySendData);
      SendPack(sendb);
      
//如果不存在则添加该用户
      if (box.FindString(recData.fromIP.ToString() + "  " + recData.computerName) <= 0)
       box.Items.Add(recData.fromIP.ToString() 
+ "  " + recData.computerName);
     }

     
break;
    
case 0x01//发言
     if (recData.toIP.Equals(myIP)&&!isfile)
     
{//当传送的不包含文件时才显示“有新消息了”
      showNe();
     }

     
break;
    
case 0x02//请求传文件
     if(recData.toIP.Equals(myIP))
     
{//准备接收文件
      romeFileName=romeCon.Split('+')[0];
      romeFilel
=romeCon.Split('+')[1];
                        readyRecFile();
     }

     
break
    
case 0x03://传文件
     if(recData.toIP.Equals(myIP))
     
{//传送文件
                        Thread mysend=new Thread(new ThreadStart(sendFile));
      mysend.Start();
                    }

     
break;
    
case 0x09//确认包
     if (recData.toIP.Equals(myIP))
      MessageBox.Show(
"信息来自:"+recData.computerName+"("+recData.fromIP.ToString()+")\r\n"+recData.content,"消息");
     
break;
   }

   
  }

       
//发送数据包到广播地址
  public void SendPack(byte[] sendbs)
  
{
   
try
   
{
    listen.Send(sendbs, sendbs.Length, End);
   }

   
catch(Exception e)
   
{
    MessageBox.Show(e.Message.ToString());
   }

  }

//struct转换成byte[]
  public static byte[] StructToBytes(sendData structObj)
  
{
   
byte[] commandb=new byte[1];
   commandb[
0]=structObj.commandNo;
   
byte[] fromipb=structObj.fromIP.GetAddressBytes();
   
byte[] nameb=Encoding.Default.GetBytes(structObj.computerName);
   
byte[] lengthb=BitConverter.GetBytes(nameb.Length);
   
byte[] toipb=structObj.toIP.GetAddressBytes();
   
byte[] contentb=Encoding.Default.GetBytes(structObj.content);
   
byte[] buffer=new byte[13+nameb.Length+contentb.Length];
   
int index=0;
   commandb.CopyTo(buffer,index);
   index
+=commandb.Length;
   fromipb.CopyTo(buffer,index);
   index
+=fromipb.Length;
   lengthb.CopyTo(buffer,index);
   index
+=lengthb.Length;
   nameb.CopyTo(buffer,index);
   index
+=nameb.Length;
   toipb.CopyTo(buffer,index);
   index
+=toipb.Length;
   contentb.CopyTo(buffer,index);
   
return buffer;
  }

//byte转换成struct
  public static sendData BytesToStruct(byte[] bytes)
  
{
   sendData myre
=new sendData();
   myre.commandNo
=bytes[0];
   
byte[] ipb=new byte[4];
   Array.Copy(bytes,
1,ipb,0,4);
   myre.fromIP
=IPAddress.Parse(ipByteToString(ipb));
   myre.nameLength
=BitConverter.ToInt32(bytes,5);
   myre.computerName
=Encoding.Default.GetString(bytes,9,myre.nameLength);
   Array.Copy(bytes,
9+myre.nameLength,ipb,0,4);
   myre.toIP
=IPAddress.Parse(ipByteToString(ipb));
   myre.content
=Encoding.Default.GetString(bytes,13+myre.nameLength,bytes.Length-13-myre.nameLength);
   
return myre;
  }
 
//将byte[]表示的IP地址转换成IPAddress类型
  public static string ipByteToString(byte[] ipbt)
  
{
   
string temp="";
   temp
=(int)ipbt[0]+"."+(int)ipbt[1]+"."+(int)ipbt[2]+"."+(int)ipbt[3];
   
return temp;
  }

//刷新
  private void button3_Click(object sender, System.EventArgs e)
  
{
   box.Items.Clear();
   box.Items.Add(
"IP            主机名");
   mySendData.commandNo 
= 0;
   mySendData.fromIP 
= myIP;
   mySendData.computerName 
= myName;
   mySendData.toIP 
= myIP;
   mySendData.content 
= "上线提示";
   
byte[] sendb = StructToBytes(mySendData);
   SendPack(sendb);
  }


//关闭循环
  private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
  
{
   runing
=false;
   UdpClient mm
=new UdpClient();
   IPEndPoint tempIPEnd
=new IPEndPoint(IPAddress.Parse("127.0.0.1"),2525);
   mySendData.commandNo 
= 0;
   mySendData.fromIP 
= myIP;
   mySendData.computerName 
= myName;
   mySendData.toIP 
= myIP;
   mySendData.content 
= "上线提示";
   
byte[] sendb = StructToBytes(mySendData);
   mm.Send(sendb,sendb.Length,tempIPEnd);
  }

//发送
  private void button1_Click(object sender, System.EventArgs e)
  
{
   
if(box.SelectedItem==null||box.SelectedIndex==0)
   
{
    MessageBox.Show(
"请先选择一个用户!");
    
return;
   }

   
if(linkLabel1.Text=="不传文件"&&t_filep.Text!="")
   
{//需要传文件
    
//首先发送2号命令 要求对方准备接收
    sendfilestream=new FileStream(t_filep.Text,FileMode.Open,FileAccess.Read);
    
long filelength=sendfilestream.Length;
    
string totalsteps;
    
int temp;
    
if(filelength/(1024*1024)>10)
    
{//文件>10M时用M做为进度最小单位
     temp=(int)(filelength/(1024*1024));
     totalsteps
=temp+"M";
    }

    
else
    
{//小于10M的文件用k做为最小单位
     temp=(int)(filelength/1024);
     totalsteps
=temp+"K";
    }

    
//用做接收端时的公共变量保存发送的临时值
    romeFilel=totalsteps;
    
string filename=Path.GetFileName(t_filep.Text);
    mySendData.commandNo 
= 0x02;
    mySendData.fromIP 
= myIP;
    mySendData.computerName 
= myName;
    mySendData.toIP 
= IPAddress.Parse(box.SelectedItem.ToString().Split(' ')[0]);
    mySendData.content 
= filename+"+"+totalsteps;//格式:yy.txt+62K+64990
    byte[] sendb = StructToBytes(mySendData);
    SendPack(sendb);
   
    
//界面上的处理
    t_send.Height=120;
    t_send.Top
=224;
    linkLabel1.Text
="传文件";
   }

   
//发言
   if(t_send.Text=="")
   
{
    
return;
   }

   mySendData.commandNo 
= 0x01;
   mySendData.fromIP 
= myIP;
   mySendData.computerName 
= myName;
   mySendData.toIP 
= IPAddress.Parse(box.SelectedItem.ToString().Split(' ')[0]);
   mySendData.content 
= t_send.Text;
   
byte[] sendb2 = StructToBytes(mySendData);
   SendPack(sendb2);
  }

//清空
  private void button2_Click(object sender, System.EventArgs e)
  
{
   t_send.Text
=string.Empty;
   t_rec.Text
=string.Empty;
  }

//单击显示接收的信息并隐藏自身
  private void newmsg_Click(object sender, System.EventArgs e)
  
{
   
if(isfile)
   
{//当传送的含有文件时
    saveFileDialog1.Filter="所有格式|*.*";
    saveFileDialog1.FileName
=romeFileName;
    
if(saveFileDialog1.ShowDialog()==DialogResult.OK)
     savePath
=saveFileDialog1.FileName;
    
else
     
return;
    t_rec.Text
="消息来自"+romeName+"("+romeIP.ToString()+")"+DateTime.Now.ToShortDateString();
    t_rec.Text
+="\r\n> "+romeCon;
    
//界面的处理
    newmsg.SendToBack();
    t_send.Height
=120;
    t_send.Top
=224;
    linkLabel1.Text
="传文件";
    
//返回一个确认接收包
    mySendData.commandNo = 0x03;
    mySendData.fromIP 
= myIP;
    mySendData.computerName 
= myName;
    mySendData.toIP 
= romeIP;
    mySendData.content 
= "ready!";
    
byte[] sendb = StructToBytes(mySendData);
    SendPack(sendb);
    
//复位文件标志
    isfile=false;
    
//同时开启线程开始监听
    Thread myrecv=new Thread(new ThreadStart(listenPort));
    myrecv.Start();
    
return;
   }

   t_rec.Text
="消息来自"+romeName+"("+romeIP.ToString()+")"+DateTime.Now.ToShortDateString();
   t_rec.Text
+="\r\n> "+romeCon;
   mySendData.commandNo 
= 0x09;
   mySendData.toIP 
= romeIP;
   mySendData.content 
= "信息被打开!";
   
byte[] sendb2 = StructToBytes(mySendData);
   SendPack(sendb2);
   newmsg.SendToBack();
  }

//当有信息时显示提示按钮
  public void showNe()
  
{
   
//窗体显示
   if(this.WindowState!=FormWindowState.Normal)
   
{
    
this.Visible=true;
    
this.WindowState=FormWindowState.Normal;
   }

   newmsg.Text
="有新消息了";
   newmsg.BringToFront();
  }

//准备接收文件
  public void readyRecFile()
  
{
   
//窗体显示
   if(this.WindowState!=FormWindowState.Normal)
   
{
    
this.Visible=true;
    
this.WindowState=FormWindowState.Normal;
   }

   isfile
=true;
   newmsg.Text
=romeFileName+"("+romeFilel+")";
   newmsg.BringToFront();
  }

//传文件
  private void linkLabel1_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
  
{
   
if(linkLabel1.Text=="传文件")
   
{
    t_send.Height
=80;
    t_send.Top
=264;
    linkLabel1.Text
="不传文件";
   }

   
else
   
{
    t_send.Height
=120;
    t_send.Top
=224;
    linkLabel1.Text
="传文件";
   }

  }

//打开文件
  private void button4_Click(object sender, System.EventArgs e)
  
{
   openFileDialog1.Filter
="所有文件|*.*|压缩文件|*.rar";
   
if(openFileDialog1.ShowDialog()==DialogResult.OK)
   
{
    t_filep.Text
=openFileDialog1.FileName;
   }

  }


//监听文件传送socket的线程——————功能:接收文件
  public void listenPort()
  
{
   TcpListener listener
=new TcpListener(2626);
   listener.Start();
   Socket s
=listener.AcceptSocket();
   FileStream filestream
=new FileStream(savePath,FileMode.OpenOrCreate,FileAccess.Write);
      NetworkStream stream
=new NetworkStream(s);
   
//定义缓冲区
   byte[] bb=new byte[1024]; 
   
//循环读socket流
   int tt=0;
   
//进度条
   processB.Visible=true;
   processB.BackColor
=System.Drawing.SystemColors.Control;
   
int stepoff;
   
if(romeFilel[romeFilel.Length-1]=='K')
    stepoff
=1024;
   
else
    stepoff
=1024*1024;
   
int totalste=int.Parse(romeFilel.Substring(0,romeFilel.Length-1));
   progressBar2.Maximum
=totalste;
   progressBar2.Step
=totalste/20;
   
float recbytes=0;
   
while((tt=stream.Read(bb,0,1024))!=0)
   
{//接收数据
    filestream.Write(bb,0,tt);
    filestream.Flush(); 
    
//更新进度条
    recbytes+=(float)tt/stepoff;
    progressBar2.Value
=(int)recbytes;
   }

   filestream.Close();
   processB.Visible
=false;
   MessageBox.Show(
"文件接收完毕!");
  }

//传送文件的线程————————功能:发送文件服务端
  public void sendFile()
  
{
   TcpClient serverClient
=new TcpClient();
   serverClient.Connect(romeIP,
2626);
   NetworkStream stream
=serverClient.GetStream();
   
//定义缓冲区
   byte[] bb=new byte[1024]; 
   
//循环读文件
   int number;
   
//进度条
   processA.Visible=true;
   processA.BackColor
=System.Drawing.SystemColors.Window;
   
int stepoff;
   
if(romeFilel[romeFilel.Length-1]=='K')
    stepoff
=1024;
   
else
    stepoff
=1024*1024;
   
int totalste=int.Parse(romeFilel.Substring(0,romeFilel.Length-1));
   progressBar1.Maximum
=totalste;
            progressBar1.Step
=totalste/20;
   
float sendedbytes=0;
   
while((number=sendfilestream.Read(bb,0,1024))!=0)
   
{//向客户端发送流
    stream.Write(bb,0,number);
    
//刷新流
    stream.Flush(); 
    
//进度条
    sendedbytes+=(float)number/stepoff;
    progressBar1.Value
=(int)sendedbytes;
   }

   sendfilestream.Close();
   stream.Close();
   processA.Visible
=false;
   MessageBox.Show(
"文件传送完毕!");
  }

 }

}


Copyright(C) 2004-2007 302Soft-寒夜听雨 版权所有