金鳞化龙 - 老白


智者创造机会,强者把握机会,弱者等待机会~
posts - 27, comments - 7, trackbacks - 0, articles - 12
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

2009年10月18日

因为特殊的原因,自己托管的服务器用不了了,只好又回到这里来写写日记了

两年过去了,生活好像没有太大的变化,最明显的变化是从未婚变成已婚,而精神状态好像也变得不太好了,工作不如以前专心。

不知道能不能痛改前非~,嘎嘎~

posted @ 2009-10-18 09:59 秋衫客 阅读(15) 评论(0) 编辑

2007年9月25日

虽然这公司不怎么样,可是,毕竟在这里呆了近三年了。
好像有点不舍得,可是没办法,毕竟要走的,还有许多其它的事等着做,不能在这里浪费时间呀。
今天把最后一点程序写完,明天,就把所有的该签的字都签了,然后。。。

posted @ 2007-09-25 11:25 秋衫客 阅读(56) 评论(0) 编辑

2006年11月10日

Microsoft.Net Framework为应用程序访问Internet提供了分层的、可扩展的以及受管辖的网络服务,其名字空间System.Net和System.Net.Sockets包含丰富的类可以开发多种网络应用程序。.Net类采用的分层结构允许应用程序在不同的控制级别上访问网络,开发人员可以根据需要选择针对不同的级别编制程序,这些级别几乎囊括了Internet的所有需要--从socket套接字到普通的请求/响应,更重要的是,这种分层是可以扩展的,能够适应Internet不断扩展的需要。

抛开ISO/OSI模型的7层构架,单从TCP/IP模型上的逻辑层面上看,.Net类可以视为包含3个层次:请求/响应层、应用协议层、传输层。WebReqeust和WebResponse 代表了请求/响应层,支持Http、Tcp和Udp的类组成了应用协议层,而Socket类处于传输层。可以如下示意:


可见,传输层位于这个结构的最底层,当其上面的应用协议层和请求/响应层不能满足应用程序的特殊需要时,就需要使用这一层进行Socket套接字编程。

而在.Net中,System.Net.Sockets 命名空间为需要严密控制网络访问的开发人员提供了 Windows Sockets (Winsock) 接口的托管实现。System.Net 命名空间中的所有其他网络访问类都建立在该套接字Socket实现之上,如TCPClient、TCPListener 和 UDPClient 类封装有关创建到 Internet 的 TCP 和 UDP 连接的详细信息;NetworkStream类则提供用于网络访问的基础数据流等,常见的许多Internet服务都可以见到Socket的踪影,如Telnet、Http、Email、Echo等,这些服务尽管通讯协议Protocol的定义不同,但是其基础的传输都是采用的Socket。

其实,Socket可以象流Stream一样被视为一个数据通道,这个通道架设在应用程序端(客户端)和远程服务器端之间,而后,数据的读取(接收)和写入(发送)均针对这个通道来进行。

可见,在应用程序端或者服务器端创建了Socket对象之后,就可以使用Send/SentTo方法将数据发送到连接的Socket,或者使用Receive/ReceiveFrom方法接收来自连接Socket的数据;

针对Socket编程,.NET 框架的 Socket 类是 Winsock32 API 提供的套接字服务的托管代码版本。其中为实现网络编程提供了大量的方法,大多数情况下,Socket 类方法只是将数据封送到它们的本机 Win32 副本中并处理任何必要的安全检查。如果你熟悉Winsock API函数,那么用Socket类编写网络程序会非常容易,当然,如果你不曾接触过,也不会太困难,跟随下面的解说,你会发觉使用Socket类开发windows 网络应用程序原来有规可寻,它们在大多数情况下遵循大致相同的步骤。

在使用之前,你需要首先创建Socket对象的实例,这可以通过Socket类的构造方法来实现:

public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType);


其中,addressFamily 参数指定 Socket 使用的寻址方案,socketType 参数指定 Socket 的类型,protocolType 参数指定 Socket 使用的协议。

下面的示例语句创建一个 Socket,它可用于在基于 TCP/IP 的网络(如 Internet)上通讯。

Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);


若要使用 UDP 而不是 TCP,需要更改协议类型,如下面的示例所示:

Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);


一旦创建 Socket,在客户端,你将可以通过Connect方法连接到指定的服务器,并通过Send/SendTo方法向远程服务器发送数据,而后可以通过Receive/ReceiveFrom从服务端接收数据;而在服务器端,你需要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联,并通过Listen方法侦听该接口上的请求,当侦听到用户端的连接时,调用Accept完成连接的操作,创建新的Socket以处理传入的连接请求。使用完 Socket 后,记住使用 Shutdown 方法禁用 Socket,并使用 Close 方法关闭 Socket。其间用到的方法/函数有:

Socket.Connect方法:建立到远程设备的连接
public void Connect(EndPoint remoteEP)(有重载方法)
Socket.Send 方法:从数据中的指示位置开始将数据发送到连接的 Socket。
public int Send(byte[], int, SocketFlags);(有重载方法)
Socket.SendTo 方法 将数据发送到特定终结点。
public int SendTo(byte[], EndPoint);(有重载方法)
Socket.Receive方法:将数据从连接的 Socket 接收到接收缓冲区的特定位置。
public int Receive(byte[],int,SocketFlags);
Socket.ReceiveFrom方法:接收数据缓冲区中特定位置的数据并存储终结点。
public int ReceiveFrom(byte[], int, SocketFlags, ref EndPoint);
Socket.Bind 方法:使 Socket 与一个本地终结点相关联:
public void Bind( EndPoint localEP );
Socket.Listen方法:将 Socket 置于侦听状态。
public void Listen( int backlog );
Socket.Accept方法:创建新的 Socket 以处理传入的连接请求。
public Socket Accept();
Socket.Shutdown方法:禁用某 Socket 上的发送和接收
public void Shutdown( SocketShutdown how );
Socket.Close方法:强制 Socket 连接关闭
public void Close();


可以看出,以上许多方法包含EndPoint类型的参数,在Internet中,TCP/IP 使用一个网络地址和一个服务端口号来唯一标识设备。网络地址标识网络上的特定设备;端口号标识要连接到的该设备上的特定服务。网络地址和服务端口的组合称为终结点,在 .NET 框架中正是由 EndPoint 类表示这个终结点,它提供表示网络资源或服务的抽象,用以标志网络地址等信息。.Net同时也为每个受支持的地址族定义了 EndPoint 的子代;对于 IP 地址族,该类为 IPEndPoint。IPEndPoint 类包含应用程序连接到主机上的服务所需的主机和端口信息,通过组合服务的主机IP地址和端口号,IPEndPoint 类形成到服务的连接点。

用到IPEndPoint类的时候就不可避免地涉及到计算机IP地址,.Net中有两种类可以得到IP地址实例:

IPAddress类:IPAddress 类包含计算机在 IP 网络上的地址。其Parse方法可将 IP 地址字符串转换为 IPAddress 实例。下面的语句创建一个 IPAddress 实例:

IPAddress myIP = IPAddress.Parse("192.168.1.2");


Dns 类:向使用 TCP/IP Internet 服务的应用程序提供域名服务。其Resolve 方法查询 DNS 服务器以将用户友好的域名(如"host.contoso.com")映射到数字形式的 Internet 地址(如 192.168.1.1)。Resolve方法 返回一个 IPHostEnty 实例,该实例包含所请求名称的地址和别名的列表。大多数情况下,可以使用 AddressList 数组中返回的第一个地址。下面的代码获取一个 IPAddress 实例,该实例包含服务器 host.contoso.com 的 IP 地址。

IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];


你也可以使用GetHostName方法得到IPHostEntry实例:

IPHosntEntry hostInfo=Dns.GetHostByName("host.contoso.com")


在使用以上方法时,你将可能需要处理以下几种异常:

SocketException异常:访问Socket时操作系统发生错误引发

ArgumentNullException异常:参数为空引用引发

ObjectDisposedException异常:Socket已经关闭引发

在掌握上面得知识后,下面的代码将该服务器主机( host.contoso.com的 IP 地址与端口号组合,以便为连接创建远程终结点:

IPEndPoint ipe = new IPEndPoint(ipAddress,11000);


确定了远程设备的地址并选择了用于连接的端口后,应用程序可以尝试建立与远程设备的连接。下面的示例使用现有的 IPEndPoint 实例与远程设备连接,并捕获可能引发的异常:

try {
s.Connect(ipe);//尝试连接
}
//处理参数为空引用异常
catch(ArgumentNullException ae) {
Console.WriteLine("ArgumentNullException : {0}", ae.ToString());
}
//处理操作系统异常
catch(SocketException se) {
Console.WriteLine("SocketException : {0}", se.ToString());
}
catch(Exception e) {
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}


需要知道的是:Socket 类支持两种基本模式:同步和异步。其区别在于:在同步模式中,对执行网络操作的函数(如 Send 和 Receive)的调用一直等到操作完成后才将控制返回给调用程序。在异步模式中,这些调用立即返回。

另外,很多时候,Socket编程视情况不同需要在客户端和服务器端分别予以实现,在客户端编制应用程序向服务端指定端口发送请求,同时编制服务端应用程序处理该请求,这个过程在上面的阐述中已经提及;当然,并非所有的Socket编程都需要你严格编写这两端程序;视应用情况不同,你可以在客户端构造出请求字符串,服务器相应端口捕获这个请求,交由其公用服务程序进行处理。以下事例语句中的字符串就向远程主机提出页面请求:

string Get = "GET / HTTP/1.1\r\nHost: " + server + "\r\nConnection: Close\r\n\r\n";


远程主机指定端口接受到这一请求后,就可利用其公用服务程序进行处理而不需要另行编制服务器端应用程序。

综合运用以上阐述的使用Visual C#进行Socket网络程序开发的知识,下面的程序段完整地实现了Web页面下载功能。用户只需在窗体上输入远程主机名(Dns 主机名或以点分隔的四部分表示法格式的 IP 地址)和预保存的本地文件名,并利用专门提供Http服务的80端口,就可以获取远程主机页面并保存在本地机指定文件中。如果保存格式是.htm格式,你就可以在Internet浏览器中打开该页面。适当添加代码,你甚至可以实现一个简单的浏览器程序。


实现此功能的主要源代码如下:

//"开始"按钮事件
private void button1_Click(object sender, System.EventArgs e) {
//取得预保存的文件名
string fileName=textBox3.Text.Trim();
//远程主机
string hostName=textBox1.Text.Trim();
//端口
int port=Int32.Parse(textBox2.Text.Trim());
//得到主机信息
IPHostEntry ipInfo=Dns.GetHostByName(hostName);
//取得IPAddress[]
IPAddress[] ipAddr=ipInfo.AddressList;
//得到ip
IPAddress ip=ipAddr[0];
//组合出远程终结点
IPEndPoint hostEP=new IPEndPoint(ip,port);
//创建Socket 实例
Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
try
{
//尝试连接
socket.Connect(hostEP);
}
catch(Exception se)
{
MessageBox.Show("连接错误"+se.Message,"提示信息
,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//发送给远程主机的请求内容串
string sendStr="GET / HTTP/1.1\r\nHost: " + hostName +
"\r\nConnection: Close\r\n\r\n";
//创建bytes字节数组以转换发送串
byte[] bytesSendStr=new byte[1024];
//将发送内容字符串转换成字节byte数组
bytesSendStr=Encoding.ASCII.GetBytes(sendStr);
try
{
//向主机发送请求
socket.Send(bytesSendStr,bytesSendStr.Length,0);
}
catch(Exception ce)
{
MessageBox.Show("发送错误:"+ce.Message,"提示信息
,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//声明接收返回内容的字符串
string recvStr="";
//声明字节数组,一次接收数据的长度为1024字节
byte[] recvBytes=new byte[1024];
//返回实际接收内容的字节数
int bytes=0;
//循环读取,直到接收完所有数据
while(true)
{
bytes=socket.Receive(recvBytes,recvBytes.Length,0);
//读取完成后退出循环
if(bytes<=0)
break;
//将读取的字节数转换为字符串
recvStr+=Encoding.ASCII.GetString(recvBytes,0,bytes);
}
//将所读取的字符串转换为字节数组
byte[] content=Encoding.ASCII.GetBytes(recvStr);
try
{
//创建文件流对象实例
FileStream fs=new FileStream(fileName,FileMode.OpenOrCreate,FileAccess.ReadWrite);
//写入文件
fs.Write(content,0,content.Length);
}
catch(Exception fe)
{
MessageBox.Show("文件创建/写入错误:"+fe.Message,"提示信息",MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//禁用Socket
socket.Shutdown(SocketShutdown.Both);
//关闭Socket
socket.Close();
}
}


程序在WindowsXP中文版、.Net Frameworkd 中文正式版、Visual Studio.Net中文正式版下调试通过

posted @ 2006-11-10 13:54 秋衫客 阅读(102) 评论(0) 编辑

谈起socket编程,大家也许会想起QQ和IE,没错。还有许多网络工具如P2P、NetMeeting等在应用层实现的应用程序,也是用socket来实现的。Socket是一个网络编程接口,实现于网络应用层,Windows Socket包括了一套系统组件,充分利用了Microsoft Windows 消息驱动的特点。Socket规范1.1版是在1993年1月发行的,并广泛用于此后出现的Windows9x操作系统中。Socket规范2.2版(其在Windows平台上的版本是Winsock2.2,也叫Winsock2)在 1996 年 5 月发行,Windows NT 5.0及以后版本的Windows系统支持Winsock2,在Winsock2中,支持多个传输协议的原始套接字,重叠I/O模型、服务质量控制等。

本文向大家介绍Windows Sockets的一些关于用C#实现的原始套接字(Raw Socket)的编程,以及在此基础上实现的网络封包监视技术。同Winsock1相比,Winsock2最明显的就是支持了Raw Socket套接字类型,使用Raw Socket,可把网卡设置成混杂模式,在这种模式下,我们可以收到网络上的IP包,当然包括目的不是本机的IP包,通过原始套接字,我们也可以更加自如地控制Windows下的多种协议,而且能够对网络底层的传输机制进行控制。

在本文例子中,我在nbyte.BasicClass命名空间实现了RawSocket类,它包含了我们实现数据包监视的核心技术。在实现这个类之前,需要先写一个IP头结构,来暂时存放一些有关网络封包的信息:


[StructLayout(LayoutKind.Explicit)]
 public struct IPHeader
 {
  [FieldOffset(0)] public byte    ip_verlen;        //I4位首部长度+4位IP版本号
  [FieldOffset(1)] public byte    ip_tos;            //8位服务类型TOS
  [FieldOffset(2)] public ushort  ip_totallength; //16位数据包总长度(字节)
  [FieldOffset(4)] public ushort  ip_id;             //16位标识
  [FieldOffset(6)] public ushort  ip_offset;       //3位标志位
  [FieldOffset(8)] public byte    ip_ttl;            //8位生存时间 TTL
  [FieldOffset(9)] public byte    ip_protocol;    //8位协议(TCP, UDP, ICMP, Etc.)
  [FieldOffset(10)] public ushort ip_checksum; //16位IP首部校验和
  [FieldOffset(12)] public uint   ip_srcaddr;     //32位源IP地址
  [FieldOffset(16)] public uint   ip_destaddr;   //32位目的IP地址
 }

这样,当每一个封包到达时候,可以用强制类型转化把包中的数据流转化为一个个IPHeader对象。
下面就开始写RawSocket类了,一开始,先定义几个参数,包括:
  private bool error_occurred;          //套接字在接收包时是否产生错误
  public bool KeepRunning;              //是否继续进行
  private static int len_receive_buf; //得到的数据流的长度
  byte [] receive_buf_bytes;          //收到的字节
  private Socket socket = null;       //声明套接字
还有一个常量:
const int SIO_RCVALL = unchecked((int)0x98000001);//监听所有的数据包

这里的SIO_RCVALL是指示RawSocket接收所有的数据包,在以后的IOContrl函数中要用,在下面的构造函数中,实现了对一些变量参数的初始化:

  public RawSocket()                    //构造函数
  {
   error_occurred=false;
   len_receive_buf = 4096;
   receive_buf_bytes = new byte[len_receive_buf];
  }

下面的函数实现了创建RawSocket,并把它与终结点(IPEndPoint:本机IP和端口)绑定:
  public void CreateAndBindSocket(string IP)                  //建立并绑定套接字
  {
   socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
   socket.Blocking = false;                                         //置socket非阻塞状态
   socket.Bind(new IPEndPoint(IPAddress.Parse(IP), 0)); //绑定套接字

   if (SetSocketOption()==false) error_occurred=true;
  }
其中,在创建套接字的一句socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);中有3个参数:

第一个参数是设定地址族,MSDN上的描述是“指定 Socket 实例用来解析地址的寻址方案”,当要把套接字绑定到终结点(IPEndPoint)时,需要使用InterNetwork成员,即采用IP版本4的地址格式,这也是当今大多数套接字编程所采用一个寻址方案(AddressFamily)。

第二个参数设置的套接字类型就是我们使用的Raw类型了,SocketType是一个枚举数据类型,Raw套接字类型支持对基础传输协议的访问。通过使用 SocketType.Raw,你不光可以使用传输控制协议(Tcp)和用户数据报协议(Udp)进行通信,也可以使用网际消息控制协议 (Icmp) 和 Internet 组管理协议 (Igmp) 来进行通信。在发送时,您的应用程序必须提供完整的 IP 标头。所接收的数据报在返回时会保持其 IP 标头和选项不变。

第三个参数设置协议类型,Socket 类使用 ProtocolType 枚举数据类型向 Windows Socket API 通知所请求的协议。这里使用的是IP协议,所以要采用ProtocolType.IP参数。

在CreateAndBindSocket函数中有一个自定义的SetSocketOption函数,它和Socket类中的SetSocketOption不同,我们在这里定义的是具有IO控制功能的SetSocketOption,它的定义如下:

  private bool SetSocketOption()                           //设置raw socket
  {
   bool ret_value = true;
   try
   {
    socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1);
    byte []IN = new byte[4]{1, 0, 0, 0};
    byte []OUT = new byte[4];
 
    //低级别操作模式,接受所有的数据包,这一步是关键,必须把socket设成raw和IP Level才可用SIO_RCVALL
    int ret_code = socket.IOControl(SIO_RCVALL, IN, OUT);
    ret_code = OUT[0] + OUT[1] + OUT[2] + OUT[3];//把4个8位字节合成一个32位整数
    if(ret_code != 0) ret_value = false;
   }
   catch(SocketException)
   {
    ret_value = false;
   }
   return ret_value;
  }

其中,设置套接字选项时必须使套接字包含IP包头,否则无法填充IPHeader结构,也无法获得数据包信息。
int ret_code = socket.IOControl(SIO_RCVALL, IN, OUT);是函数中最关键的一步了,因为,在windows中我们不能用Receive函数来接收raw socket上的数据,这是因为,所有的IP包都是先递交给系统核心,然后再传输到用户程序,当发送一个raws socket包的时候(比如syn),核心并不知道,也没有这个数据被发送或者连接建立的记录,因此,当远端主机回应的时候,系统核心就把这些包都全部丢掉,从而到不了应用程序上。所以,就不能简单地使用接收函数来接收这些数据报。要达到接收数据的目的,就必须采用嗅探,接收所有通过的数据包,然后进行筛选,留下符合我们需要的。可以通过设置SIO_RCVALL,表示接收所有网络上的数据包。接下来介绍一下IOControl函数。MSDN解释它说是设置套接字为低级别操作模式,怎么低级别操作法?其实这个函数与API中的WSAIoctl函数很相似。WSAIoctl函数定义如下:

int WSAIoctl(
  SOCKET s,                                                                          //一个指定的套接字
  DWORD dwIoControlCode,                                                      //控制操作码
  LPVOID lpvInBuffer,                                                              //指向输入数据流的指针
  DWORD cbInBuffer,                                                              //输入数据流的大小(字节数)
  LPVOID lpvOutBuffer,                                                           // 指向输出数据流的指针
  DWORD cbOutBuffer,                                                           //输出数据流的大小(字节数)
  LPDWORD lpcbBytesReturned,                                               //指向输出字节流数目的实数值
  LPWSAOVERLAPPED lpOverlapped,                                         //指向一个WSAOVERLAPPED结构
  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine//指向操作完成时执行的例程
);

C#的IOControl函数不像WSAIoctl函数那么复杂,其中只包括其中的控制操作码、输入字节流、输出字节流三个参数,不过这三个参数已经足够了。我们看到函数中定义了一个字节数组:byte []IN = new byte[4]{1, 0, 0, 0}实际上它是一个值为1的DWORD或是Int32,同样byte []OUT = new byte[4];也是,它整和了一个int,作为WSAIoctl函数中参数lpcbBytesReturned指向的值。
因为设置套接字选项时可能会发生错误,需要用一个值传递错误标志:

  public bool ErrorOccurred
  {
   get
   {
    return error_occurred;
   }
  }

下面的函数实现的数据包的接收:

  //解析接收的数据包,形成PacketArrivedEventArgs事件数据类对象,并引发PacketArrival事件
  unsafe private void Receive(byte [] buf, int len)
  {
   byte temp_protocol=0;
   uint temp_version=0;
   uint temp_ip_srcaddr=0;
   uint temp_ip_destaddr=0;
   short temp_srcport=0;
   short temp_dstport=0;
   IPAddress temp_ip;
  
   PacketArrivedEventArgs e=new PacketArrivedEventArgs();//新网络数据包信息事件

   fixed(byte *fixed_buf = buf)
   {
    IPHeader * head = (IPHeader *) fixed_buf;//把数据流整和为IPHeader结构
    e.HeaderLength=(uint)(head->ip_verlen & 0x0F) << 2;
   
    temp_protocol = head->ip_protocol;
    switch(temp_protocol)//提取协议类型
    {
     case 1: e.Protocol="ICMP";     break;
     case 2: e.Protocol="IGMP";     break;
     case 6: e.Protocol="TCP";      break;
     case 17: e.Protocol="UDP";     break;
     default: e.Protocol= "UNKNOWN"; break;
    }

    temp_version =(uint)(head->ip_verlen & 0xF0) >> 4;//提取IP协议版本
    e.IPVersion = temp_version.ToString();

                   //以下语句提取出了PacketArrivedEventArgs对象中的其他参数
    temp_ip_srcaddr = head->ip_srcaddr;
    temp_ip_destaddr = head->ip_destaddr;
    temp_ip = new IPAddress(temp_ip_srcaddr);
    e.OriginationAddress =temp_ip.ToString();
    temp_ip = new IPAddress(temp_ip_destaddr);
    e.DestinationAddress = temp_ip.ToString();

    temp_srcport = *(short *)&fixed_buf[e.HeaderLength];
    temp_dstport = *(short *)&fixed_buf[e.HeaderLength+2];
    e.OriginationPort=IPAddress.NetworkToHostOrder(temp_srcport).ToString();
    e.DestinationPort=IPAddress.NetworkToHostOrder(temp_dstport).ToString();

    e.PacketLength =(uint)len;
    e.MessageLength =(uint)len - e.HeaderLength;

    e.ReceiveBuffer=buf;
    //把buf中的IP头赋给PacketArrivedEventArgs中的IPHeaderBuffer
    Array.Copy(buf,0,e.IPHeaderBuffer,0,(int)e.HeaderLength);
    //把buf中的包中内容赋给PacketArrivedEventArgs中的MessageBuffer
    Array.Copy(buf,(int)e.HeaderLength,e.MessageBuffer,0,(int)e.MessageLength);
   }
   //引发PacketArrival事件
   OnPacketArrival(e);
  }

大家注意到了,在上面的函数中,我们使用了指针这种所谓的不安全代码,可见在C#中指针和移位运算这些原始操作也可以给程序员带来编程上的便利。在函数中声明PacketArrivedEventArgs类对象,以便通过OnPacketArrival(e)函数通过事件把数据包信息传递出去。其中PacketArrivedEventArgs类是RawSocket类中的嵌套类,它继承了系统事件(Event)类,封装了数据包的IP、端口、协议等其他数据包头中包含的信息。在启动接收数据包的函数中,我们使用了异步操作的方法,以下函数开启了异步监听的接口:

public void Run() //开始监听
{
 IAsyncResult ar = socket.BeginReceive(receive_buf_bytes, 0, len_receive_buf, SocketFlags.None, new AsyncCallback(CallReceive), this);
}

Socket.BeginReceive函数返回了一个异步操作的接口,并在此接口的生成函数BeginReceive中声明了异步回调函数CallReceive,并把接收到的网络数据流传给receive_buf_bytes,这样就可用一个带有异步操作的接口参数的异步回调函数不断地接收数据包:

  private void CallReceive(IAsyncResult ar)//异步回调
  {
   int received_bytes;
   received_bytes = socket.EndReceive(ar);
   Receive(receive_buf_bytes, received_bytes);
   if (KeepRunning) Run();
  }

此函数当挂起或结束异步读取后去接收一个新的数据包,这样能保证让每一个数据包都能够被程序探测到。
下面通过声明代理事件句柄来实现和外界的通信:

public delegate void PacketArrivedEventHandler(Object sender, PacketArrivedEventArgs args);
//事件句柄:包到达时引发事件
public event PacketArrivedEventHandler PacketArrival;//声明时间句柄函数

这样就可以实现对数据包信息的获取,采用异步回调函数,可以提高接收数据包的效率,并通过代理事件把封包信息传递到外界。既然能把所有的封包信息传递出去,就可以实现对数据包的分析了:)不过RawSocket的任务还没有完,最后不要望了关闭套接字啊:

  public void Shutdown()                                       //关闭raw socket
  {
   if(socket != null)
   {
    socket.Shutdown(SocketShutdown.Both);
    socket.Close();
   }
  }

以上介绍了RawSocket类通过构造IP头获取了包中的信息,并通过异步回调函数实现了数据包的接收,并使用时间代理句柄和自定义的数据包信息事件类把数据包信息发送出去,从而实现了网络数据包的监视,这样我们就可以在外部添加一些函数对数据包进行分析了。

posted @ 2006-11-10 13:48 秋衫客 阅读(100) 评论(0) 编辑

2006年11月6日

转自:http://nonregister.spaces.live.com/blog/cns!13069547FB217839!294.entry
 
    使用debian已经很久了从最开始安装时的战战兢兢到现在安装成了家常便饭,bbs上已经有很多人写了安装方法,这里把参考了不少方法后,把整个流程记录下来希望能对其他人有所帮助。
  
 1. 安装基本系统
 
       我选择的是 Debian Srage 3.1 r2,可以在debian的官方网站(www.debian.com)上下载到,一共180多M,刻成一张cd。
 然后光盘启动进入安装画面,此时如果直接回车会安装2.4.2的内核,输入linux26再回车将安装2.6.8的内核。
 
 [选择语言]:
 建议选用英文,因为如果选择中文,后面的安装过程可能有一些看不见界面下的提示,当然后面也可以通过apt-get install zhcon来解决这个问题;
 
 [选择区域]:在other的asia里面可以找到china;
 
 [选择键盘布局]:默认即可;
 
 [网络配置]:
 安装程序检查硬件然后自动配置网卡并分配IP,也可以用手动配置的方式,之后的主机名字就由自己设置了,一般没有域名,该选项就可以跳过了。
 
 [分区]:
 如果选择手动分区,记住,至少要有两个分区,一个给debian系统,另一个为swap分区。为了以后安装应用程序,至少10g给debian还是不过分的。至于swap分区,这里在bbs上有很多讨论:现在物理内存都很大了,还有没有必要分给swap分区,如果感兴趣可以去看看。我的建议是还是至少给300--800给swap。
        而对于分区方案,如果你是新手那么还是将所有的文件放在一个分区里,也就是将整个分区挂载到根目录“/”下。如果你想将不同分区挂载到不同的文件夹下,那么可以去bbs上找找文件系统的介绍。
 最后再提醒一下,错误的分区可能造成数据的丢失,仔细检查后再确定更改,否则可以选择“NO”取消分区。
  
 [基本系统安装]:
 进行完上面的步骤,Debian就开始基本系统的安装,主要是内核和其他一些必要的程序和工具。之后,Debian会安装系统引导程序Grub,用来生成多系统的启动菜单。如果你还安装着其他的操作系统,比如Windows或者BSD,那么选择“Yes”将grub安装到主引导区。然后会提示退出光盘,之后重启。
 
 
    2. 基本系统配置

 
 重启之后应该能看到Grub的启动菜单,选择进入以后,不久就可以看见欢迎界面了。
 
[时区选择]:
 首先会提示是否采用GMT(格林威治时间),如果有多个操作系统,那还是选择“no”吧,否则你会发现xp的时间总是不对,设置正确了,重启以后又错了。时区选择选默认的就可以了。
 
 [用户密码设置]
首先是root,相当于计算机管理员;然后建立一个新用户,设置密码。这个用户只是普通权限的帐户。
 
 [网络安装]:
 此时Debian会让配置apt源,选择ftp还是http方式就看源的属性了。这里可以选择上面已有的源,也可以自己编辑。
 如果要自己编辑就进入其中一个选项,然后选择第一个手动编辑,我选择的是中科大的源(ftp://debian.ustc.edu.cn),安装包比较全,也比较快,然后输入安装包所在的目录(/debian)。如果不是教育网,那么也可以选择cn99.com的源。
 OK以后,Debian会询问安装类型,这里如果选择了第一个桌面应用,那么安装程序会连接到你所选的源上,然后将需要的包下载,然后安装。不过这样安装的将是sarge,以后如果想要用etch还要重新升级。而且会自动安装很多程序,比如会同时安装上Gnome和KDE。如果你才开始接触Debian,可以尝试这样安装,以后熟悉以后再开始手动安装。下面我将介绍的就是手动安装的过程。
 
 3.  升级Debian版本和内核
 
 在界面上按Esc,会退到Base-config程序的主界面,然后选择最后一个执行shell。
      
 [源配置]:
 首先将/etc/apt/sources.list的stable源更改为testing的源。如下所示:
 # deb ftp://debian.ustc.edu.cn/debian/ sarge main 
 # deb-src ftp://debian.ustc.edu.cn/debian/ sarge main  
 deb ftp://debian.ustc.edu.cn/debian/ testing main  
 deb-src ftp://debian.ustc.edu.cn/debian/ testing main
 
 [版本升级]:
 然后更新源,升级基本系统到testing,会提示要删除和安装一些包,Yes就可以了。
 #apt-get update
 #apt-get dist-upgrade
 
 [升级内核]:
 #apt-get install linux-image
 我直接用上面的方法得到现在的linux版本,找一个合适的。我的是AMD的闪龙,所以选择了linux-image-2.6.16-2-K7,如果你是新的Intel的CPU那么选择linux-image-2.6.16-2-i686应该没有问题。
 然后执行:
 #apt-get install linux-image-2.6.16-2-K7
 安装了新的内核。之后可以重启,看看Grub菜单中是否已经有了linux-2.6.16-2-K7的选项,如果有,那么新的内核就已经安装完成了。
 
    4.  安装桌面
 
 [安装Xserver]:
 我选择的是xorg,执行下面的语句,
 #apt-get  install xorg 
 一般的做法是:
 #apt-get install xserver-xorg-core xserver-xorg xorg,
 我仔细比较过,对于Debian来说,直接安装xorg,它会分析依赖关系,会同时安装前两个包。安装过程中,需要配置xorg,详细的配置方法可以到BBS上查到相应的文章。
 
 [安装gdm]:
 这是启动界面,如果没有,那么每次都会从console方式进入,然后输入startx才能进入Gnome,为了方便还是安装上吧。
 #apt-get install gdm gdm-themes
 
 [安装Gnome]:
 #apt-get install gnome
            其实只需要上面的指令就可以安装Gnome了,但是为了以后安装其他应用程序的方便,可以把Gnome的开发包安装上,于是我采用了下面的方式:
  #apt-get install  gnome gnome-core gnome-devel gnome-core-devel
            这样,完整的桌面系统就完成了。进入Gnome,是不是发现分辨率是800*600的,刷新率也只能达到65。虽然BBS上有很多通过更改相应的xorg配置可以解决方案,其实安装了相应的显卡驱动以后,就可以实现分辨率和刷新率的调节了。

posted @ 2006-11-06 10:41 秋衫客 阅读(1905) 评论(0) 编辑

2006年4月10日

摘要: 6.3.Configuration variables6.3.1.abort_nosubjectType:quadoptionDefault:ask-yesIf set to yes, when composing messages and no subject is given at the subject prompt, composition will be aborted. If set ...阅读全文

posted @ 2006-04-10 16:22 秋衫客 阅读(210) 评论(0) 编辑

2006年4月5日

摘要: 1个窝窝头,2天了,3了,4孬了,5了,66,7晾7晾,88皮,99毛,10在不行,愣了...阅读全文

posted @ 2006-04-05 14:37 秋衫客 阅读(98) 评论(0) 编辑

2006年3月24日

摘要: 1.安装前先看一下服务器上有没有编译软件,没有的话先装一个gcc,我装的是gcc4.0的rpm包。2.rpm包可以到http://www.findrpm.com上找,装gc...??方法参考网上教程。(装包:rpm ivh 包名、删包 rpm -e 包名、查询 rpm -qa|grep 包名、详细信息 rpm -q 包名 i、文件列表 rpm -q 包名 l)。3.apache,mysql,php...阅读全文

posted @ 2006-03-24 10:56 秋衫客 阅读(612) 评论(0) 编辑

2006年3月16日

摘要: SQL Relay的项目网址http://sqlrelay.sourceforge.net/上面几乎有关于SQL Relay的一切,不知道你有没有下载我写的那个操作类,可以看出,里面关于Oracle的代码并没有写完,我的项目主要是运行在Sybase上的,暂时没有操作Oracle的需要,所以没写,不过,我估计运行Oracle的问题应该不大。哦,对了,在使用中我还发现了一个问题,也不知道是不是SQL ...阅读全文

posted @ 2006-03-16 10:17 秋衫客 阅读(40) 评论(0) 编辑

2006年3月8日

posted @ 2006-03-08 16:33 秋衫客 阅读(85) 评论(1) 编辑