循序渐进学WinPcap

循序渐进学WinPcap

[导入][转]C#.net同步异步SOCKET通讯和多线程总结
     摘要: 同步套接字通信Socket支持下的网上点对点的通信服务端实现监听连接,客户端实现发送连接请求,建立连接后进行发送和接收数据的功能服务器端建立一个socket,设置好本机的ip和监听的端口与socket进行绑定,开始监听连接请求,当接收到连接请求后,发送确认,同客户端建立连接,开始与客户端进行通信。客户端建立一个socket,设置好服务器端的IP和提供服务的端口,发出连接请求,接收到服务的确认后,尽... 阅读全文
posted @ 2007-08-13 20:18 ymz 阅读(1147) | 评论 (0) | 编辑


[导入][转]实现之用Raw Socket实现Sniffer(1)
一. 摘要
    Raw Socket: 原始套接字
    可以用它来发送和接收 IP 层以上的原始数据包, 如 ICMP, TCP, UDP...

        int sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);

    这样我们就创建了一个 Raw Socket

    Sniffer: 嗅探器
    关于嗅探器的原理我想大多数人可能都知道
    1. 把网卡置于混杂模式;
    2. 捕获数据包;
    3. 分析数据包.

    但具体的实现知道的人恐怕就不是那么多了. 好, 现在让我们用 Raw Socket 的做一个自已的 Sniffer.

二. 把网卡置于混杂模式
    在正常的情况下,一个网络接口应该只响应两种数据帧:
    一种是与自己硬件地址相匹配的数据帧
    一种是发向所有机器的广播数据帧
    如果要网卡接收所有通过它的数据, 而不管是不是发给它的, 那么必须把网卡置于混杂模式. 也就是说让

它的思维混乱, 不按正常的方式工作. 用 Raw Socket 实现代码如下:

        setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag); //设置 IP 头操作选项
        bind(sockRaw, (PSOCKADDR)&addrLocal, sizeof(addrLocal); //把 sockRaw 绑定到本地网卡上
        ioctlsocket(sockRaw, SIO_RCVALL, &dwValue);             //让 sockRaw 接受所有的数据

    flag 标志是用来设置 IP 头操作的, 也就是说要亲自处理 IP 头: bool flag = ture;
    addrLocal 为本地地址: SOCKADDR_IN addrLocal;
    dwValue 为输入输出参数, 为 1 时执行, 0 时取消: DWORD dwValue = 1;
    没想到这么简单吧?

三. 捕获数据包
    你的 sockRaw 现在已经在工作了, 可以在局域网内其它的电脑上用 Sniffer 检测工具检测一下, 看你的
网卡是否处于混杂模式(比如 DigitalBrain 的 ARPKiller).
    不能让他白白的浪费资源啊, 抓包!

        recv(sockRaw, RecvBuf, BUFFER_SIZE, 0); //接受任意数据包

    #define BUFFER_SIZE 65535
    char RecvBuf[BUFFER_SIZE];
    越来越发现 Sniffer 原来如此的简单了, 这么一个函数就已经完成抓取数据包的任务了.

四. 分析数据包
    这回抓来的包和平常用 Socket 接受陌删筒皇且换厥露? 里面包含 IP, TCP 等原始信息. 要分析它
首先得知道这些结构.
    数据包的总体结构:
    ----------------------------------------------
    | ip header | tcp header(or x header) | data |
    ----------------------------------------------

    IP header structure:
             4        8        16                                       32 bit
    |--------|--------|----------------|--------------------------------|
    |  Ver   |  IHL   |Type of service |          Total length          |
    |--------|--------|----------------|--------------------------------|
    | Identification  |      Flags     |         Fragment offset        |
    |--------|--------|----------------|--------------------------------|
    |  Time to live   |    Protocol    |         Header checksum        |
    |--------|--------|----------------|--------------------------------|
    |                         Source address                            |
    |--------|--------|----------------|--------------------------------|
    |                       Destination address                         |
    |--------|--------|----------------|--------------------------------|
    |                        Option + Padding                           |
    |--------|--------|----------------|--------------------------------|
    |                                Data                               |
    |--------|--------|----------------|--------------------------------|

    TCP header structure:

 |--------------------------------|--------------------------------|
    |          Source port           |        Destination port        |
    |--------------------------------|--------------------------------|
    |                         Sequence number                         |
    |--------------------------------|--------------------------------|
    |                      Acknowledgement number                     |
    |--------------------------------|--------------------------------|
    |  Offset  | Resrvd  |U|A|P|R|S|F|            Window              |
    |--------------------------------|--------------------------------|
    |           Checksum             |        Urgent pointer          |
    |--------------------------------|--------------------------------|
    |                         Option + Padding                        |
    |--------------------------------|--------------------------------|
    |                              Data                               |
    |--------------------------------|--------------------------------|

五. 实现 Sniffer
    OK!
    现在都清楚了, 还等什么.
    下面是我用 BCB6 写的一个 Simple Sniffer 的代码, 仅供参考.
    (需要在工程文件里加入WS2_32.LIB这个文件)
//*************************************************************************//
//* CPP File: WMain.cpp
//* Simple Sniffer by shadowstar
//* http://shadowstar.126.com/
//*************************************************************************//
#include <vcl.h>
#pragma hdrstop

#include <winsock2.h>
#include <ws2tcpip.h>
#include <mstcpip.h>
#include <netmon.h>
#include "WMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
    : TForm(Owner)
{
WSADATA WSAData;
    BOOL    flag    = true;
    int     nTimeout = 1000;
    char    LocalName[16];
    struct  hostent *pHost;

    //检查 Winsock 版本号
    if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
        throw Exception("WSAStartup error!");

    //初始化 Raw Socket
    if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == INVALID_SOCKET)
        throw Exception("socket setup error!");

    //设置IP头操作选项
    if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)) == SOCKET_ERROR)
        throw Exception("setsockopt IP_HDRINCL error!");

    //获取本机名
    if (gethostname((char*)LocalName, sizeof(LocalName)-1) == SOCKET_ERROR)
        throw Exception("gethostname error!");

    //获取本地 IP 地址
    if ((pHost = gethostbyname((char*)LocalName)) == NULL)
        throw Exception("gethostbyname error!");

    addr_in.sin_addr    = *(in_addr *)pHost->h_addr_list[0]; //IP
    addr_in.sin_family  = AF_INET;
    addr_in.sin_port    = htons(57274);

    //把 sock 绑定到本地地址上
    if (bind(sock, (PSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR)
        throw Exception("bind error!");

    iSortDirection = 1;
}
//---------------------------------------------------------------------------
__fastcall TMainForm::~TMainForm()
{
    WSACleanup();
}
//---------------------------------------------------------------------------

void __fastcall TMainForm::btnCtrlClick(TObject *Sender)
{
    TListItem *Item;
    DWORD dwValue;
    int nIndex = 0;

    if (btnCtrl->Caption == "&Start")
    {
        dwValue = 1;
//设置 SOCK_RAW 为SIO_RCVALL,以便接收所有的IP包
        if (ioctlsocket(sock, SIO_RCVALL, &dwValue) != 0)
            throw Exception("ioctlsocket SIO_RCVALL error!");
        bStop = false;
        btnCtrl->Caption = "&Stop";
        lsvPacket->Items->Clear();
    }
    else
    {
        dwValue = 0;
        bStop = true;
        btnCtrl->Caption = "&Start";
        //设置SOCK_RAW为SIO_RCVALL,停止接收
        if (ioctlsocket(sock, SIO_RCVALL, &dwValue) != 0)
            throw Exception("WSAIoctl SIO_RCVALL error!");
    }

    while (!bStop)
    {
        if (recv(sock, RecvBuf, BUFFER_SIZE, 0) > 0)
        {
            nIndex++;
           
            ip  = *(IP*)RecvBuf;
            tcp = *(TCP*)(RecvBuf + (ip.HdrLen & IP_HDRLEN_MASK));

            Item = lsvPacket->Items->Add();
            Item->Caption = nIndex;
            Item->SubItems->Add(GetProtocolTxt(ip.Protocol));
            Item->SubItems->Add(inet_ntoa(*(in_addr*)&ip.SrcAddr));
            Item->SubItems->Add(inet_ntoa(*(in_addr*)&ip.DstAddr));
            Item->SubItems->Add(tcp.SrcPort);
            Item->SubItems->Add(tcp.DstPort);
            Item->SubItems->Add(ntohs(ip.TotalLen));
        }
        Application->ProcessMessages();
    }   
}
//---------------------------------------------------------------------------

AnsiString __fastcall TMainForm::GetProtocolTxt(int Protocol)
{
    switch (Protocol)
    {
        case IPPROTO_ICMP :           //1               /* control message protocol */
            return PROTOCOL_STRING_ICMP_TXT;
        case IPPROTO_TCP  :           //6               /* tcp */
            return PROTOCOL_STRING_TCP_TXT;
        case IPPROTO_UDP  :           //17              /* user datagram protocol */
            return PROTOCOL_STRING_UDP_TXT;
        default :
            return PROTOCOL_STRING_UNKNOWN_TXT;
    }
}
//---------------------------------------------------------------------------


//*************************************************************************//
//* Header File: WMain.h for WMain.cpp class TMainForm
//*************************************************************************//
//---------------------------------------------------------------------------

#ifndef WMainH
#define WMainH
//---------------------------------------------------------------------------
#define BUFFER_SIZE 65535

#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
#include <winsock2.h>
#include "netmon.h"


//---------------------------------------------------------------------------
class TMainForm : public TForm
{
__published: // IDE-managed Components
    TPanel *Panel1;
    TButton *btnCtrl;
    TListView *lsvPacket;
    TLabel *Label1;
    void __fastcall btnCtrlClick(TObject *Sender);
    void __fastcall lsvPacketColumnClick(TObject *Sender,
          TListColumn *Column);
    void __fastcall lsvPacketCompare(TObject *Sender, TListItem *Item1,
          TListItem *Item2, int Data, int &Compare);
    void __fastcall Label1Click(TObject *Sender);
private: // User declarations
    AnsiString __fastcall GetProtocolTxt(int Protocol);
public: // User declarations
    SOCKET      sock;
    SOCKADDR_IN addr_in;
    IP          ip;
    TCP         tcp;
    PSUHDR      psdHeader;

char        RecvBuf[BUFFER_SIZE];
    bool        bStop;

    int iSortDirection;
    int iColumnToSort;
   
    __fastcall TMainForm(TComponent* Owner);
    __fastcall ~TMainForm();
};
//---------------------------------------------------------------------------
extern PACKAGE TMainForm *MainForm;
//---------------------------------------------------------------------------
#endif

    偷了个懒, IP, TCP 头及一些宏定义用了 netmon.h 的头, 这个文件在 BCB6 的 include 目录下可以找得
到, 其中与本程序相关内容如下:

//*************************************************************************//
//* Header File: netmon.h
//*************************************************************************//
//
// IP Packet Structure
//
typedef struct _IP
{
    union
    {
        BYTE   Version;
        BYTE   HdrLen;
    };
    BYTE ServiceType;
    WORD TotalLen;
    WORD ID;
    union
    {
        WORD   Flags;
        WORD   FragOff;
    };
    BYTE TimeToLive;
    BYTE Protocol;
    WORD HdrChksum;
    DWORD   SrcAddr;
    DWORD   DstAddr;
    BYTE Options[0];
} IP;

typedef IP * LPIP;
typedef IP UNALIGNED * ULPIP;

//
// TCP Packet Structure
//
typedef struct _TCP
    {
    WORD SrcPort;
    WORD DstPort;
    DWORD SeqNum;
    DWORD AckNum;
    BYTE DataOff;
    BYTE Flags;
    WORD Window;
    WORD Chksum;
    WORD UrgPtr;
    } TCP;

typedef TCP *LPTCP;
typedef TCP UNALIGNED * ULPTCP;

// upper protocols
#define PROTOCOL_STRING_ICMP_TXT       "ICMP"
#define PROTOCOL_STRING_TCP_TXT        "TCP"
#define PROTOCOL_STRING_UDP_TXT        "UDP"
#define PROTOCOL_STRING_SPX_TXT        "SPX"
#define PROTOCOL_STRING_NCP_TXT        "NCP"

#define PROTOCOL_STRING_UNKNOW_TXT     "UNKNOW"


    这个文件也有人声称没有.
//*************************************************************************//
//* Header File: mstcpip.h
//*************************************************************************//
//  Copyright (c) Microsoft Corporation. All rights reserved.
#if _MSC_VER > 1000
#pragma once
#endif

/* Argument structure for SIO_KEEPALIVE_VALS */

struct tcp_keepalive {
    u_long  onoff;
    u_long  keepalivetime;
    u_long  keepaliveinterval;
};

// New WSAIoctl Options

#define SIO_RCVALL            _WSAIOW(IOC_VENDOR,1)
#define SIO_RCVALL_MCAST      _WSAIOW(IOC_VENDOR,2)
#define SIO_RCVALL_IGMPMCAST  _WSAIOW(IOC_VENDOR,3)
#define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
#define SIO_ABSORB_RTRALERT   _WSAIOW(IOC_VENDOR,5)
#define SIO_UCAST_IF          _WSAIOW(IOC_VENDOR,6)
#define SIO_LIMIT_BROADCASTS  _WSAIOW(IOC_VENDOR,7)
#define SIO_INDEX_BIND        _WSAIOW(IOC_VENDOR,8)
#define SIO_INDEX_MCASTIF     _WSAIOW(IOC_VENDOR,9)
#define SIO_INDEX_ADD_MCAST   _WSAIOW(IOC_VENDOR,10)
#define SIO_INDEX_DEL_MCAST   _WSAIOW(IOC_VENDOR,11)

// Values for use with SIO_RCVALL* options
#define RCVALL_OFF             0
#define RCVALL_ON              1
#define RCVALL_SOCKETLEVELONLY 2

    现在我们自已的 Sniffer 就做好了, Run, Start......哇, 这么多数据包, 都是从这一台机器上发出的,

它在干什么? 原来 Adminstrator 密码为空, 中了尼姆达病毒!

六. 小结
    优点: 实现简单, 不需要做驱动程序就可实现抓包.
    缺点: 数据包头不含帧信息, 不能接收到与 IP 同层的其它数据包, 如 ARP, RARP...
    这里提供的程序仅仅是一个 Sniffer 的例子, 没有对数据包进行进一步的分析. 写此文的目的在于熟悉
Raw Socket 编程方法, 了解 TCP/IP 协议结构原理以及各协议之间的关系.

 

文章来源:http://meymz.blog.163.com/blog/static/38120921200771333056703
posted @ 2007-08-13 20:18 ymz 阅读(162) | 评论 (0) | 编辑


[导入]C#中实现Socket端口复用
一、什么是端口复用:
  因为在winsock的实现中,对于服务器的绑定是可以多重绑定的,在确定多重绑定使用谁的时候,根据一条原则是谁的指定最明确则将包递交给谁,而且没有权限之分。这种多重绑定便称之为端口复用。

二、我们如何实现Socket端口复用:

  其实我们要实现端口复用很简单,我们只要使用SetSocketOption函数设置Socket选项就可以了。MSDN是这样解释的:
Socket 选项确定当前 Socket 的行为。对于具有 Boolean 数据类型的选项,指定非零值可启用该选项,指定零值可禁用该选项。对于具有整数数据类型的选项,指定适当的值。Socket 选项按照协议支持程度来分组。

我们来看看这个函数是怎么用的:

public void SetSocketOption (
    SocketOptionLevel optionLevel,
    SocketOptionName optionName,
    int optionValue
)
 

参数
optionLevel
SocketOptionLevel 值之一。

optionName
SocketOptionName 值之一。

optionValue
该选项的值。

以上参数大家可以去看看MSDN。我这里就不多讲了。

在这里我们optionLevel 参数传SocketOptionLevel.Socket;optionName参数传SocketOptionName.ReuseAddress; optionValue参传一个非零值,我传的是True,如果要禁用的话,就传False。

如:

socket2.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
具体我们看看下面的代码:

我们首先建立第一个Socket:

        Socket socket1;
        IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 20000);
        socket1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket1.Bind(localEP);
再建立第二个Socket:

        Socket socket2
        IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 20000);
        socket2= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket2.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
       //请注意这一句。ReuseAddress选项设置为True将允许将套接字绑定到已在使用中的地址。
        socket2.Bind(localEP);
这样Socket1和Socket2便绑定在同一个端口上了。


文章来源:http://meymz.blog.163.com/blog/static/38120921200771292837966
posted @ 2007-08-13 20:18 ymz 阅读(121) | 评论 (0) | 编辑


[导入]【转】Visual C#实现HTTP代理服务程序
转自:http://hi.baidu.com/borcn/blog/item/4d91a2ccacf8c21201e92814.html

网络代理程序的种类非常多,根据代理服务程序代理的协议不同,分成HTTP代理服务程序、FTP代理服务程序等,运行代理服务程序的服务器也就称为HTTP代理服务器和FTP代理服务器。在本节中介绍的Web代理服务程序代理的就是HTTP协议。
一.网络代理的类型及实现原理:

网络代理服务根据工作层次,一般可分为应用层代理、传输层代理和SOCKS代理。应用层代理是工作在TCP/IP参考模型的应用层之上,它支持对应用层协议(如HTTP,FTP)的代理。它提供的控制最多,但是不灵活,必须要有相应的协议支持。如果协议不支持代理(如 SMTP和POP),那就只能在应用层以下代理,也即传输层代理。传输层代理直接与TCP层交互,更加灵活。要求代理服务器具有部分真正服务器的功能:监听特定TCP或UDP端口,接收客户端的请求同时向客户端发出相应的响应。另一种代理需要改变客户端的IP栈,即SOCKS代理。它是可用的最强大、最灵活的代理标准协议。SOCK V4允许代理服务器内部的客户端完全地连接到外部的服务器,SOCK V5增加了对客户端的授权和认证,因此它是一种安全性较高的代理。本节后面介绍的代理是一种应用层上面的代理,所代理的协议是HTTP,也就是经常见到的 Web代理。

网络代理就是一个连接客户端(设定需要代理的计算机)和服务器端(需要访问资源的服务器)的桥。要实现这种桥,网络代理就必须满足下列条件,其实也是代理服务的运行的流程:

(1). 能够接收并解析客户端的请求。

(2). 创建到服务器的新连接,并根据转发客户端的请求信息。

(3). 接收服务器反馈的信息。

(4). 能够发出或解释服务器的响应并将该响应传回给客户端。

图01是网络代理服务的一个典型模型图:

 

图01:代理服务的模型
二.Visual C#实现Web代理服务程序

Web代理服务是代理服务中最常用的一种代理服务,按照代理服务的层次,它属于应用层代理,是对TCP/IP参考模型中的应用层的HTTP协议的代理。

Web代理服务也是代理服务中的一种,所以它也要满足代理服务的基本条件。在下面介绍的代理服务程序中,是按照下列的顺序来实现其功能的。

(1). 代理服务器程序侦听端口,接收客户端浏览器发送来的Web请求信息。

(2). 代理服务器程序接收到客户端Web请求信息后,解析出Web服务器的地址,并创建一个Socket实例,并以此实例连接Web服务器上。

(3). 通过创建的Socket传送客户端的Web请求数据包到Web服务器的80端口。

(4). 代理服务器程序接收Web服务器返回页面数据。

(5). 代理服务器程序把接收来的数据传送到客户端,实现Web代理。

由于客户端的对一个地址的浏览,要传送很多的Web请求信息,为了更快、更准确的处理这些信息,Web代理服务程序采用了多线程来处理每一个Web请求。细心的读者可能会发现,处理每一个客户端的Web请求信息,代理服务器软件都要使用二个Socket,一个是用来接收/传送客户机的信息,一个是和 Web服务器进行交流。为了区分这二个Socket,我们把他们都命名,和服务器对话的Socket,称为服务Socket;和客户端机器对话的 Scoket,称为客户Socket。

下面就开始Web代理服务程序的编写工作。

这个示例主要包含三个部分内容:

·创建一个Web代理类。

·Web代理服务的类的实例化。

·如何通过这个Web代理类的实例实现Web代理服务。

下面就是第一部分的具体的实现步骤。

(一).创建一个Web代理类

以下是具体的操作步骤如下:

1. 首先启动Visual Studio .Net,依次选择"文件"、"新建"、"项目"菜单后,在弹出"新建项目"对话框中将"项目类型"设置为"Visual C#项目",将"模板"设置为"Windows应用程序",在"名称"文本框中输入"WebProxy",在"位置"的文本框中输入"E:/VS.NET 项目",然后单击"确定"按钮。这样在"E:/VS.NET项目"目录中就创建了一个新名称为"WebProxy"文件夹,里面存放的就是 "WebProxy"的项目文件。

2. 选择菜单【项目】|【添加类】,弹出【添加新项】对话框

3. 将【模板】设置【类】

4. 在【名称】文本框中输入【Proxy】,单击【打开】按钮,具体如图02所示。


图02:Web代理项目中【添加新项】对话框

5. 在【解决方案资源管理器】窗口中,双击Proxy.cs文件,进入Proxy.cs文件的编辑界面。

6. 在Proxy.cs源文件的开头,添加下列代码,下列代码是导入Proxy.cs中要使用到的命名空间:

using System ;
using System.Net ;
using System.Net.Sockets ;
using System.Text ;
using System.IO ;

  7. 用下列构造函数替代默认的构造函数。下面的代码是在Proxy类中创建一个构造函数。 Proxy类只有一个构造函数,并且这个构造函数只有一个参数,这个参数是Socket对象,它主要用来和客户端进行数据交换,是一个客户Socket.。

public Proxy ( Socket socket )
{
//
// TODO: 在此处添加构造函数逻辑
//
this.clientSocket = socket ;
}

8. 创建Proxy类中的Run方法,Run方法是Proxy类中唯一的方法。其功能是从客户端接收HTTP请求,并传送到Web服务器,然后从Web服务器接收反馈来的数据,并传送到客户端。为了实现这二个不同方面的数据传送,Run方法中是通过二个Socket实例来实现的。在编写Run方法的时候,要注意下面几点:

(1). 由于HTTP是TCP/IP参考模型中的应用层协议,它建立于TCP协议之上,所以创建的Socket实例使用的协议类型应该为TCP协议。下面代码是创建可以传送HTTP请求命令到Web服务器和接收来自Web服务器反馈来信息的Socket实例:

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

(2). 另外一个Socket是在代理服务程序侦听端口号,接收挂起的连接请求时候得到的,以此Socket为参数,利用Proxy类中的构造函数,来创建一个Proxy实例的。此Socket实现从客户端接收HTTP请求信息,并传送数据到客户端。

Socket创建和使用是实现Web代理软件的关键,具体实现方法是在构造函数代码后面,输入下列代码,创建Proxy类的Run方法:

public void Run ( )
{
string clientmessage = " " ;
//存放来自客户端的HTTP请求字符串
string URL = " " ;
//存放解析出地址请求信息
int bytes = ReadMessage ( read , ref clientSocket , ref clientmessage ) ;
if ( bytes == 0 )
{
return ;
}

int index1 = clientmessage.IndexOf ( ' ' ) ;
int index2 = clientmessage.IndexOf ( ' ' , index1 + 1 ) ;
if ( ( index1 == -1 ) || ( index2 == -1 ) )
{
throw new IOException ( ) ;
}
string part1 = clientmessage.Substring ( index1 + 1 , index2 - index1 ) ;
int index3 = part1.IndexOf ( '/' , index1 + 8 ) ;
int index4 = part1.IndexOf ( ' ' , index1 + 8 ) ;
int index5 = index4 - index3 ;
URL = part1.Substring ( index1 + 4 , ( part1.Length - index5 ) - 8 ) ;

try
{
IPHostEntry IPHost = Dns.Resolve ( URL ) ;
Console.WriteLine ( "远程主机名: " + IPHost.HostName ) ;
string [] aliases = IPHost.Aliases ;
IPAddress[] address = IPHost.AddressList ;
Console.WriteLine ( "Web服务器IP地址:" + address[0] ) ;
//解析出要访问的服务器地址
IPEndPoint ipEndpoint = new IPEndPoint ( address[0] , 80 ) ;
Socket IPsocket = new Socket ( AddressFamily.InterNetwork , SocketType.Stream , ProtocolType.Tcp ) ;
//创建连接Web服务器端的Socket对象
IPsocket.Connect ( ipEndpoint ) ;
//Socket连Web接服务器
if ( IPsocket.Connected )
Console.WriteLine ( "Socket 正确连接!" ) ;
string GET = clientmessage ;
Byte[] ByteGet = ASCII.GetBytes ( GET ) ;
IPsocket.Send ( ByteGet , ByteGet.Length , 0 ) ;
//代理访问软件对服务器端传送HTTP请求命令
Int32 rBytes = IPsocket.Receive ( RecvBytes , RecvBytes.Length , 0 ) ;
//代理访问软件接收来自Web服务器端的反馈信息
Console.WriteLine ( "接收字节数:" + rBytes.ToString ( ) ) ;
String strRetPage = null ;
strRetPage = strRetPage + ASCII.GetString ( RecvBytes , 0 , rBytes ) ;
while ( rBytes > 0 )
{
rBytes = IPsocket.Receive ( RecvBytes , RecvBytes.Length , 0 ) ;
strRetPage = strRetPage + ASCII.GetString ( RecvBytes , 0 , rBytes ) ;
}
IPsocket.Shutdown ( SocketShutdown.Both ) ;
IPsocket.Close ( ) ;
SendMessage ( clientSocket , strRetPage ) ;
//代理服务软件往客户端传送接收到的信息
}
catch ( Exception exc2 )
{
Console.WriteLine ( exc2.ToString ( ) ) ;
}
}
//接收客户端的HTTP请求数据
private int ReadMessage ( byte [ ] ByteArray , ref Socket s , ref String clientmessage )
{
int bytes = s.Receive ( ByteArray , 1024 , 0 ) ;
string messagefromclient = Encoding.ASCII.GetString ( ByteArray ) ;
clientmessage = ( String )messagefromclient ;
return bytes ;
}
//传送从Web服务器反馈的数据到客户端
private void SendMessage ( Socket s , string message )
{
Buffer = new Byte[message.Length + 1] ;
int length = ASCII.GetBytes ( message , 0 , message.Length , Buffer , 0 ) ;
Console.WriteLine ( "传送字节数:" + length.ToString ( ) ) ;
s.Send ( Buffer , length , 0 ) ;
}

9. 在定义Proxy类代码区中加入下列代码,下列代码是定义Proxy类中的使用的一些变量,这些变量主要是在后面的定义Run方法中使用。

Socket clientSocket ;
Byte[] read = new byte[1024] ;
//定义一个空间,存储来自客户端请求数据包
Byte [] Buffer = null ;
Encoding ASCII = Encoding.ASCII ;
//设定编码
Byte[] RecvBytes = new Byte[4096] ;
//定义一个空间,存储Web服务器返回的数据

10. 至此,Proxy类的定义过程就完成了。把Proxy类实例化非常简单,和以前用的其他完全一样,具体语法如下:

public Proxy ( Socket socket );

参数:socket为一个Scoket实例

下面代码是创建一个Proxy实例:

Proxy proxy = new Proxy ( socket ) ;

  (二). 利用Proxy类,实现Web代理的具体示例:

下面是利用上面创建的Proxy类,实现Web代理程序的具体实现步骤,Proxy类被定义在命名空间WebProxy中。

1. 在Visual Studio .Net的代码编辑器中打开Class1.cs文件,进入Class1.cs的代码编辑界面。

2. 在Class1.cs源文件的开头导入下列命名空间:

using System ;
using System.Net ;
using System.Net.Sockets ;
using System.Text ;
using System.IO ;
using System.Threading ;
using WebProxy ;
//其中命名空间WebProxy是Proxy类所处的位置,具体可以参阅Proxy.cs源文件
//中命名空间的定义。

3. 在Main函数中添加下列代码,下列代码是利用Proxy类,来实现Web代理程序。

const int port = 8000 ;
//定义端口号
TcpListener tcplistener = new TcpListener ( port ) ;
Console.WriteLine ( "侦听端口号: " + port.ToString ( ) ) ;
tcplistener.Start ( ) ;
//侦听端口号
while ( true )
{
Socket socket = tcplistener.AcceptSocket ( ) ;
//并获取传送和接收数据的Scoket实例
Proxy proxy = new Proxy ( socket ) ;
//Proxy类实例化
Thread thread = new Thread ( new ThreadStart ( proxy.Run ) ) ;
//创建线程
thread.Start ( ) ;
//启动线程
}

保存上面的所有步骤,这样一个简单Web代理程序就算是完成了。此Web代理程序侦听的是8000端口号。

(三).测试Web代码程序:

Web代理程序要通过二台计算机才能够实现。其中的一台计算机运行Web代理程序,充当Web代理服务器。另外一台计算机充当客户机,通过Web代理服务器来浏览网页。在确定Web代理软件运行后,下面是对客户机进行必要的设置。

1. 打开IE浏览器。

2. 选择【工具】|【Internet选项】,弹出【Internet选项】对话框。在此对话框中选择【连接】页面,单击其中的【局域网设置】按钮。弹出【局域网(LAN)设置】对话框。选择【为LAN使用代理服务器(X),(这些设置不会应用于拨号和VPN连接)】多选框。并在其中的【地址】文本框中输入代理服务器的IP地址,由于测试的代理服务器的IP地址为"10.138.198.213",所有也输入此IP地址,在【端口】文本框中输入"8000"。具体如图03所示:


  此时客户端的设置就完成了,在确定IP地址为"10.138.198.213"的这台计算机已经运行上面介绍的 Web代理程序后。打开客户端的IE浏览器,并输入要浏览的网址,就可以通过Web代理服务器来浏览网页了,图04是Web代理服务程序在服务器端运行时的界面。


 四.总结:

至此一个简单的Web代理服务软件就算基本完成了,通过上面内容的介绍可见,虽然代理服务的实现原理相对简单,但具体实现其实还是很繁琐的。网络代理是一个内容丰富,实现复杂的论题,本节介绍的代理服务软件,无论在实现的协议种类,还是实现的功能,都只能算很小的一部分。希望各位能够通过本文的介绍,结合其他相关的知识,创造出功能更强大、安全性更高,使用更稳定的网络代理服务程序来

 

文章来源:http://meymz.blog.163.com/blog/static/3812092120077109230412
posted @ 2007-08-13 20:18 ymz 阅读(221) | 评论 (0) | 编辑


[导入]HTTP协议头字段
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1358128

http头的组成:
==============
 HTTP头字段包括4类:
       general-header ;
     request-header ;
       response-header ;
     entity-header .
 
*******************************************************************************
 General Header Fields
=============================
   general header是request、response都可用的, 但是不能用于entity.
 
 
       -- Cache-Control
       -- Connection
       -- Date
       -- Pragma
       -- Trailer
       -- Transfer-Encoding
       -- Upgrade
       -- Via
       -- Warning
 
*******************************************************************************
 Request Header Fields
======================
 
   request-header fields 允许客户端传递关于request和客户端的附加信息到服务端,
 
       -- Accept
       -- Accept-Charset
       -- Accept-Encoding
       -- Accept-Language
       -- Authorization
       -- Expect
       -- From
       -- Host
       -- If-Match
       -- If-Modified-Since
       -- If-None-Match
       -- If-Range
       -- If-Unmodified-Since
       -- Max-Forwards
       -- Proxy-Authorization
       -- Range
       -- Referer
       -- TE
       -- User-Agent
 
*******************************************************************************
  Response Header Fields
===============================
 
   response-header fields 允许服务端传递关于response的、不能放到Status-Line的附加信息。
   这些头给出关于服务端的信息。  
 
      -- Accept-Ranges
      -- Age
      -- ETag
      -- Location
      -- Proxy-Authenticate
      -- Retry-After
      -- Server
      -- Vary
      -- WWW-Authenticate
 
*******************************************************************************
 Entity Header Fields
========================
 
   Entity-header fields 定义关于entity-body的metainformation(标题字段数据),
   如果当前没有body, 则定义被request确定的资源信息.
   一些metainformation是可选的; 一些是必须的。
 
       -- Allow
       -- Content-Encoding
       -- Content-Language
       -- Content-Length
       -- Content-Location
       -- Content-MD5
       -- Content-Range
       -- Content-Type
       -- Expires
       -- Last-Modified
       -- extension-header


【转自】http://www.x5dj.com/userforum/00100239/00305167.shtml


一、基础篇
HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送WWW方式的数据,关于HTTP协议的详细内容请参考RFC2616。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求,请求头包含请求的方法、URI、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。服务器以一个状态行作为响应,相应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息、实体元信息以及可能的实体内容。
通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。这两种类型的消息由一个起始行,一个或者多个头域,一个只是头域结束的空行和可选的消息体组成。HTTP的头域包括通用头,请求头,响应头和实体头四个部分。每个头域由一个域名,冒号(:)和域值三部分组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。
1、通用头域
通用头域包含请求和响应消息都支持的头域,通用头域包含Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via。对通用头域的扩展要求通讯双方都支持此扩展,如果存在不支持的通用头域,一般将会作为实体头域处理。下面简单介绍几个在UPnP消息中使用的通用头域。
Cache-Control头域
Cache-Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令包括no-cache、no- store、max-age、max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、 private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、 max-age。各个消息中的指令含义如下:
Public指示响应可被任何缓存区缓存。
Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache指示请求或响应消息不能缓存
no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
Date头域
Date头域表示消息发送的时间,时间的描述格式由rfc822定义。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时,换算成本地时间,需要知道用户所在的时区。
Pragma头域
Pragma头域用来包含实现特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1协议中,它的含义和Cache-Control:no-cache相同。
2、请求消息
请求消息的第一行为下面的格式:
Method SP Request-URI SP HTTP-Version CRLF 
Method表示对于Request-URI完成的方法,这个字段是大小写敏感的,包括OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE。方法GET和HEAD应该被所有的通用WEB服务器支持,其他所有方法的实现是可选的。GET方法取回由Request-URI标识的信息。HEAD方法也是取回由Request-URI标识的信息,只是可以在响应时,不返回消息体。POST方法可以请求服务器接收包含在请求中的实体信息,可以用于提交表单,向新闻组、BBS、邮件群组和数据库发送消息。
SP表示空格。
Request-URI遵循URI格式,在此字段为星号(*)时,说明请求并不用于某个特定的资源地址,而是用于服务器本身。
HTTP-Version表示支持的HTTP版本,例如为HTTP/1.1。
CRLF表示换行回车符。
请求头域允许客户端向服务器传递关于请求或者关于客户机的附加信息。请求头域可能包含下列字段Accept、Accept-Charset、Accept- Encoding、Accept-Language、Authorization、From、Host、If-Modified-Since、If- Match、If-None-Match、If-Range、If-Range、If-Unmodified-Since、Max-Forwards、 Proxy-Authorization、Range、Referer、User-Agent。对请求头域的扩展要求通讯双方都支持,如果存在不支持的请求头域,一般将会作为实体头域处理。
典型的请求消息:
GEThttp://class/download.microtool.de:80/somedata.exe
Host:download.microtool.de
Accept:*/*
Pragma:no-cache
Cache-Control:no-cache
Referer:http://class/download.microtool.de/
User-Agent:Mozilla/4.04[en](Win95;I;Nav)
Range:bytes=554554-
上例第一行表示HTTP客户端(可能是浏览器、下载程序)通过GET方法获得指定URL下的文件。棕色的部分表示请求头域的信息,绿色的部分表示通用头部分。
Host头域
Host头域指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回。
Referer头域
Referer头域允许客户端指定请求uri的源资源地址,这可以允许服务器生成回退链表,可用来登陆、优化cache等。他也允许废除的或错误的连接由于维护的目的被追踪。如果请求的uri没有自己的uri地址,Referer不能被发送。如果指定的是部分uri地址,则此地址应该是一个相对地址。
Range头域
Range头域可以请求实体的一个或者多个子范围。例如,
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200(OK)。
User-Agent头域
User-Agent头域的内容包含发出请求的用户信息。

3、响应消息
响应消息的第一行为下面的格式:
HTTP-Version SP Status-Code SP Reason-Phrase CRLF
HTTP-Version表示支持的HTTP版本,例如为HTTP/1.1。
Status-Code是一个三个数字的结果代码。
Reason-Phrase给Status-Code提供一个简单的文本描述。Status-Code主要用于机器自动识别,Reason-Phrase主要用于帮助用户理解。Status-Code的第一个数字定义响应的类别,后两个数字没有分类的作用。第一个数字可能取5个不同的值:
1xx:信息响应类,表示接收到请求并且继续处理
2xx:处理成功响应类,表示动作被成功接收、理解和接受
3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理
4xx:客户端错误,客户请求包含语法错误或者是不能正确执行
5xx:服务端错误,服务器不能正确执行一个正确的请求
响应头域允许服务器传递不能放在状态行的附加信息,这些域主要描述服务器的信息和Request-URI进一步的信息。响应头域包含Age、 Location、Proxy-Authenticate、Public、Retry-After、Server、Vary、Warning、WWW- Authenticate。对响应头域的扩展要求通讯双方都支持,如果存在不支持的响应头域,一般将会作为实体头域处理。
典型的响应消息:
HTTP/1.0200OK
Date:Mon,31Dec200104:25:57GMT
Server:Apache/1.3.14(Unix)
Content-type:text/html
Last-modified:Tue,17Apr200106:46:28GMT
Etag:"a030f020ac7c01:1e9f"
Content-length:39725426
Content-range:bytes554554-40279979/40279980
上例第一行表示HTTP服务端响应一个GET方法。棕色的部分表示响应头域的信息,绿色的部分表示通用头部分,红色的部分表示实体头域的信息。
Location响应头
Location响应头用于重定向接收者到一个新URI地址。
Server响应头
Server响应头包含处理请求的原始服务器的软件信息。此域能包含多个产品标识和注释,产品标识一般按照重要性排序。
4、实体信息
请求消息和响应消息都可以包含实体信息,实体信息一般由实体头域和实体组成。实体头域包含关于实体的原信息,实体头包括Allow、Content-Base、Content-Encoding、Content-Language、 Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、 Etag、Expires、Last-Modified、extension-header。extension-header允许客户端定义新的实体头,但是这些域可能无法未接受方识别。实体可以是一个经过编码的字节流,它的编码方式由Content-Encoding或Content-Type定义,它的长度由Content-Length或Content-Range定义。
Content-Type实体头
Content-Type 实体头用于向接收方指示实体的介质类型,指定HEAD方法送到接收方的实体介质类型,或GET方法发送的请求介质类型Content-Range实体头
Content-Range实体头
用于指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式:
Content-Range:bytes-unit SP first-byte-pos - last-byte-pos/entity-legth
例如,传送头500个字节次字段的形式:Content-Range:bytes0-499/1234如果一个http消息包含此节(例如,对范围请求的响应或对一系列范围的重叠请求),Content-Range表示传送的范围,Content-Length表示实际传送的字节数。
Last-modified实体头
Last-modified实体头指定服务器上保存内容的最后修订时间。
5、 HTTP 头参考(microsoft)
HTTP 请求和 HTTP 响应都使用头发送有关 HTTP 消息的信息。头由一系列行组成,每行都包含名称,然后依次是冒号、空格、值。字段可按任何顺序排列。某些头字段既能用于请求头也能用于响应头,而另一些头字段只能用于其中之一。
许多请求头字段都允许客户端在值部分指定多个可接受的选项,有时甚至可以对这些选项的首选项进行排名。多个项以逗号分隔。例如,客户端可以发送包含 “Content-Encoding: gzip, compress,”的请求头,表示可以接受各种压缩类型。如果服务器的响应正文使用 gzip 编码,其响应头中将包含“Content-Encoding: gzip”。
有些字段可以在单个头中出现多次。例如,头可以有多个“Warning”字段。
下表列出了 HTTP 1.1 头字段。注意:有些头字段是 MIME 字段。MIME 字段在 Internet Engineering Task Force (IETF) 文档 RFC 2045 中进行了定义,但也可用于 HTTP 1.1 协议。有关 MIME 和 HTTP 1.1 规范的详细信息,请参阅 IEIF 页。
一般头字段
一般头字段可用于请求消息和响应消息。
 名称          示例值
Cache-Control  "max-age=10"
Connection    "close"
Date          "Tue, 11 Jul 2000 18:23:51 GMT"
Pragma        "no-cache"
Trailer         "Date"
Transfer-Encoding "chunked"
Upgrade       "SHTTP/1.3"
Via            "HTTP/1.1 Proxy1, HTTP/1.1 Proxy2"
Warning       "112 Disconnected Operation"
请求头字段
请求头字段仅用于请求消息。
   名称             示例值
Accept           "text/html, image/*"
Accept-Charset   "iso8859-5"
Accept-Encoding  "gzip, compress"
Accept-Language "en, fr"
Authorization     [credentials]
Content-Encoding "gzip"
Expect           "100-continue"
From            "user@microsoft.com"
Host            "www.microsoft.com"
If-Match         "entity_tag001"
If-Modified-Since "Tue, 11 Jul 2000 18:23:51 GMT"
If-None-Match    "entity_tag001"
If-Range         "entity_tag001" or "Tue, 11 Jul 2000 18:23:51 GMT"
If-Unmodified-Since "Tue, 11 Jul 2000 18:23:51 GMT"
Max-Forwards    "3"
Proxy-Authorization [credentials]
Range       "bytes=100-599"
Referer      "http://www.microsoft.com/resources.asp"
TE          "trailers"
User-Agent   "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)"
 
>>请求头字段的具体含义
Accept:浏览器可接受的MIME类型。
Accept-Charset:浏览器可接受的字符集。
Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。
Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。
Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点, Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。
Content-Length:表示请求消息正文的长度。
Cookie:设置cookie,这是最重要的请求头信息之一
From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。
Host:初始URL中的主机和端口。
If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答。
Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝。
Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。
响应头字段
响应头字段仅用于响应消息。
  名称          示例值
Accept-Ranges  "none"
Age            "2147483648(2^31)"
ETag           "b38b9-17dd-367c5dcd"
Last-Modified    "Tue, 11 Jul 2000 18:23:51 GMT"
Location        "http://localhost/redirecttarget.asp"
Proxy-Authenticate [challenge]
Retry-After      "Tue, 11 Jul 2000 18:23:51 GMT" or "60"
Server         "Microsoft-IIS/5.0"
Vary            "Date"
WWW-Authenticate [challenge]
实体头字段
实体头字段可以用于请求消息或响应消息。实体头字段中包含消息实体正文的有关信息,如使用的编码格式。
   名称            示例值
Allow              "GET, HEAD"
Content-Encoding   "gzip"
Content-Language  "en"
Content-Length     "8445"
Content-Location   "http://localhost/page.asp"
Content-MD5       [md5-digest]
Content-Range     "bytes 2543-4532/7898"
Content-Type      "text/html"
Expires           "Tue, 11 Jul 2000 18:23:51 GMT"
Last-Modified      "Tue, 11 Jul 2000 18:23:51 GMT"
>>实体头字段的具体含义
Allow 服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。
Content-Length 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。
Content-Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。
Date 当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
Expires 应该在什么时候认为文档已经过期,从而不再缓存它?
Last-Modified 文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。
Location 表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。
Refresh 表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。
注意这种功能通常是通过设置HTML页面HEAD区的<META HTTP-EQUIV="Refresh" C>实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是,对于Servlet来说,直接设置 Refresh头更加方便。
注意Refresh的意义是“N秒之后刷新本页面或访问指定页面”,而不是“每隔N秒刷新本页面或访问指定页面 ”。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷新,不管是使用Refresh头还是<META HTTP-EQUIV="Refresh" ...>。
注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。
请求头示例
以下是 HTTP 请求的简单示例。
GET /articles/news/today.asp HTTP/1.1
Accept: */*
Accept-Language: en-us
Connection: Keep-Alive
Host: localhost
Referer: http://localhost/links.asp
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Accept-Encoding: gzip, deflate
该请求具有请求行,其中包括方法 (GET)、资源路径 (/articles/news/today.asp) 和 HTTP 版本 (HTTP/1.1)。由于该请求没有正文,故所有请求行后面的内容都是头的一部分。紧接着头之后是一个空行,表示头已结束。
响应头示例
Web 服务器可以通过多种方式响应前一个请求。假设文件是可以访问的,并且用户具有查看该文件的权限,则响应类似于:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Thu, 13 Jul 2000 05:46:53 GMT
Content-Length: 2291
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGGGNCG=LKLDFFKCINFLDMFHCBCBMFLJ; path=/
Cache-control: private
...
响应的第一行称为状态行。它包含响应所用的 HTTP 版本、状态编码 (200) 和原因短语。示例中包含一个头,其中具有五个字段,接着是一个空行(回车和换行符),然后是响应正文的头两行。
有关HTTP头完整、详细的说明,请参见http://www.w3.org/Protocols/的HTTP规范。
 
附录:HTTP协议状态码的含义
  状态代码 状态信息 含义
100 Continue 初始的请求已经接受,客户应当继续发送请求的其余部分。(HTTP 1.1新)
101 Switching Protocols 服务器将遵从客户的请求转换到另外一种协议(HTTP 1.1新
200 OK 一切正常,对GET和POST请求的应答文档跟在后面。
201 Created 服务器已经创建了文档,Location头给出了它的URL。
202 Accepted 已经接受请求,但处理尚未完成。
203 Non-Authoritative Information 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝(HTTP 1.1新)。
204 No Content 没有新文档,浏览器应该继续显示原来的文档。
205 Reset Content 没有新的内容,但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容(HTTP 1.1新)。
206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它(HTTP 1.1新)。
300 Multiple Choices 客户请求的文档可以在多个位置找到,这些位置已经在返回的文档内列出。如果服务器要提出优先选择,则应该在Location应答头指明。
301 Moved Permanently 客户请求的文档在其他地方,新的URL在Location头中给出,浏览器应该自动地访问新的URL。
302 Found 类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。注意,在HTTP1.0中对应的状态信息是“Moved Temporatily”,出现该状态代码时,浏览器能够自动访问新的URL,因此它是一个很有用的状态代码。注意这个状态代码有时候可以和301替换使用。例如,如果浏览器错误地请求http://host/~user(缺少了后面的斜杠),有的服务器返回301,有的则返回302。严格地说,我们只能假定只有当原来的请求是GET时浏览器才会自动重定向。请参见307。
303 See Other 类似于301/302,不同之处在于,如果原来的请求是POST,Location头指定的重定向目标文档应该通过GET提取(HTTP 1.1新)。
304 Not Modified 客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取(HTTP 1.1新)。
307 Temporary Redirect 和302(Found)相同。许多浏览器会错误地响应302应答进行重定向,即使原来的请求是POST,即使它实际上只能在POST请求的应答是303时才能重定向。由于这个原因,HTTP 1.1新增了307,以便更加清除地区分几个状态代码:当出现303应答时,浏览器可以跟随重定向的GET和POST请求;如果是307应答,则浏览器只能跟随对GET请求的重定向。(HTTP 1.1新)
400 Bad Request 请求出现语法错误。
401 Unauthorized 客户试图未经授权访问受密码保护的页面。应答中会包含一个WWW-Authenticate头,浏览器据此显示用户名字/密码对话框,然后在填写合适的Authorization头后再次发出请求。
403 Forbidden 资源不可用。服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。
404 Not Found 无法找到指定位置的资源。这也是一个常用的应答,
405 Method Not Allowed 请求方法(GET、POST、HEAD、DELETE、PUT、TRACE等)对指定的资源不适用。(HTTP 1.1新)
406 Not Acceptable 指定的资源已经找到,但它的MIME类型和客户在Accpet头中所指定的不兼容(HTTP 1.1新)。
407 Proxy Authentication Required 类似于401,表示客户必须先经过代理服务器的授权。(HTTP 1.1新)
408 Request Timeout 在服务器许可的等待时间内,客户一直没有发出任何请求。客户可以在以后重复同一请求。(HTTP 1.1新)
409 Conflict 通常和PUT请求有关。由于请求和资源的当前状态相冲突,因此请求不能成功。(HTTP 1.1新)
410 Gone 所请求的文档已经不再可用,而且服务器不知道应该重定向到哪一个地址。它和404的不同在于,返回407表示文档永久地离开了指定的位置,而404表示由于未知的原因文档不可用。(HTTP 1.1新)
411 Length Required 服务器不能处理请求,除非客户发送一个Content-Length头。(HTTP 1.1新)
412 Precondition Failed 请求头中指定的一些前提条件失败(HTTP 1.1新)。
413 Request Entity Too Large 目标文档的大小超过服务器当前愿意处理的大小。如果服务器认为自己能够稍后再处理该请求,则应该提供一个Retry-After头(HTTP 1.1新)。
414 Request URI Too Long URI太长(HTTP 1.1新)。
416 Requested Range Not Satisfiable 服务器不能满足客户在请求中指定的Range头。(HTTP 1.1新)
500 Internal Server Error 服务器遇到了意料不到的情况,不能完成客户的请求。
501 Not Implemented 服务器不支持实现请求所需要的功能。例如,客户发出了一个服务器不支持的PUT请求。
502 Bad Gateway 服务器作为网关或者代理时,为了完成请求访问下一个服务器,但该服务器返回了非法的应答。
503 Service Unavailable 服务器由于维护或者负载过重未能应答。
504 Gateway Timeout 由作为代理或网关的服务器使用,表示不能及时地从远程服务器获得应答。(HTTP 1.1新)
505 HTTP Version Not Supported 服务器不支持请求中所指明的HTTP版本

 

文章来源:http://meymz.blog.163.com/blog/static/38120921200771033135285
posted @ 2007-08-13 20:18 ymz 阅读(560) | 评论 (0) | 编辑


[导入] 【转】用C#实现HTTP协议下的多线程文件传输
很多人都有过使用网络蚂蚁或网络快车互联网文件的经历,这些软件的使用可以大大加速互联网上文件的传输速度,减少文件传输的时间。这些软件为什么有如此大的魔力呢?其主要原因是这些软件都采用了多线程下载和断点续传技术。如果我们自己来编写一个类似这样的程序,也能够快速的在互联网上下载文件,那一定是非常愉快的事情。下面我就讲一讲如何利用C#语言编写一个支持多线程下载文件的程序,你会看到利用C#语言编写网络应程序是多么的容易,从中也能体会到C#语言中强大的网络功能。

  首先介绍一下HTTP协议, HTTP亦即Hpyer Text Transfer Protocal的缩写,它是现代互联网上最重要的一种网络协议,超文本传输协议位于TCP/IP协议的应用层,是一个面向无连接、简单、快速的C/S结构的协议。HTTP的工作过程大体上分连接、请求、响应和断开连接四个步骤。C#语言对HTTP协议提供了良好的支持,在类库中提供了WebRequest和WebResponse类,这两个类都包含在System.Net命名空间中,利用这两个类可以实现很多高级的网络功能,本文中多线程文件下载就是利用这两个类实现的。 WebRequest和WebResponse都是抽象基类,因此在程序中不能直接作为对象使用,必须被继承,实际使用中,可根据URI参数中的URI前缀选用它们合适的子类,对于HTTP这类URI,HttpWebRequest和HttpWebResponse类可以用于处理客户程序同WEB服务器之间的HTTP通讯。

  HttpWebRequest类实现了很多通过HTTP访问WEB服务器上文件的高级功能。 HttpWebRequest类对WebRequest中定义的属性和方法提供支持,HttpWebRequest将发送到Internet资源的公共 HTTP标头的值公开为属性,由方法或系统设置,常用的由属性或方法设置的HTTP标头为:接受, 由Accept属性设置, 连接, 由Connection属性和KeepAlive属性设置, Content-Length, 由ContentLength属性设置, Content-Type, 由ContentType属性设置, 范围, 由AddRange方法设置. 实际使用中是将标头信息正确设置后,传递到WEB服务器,WEB服务器根据要求作出回应。

  HttpWebResponse类继承自WebResponse类,专门处理从WEB服务器返回的HTTP响应,这个类实现了很多方法,具有很多属性,可以全面处理接收到的互联网信息。在HttpWebResponse类中,对于大多数通用的HTTP标头字段,都有独立的属性与其对应,程序员可以通过这些属性方便的访问位于HTTP接收报文标头字段中的信息,本例中用到的HttpWebResponse类属性为:ContentLength 既接收内容的长度。

  有了以上的了解后,下面看看这两个类的用法,要创建HttpWebRequest对象,不要直接使用HttpWebRequest的构造函数,而要使用WebRequest.Create方法初始化一个HttpWebRequest实例,如:

HttpWebRequest hwr=(HttpWebRequest)WebRequest.Create(http://www.163.com/); 

  创建了这个对象后,就可以通过HttpWebRequest属性,设置很多HTTP标头字段的内容,如hwr.AddRange(100,1000);设置接收对象的范围为100-1000字节。

HttpWebReques对象使用GetResponse()方法时,会返回一个HttpWebResponse对象,为提出HTTP返回报文信息,需要使用HttpWebResponse的GetResponseStream()方法,该方法返回一个Stream对象,可以读取HTTP返回的报文,如:首先定义一个Strean 对象 public System.IO.Stream ns; 然后 ns=hwr.GetResponse ().GetResponseStream ();即可创建Stream对象。有了以上的准备知识后下面开始设计我们的多线程互联网文件的下载程序,首先打开.Net集成开发环境,选择“文件”、“新建”、“项目”,然后选择“Visual C#项目”,在向导右边列表框中选中“Windows应用程序”,输入项目名称,如本例为:httpftp,然后选择“确定”按钮,向导自动生成了一个 Windows应用程序项目。首先打开窗口设计器设计应用程序窗口,增加如下控件:

  一个列表框 listBox1 三个文本标签 label1-label3 三个文本框 textBox1-textBox3 一个开始接收按钮 button1 设计好的窗口如下图:


 

  控件定义代码是:

public System.Windows.Forms.ListBox listBox1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox1
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.TextBox textBox4;

  打开Form1的代码编辑器,增加如下的命名空间:

using System.Net;//网络功能
using System.IO;//流支持
using System.Threading ;//线程支持

  增加如下的程序变量:

public bool[] threadw; //每个线程结束标志
public string[] filenamew;//每个线程接收文件的文件名
public int[] filestartw;//每个线程接收文件的起始位置
public int[] filesizew;//每个线程接收文件的大小
public string strurl;//接受文件的URL
public bool hb;//文件合并标志
public int thread;//进程数

  定义一个HttpFile类,用于管理接收线程,其代码如下:

public class HttpFile
{
 public Form1 formm;
 public int threadh;//线程代号
 public string filename;//文件名
 public string strUrl;//接收文件的URL
 public FileStream fs;
 public HttpWebRequest request;
 public System.IO.Stream ns;
 public byte[] nbytes;//接收缓冲区
 public int nreadsize;//接收字节数
 public HttpFile(Form1 form,int thread)//构造方法
 {
  formm=form;
  threadh=thread;
 }
 ~HttpFile()//析构方法
 {
  formm.Dispose ();
 }
 public void receive()//接收线程
 {
  filename=formm.filenamew[threadh];
  strUrl=formm.strurl;
  ns=null;
  nbytes= new byte[512];
  nreadsize=0;
  formm.listBox1 .Items .Add ("线程"+threadh.ToString ()+"开始接收");
  fs=new FileStream (filename,System.IO.FileMode.Create);
  try
  {
   request=(HttpWebRequest)HttpWebRequest.Create (strUrl);
   //接收的起始位置及接收的长度
   request.AddRange(formm.filestartw [threadh],
   formm.filestartw [threadh]+formm.filesizew [threadh]);
   ns=request.GetResponse ().GetResponseStream ();//获得接收流
   nreadsize=ns.Read (nbytes,0,512);
   while (nreadsize>0)
   {
    fs.Write (nbytes,0,nreadsize);
    nreadsize=ns.Read (nbytes,0,512);
    formm.listBox1 .Items .Add ("线程"+threadh.ToString ()+"正在接收");
   }
   fs.Close();
   ns.Close ();
  }
  catch (Exception er)
  {
   MessageBox.Show (er.Message );
   fs.Close();
  }
  formm.listBox1 .Items.Add ("进程"+threadh.ToString ()+"接收完毕!");
  formm.threadw[threadh]=true;
 }
}

  该类和Form1类处于统一命名空间,但不包含在Form1类中。下面定义“开始接收”按钮控件的事件响应函数:

private void button1_Click(object sender, System.EventArgs e)
{
 DateTime dt=DateTime.Now;//开始接收时间
 textBox1.Text =dt.ToString ();
 strurl=textBox2.Text .Trim ().ToString ();
 HttpWebRequest request;
 long filesize=0;
 try
 {
  request=(HttpWebRequest)HttpWebRequest.Create (strurl);
  filesize=request.GetResponse ().ContentLength;//取得目标文件的长度
  request.Abort ();
 }
 catch (Exception er)
 {
  MessageBox.Show (er.Message );
 }
 // 接收线程数
 thread=Convert.ToInt32 (textBox4.Text .Trim().ToString (),10);
 //根据线程数初始化数组
 threadw=new bool [thread];
 filenamew=new string [thread];
 filestartw=new int [thread];
 filesizew=new int[thread];

 //计算每个线程应该接收文件的大小
 int filethread=(int)filesize/thread;//平均分配
 int filethreade=filethread+(int)filesize%thread;//剩余部分由最后一个线程完成
 //为数组赋值
 for (int i=0;i<thread;i++)
 {
  threadw[i]=false;//每个线程状态的初始值为假
  filenamew[i]=i.ToString ()+".dat";//每个线程接收文件的临时文件名
  if (i<thread-1)
  {
   filestartw[i]=filethread*i;//每个线程接收文件的起始点
   filesizew[i]=filethread-1;//每个线程接收文件的长度
  }
  else
  {
   filestartw[i]=filethread*i;
   filesizew[i]=filethreade-1;
  }
 }
 //定义线程数组,启动接收线程
 Thread[] threadk=new Thread [thread];
 HttpFile[] httpfile=new HttpFile [thread];
 for (int j=0;j<thread;j++)
 {
  httpfile[j]=new HttpFile(this,j);
  threadk[j]=new Thread(new ThreadStart (httpfile[j].receive ));
  threadk[j].Start ();
 }
 //启动合并各线程接收的文件线程
 Thread hbth=new Thread (new ThreadStart (hbfile));
 hbth.Start ();
}
 

  合并文件的线程hbfile定义在Form1类中,定义如下:

public void hbfile()
{
 while (true)//等待
 {
  hb=true;
  for (int i=0;i<thread;i++)
  {
   if (threadw[i]==false)//有未结束线程,等待
   {
    hb=false;
    Thread.Sleep (100);
    break;
   }
  }
  if (hb==true)//所有线程均已结束,停止等待,
  {
   break;
  }
 }
 FileStream fs;//开始合并
 FileStream fstemp;
 int readfile;
 byte[] bytes=new byte[512];
 fs=new FileStream (textBox3.Text .Trim ().ToString (),System.IO.FileMode.Create);
 for (int k=0;k<thread;k++)
 {
  fstemp=new FileStream (filenamew[k],System.IO.FileMode .Open);
  while (true)
  {
   readfile=fstemp.Read (bytes,0,512);
   if (readfile>0)
   {
    fs.Write (bytes,0,readfile);
   }
   else
   {
    break;
   }
  }
  fstemp.Close ();
 }
 fs.Close ();
 DateTime dt=DateTime.Now;
 textBox1.Text =dt.ToString ();//结束时间
 MessageBox.Show ("接收完毕!!!");

至此,一个多线程下载文件的程序就大功告成了,注意在输入本地文件名时,应按如下格式输入:“c://test//httpftp//bin //d.htm”,因”/”后的字符在C#中是转义字符,线程数并非越大越好,一般5个线程就可以了,该程序在Visual Studio.Net 2002开发环境及Windows xp 上通过。


文章来源:http://meymz.blog.163.com/blog/static/38120921200771032515991
posted @ 2007-08-13 20:18 ymz 阅读(110) | 评论 (0) | 编辑


[导入]什么是MIME
MIME(Multipurpose Internet Mail Extensions)多功能Internet 邮件扩充服务。
MIME利用了一个事实就是,RFC 822在消息体的内容中做了一点限制:唯一的限制就是只能使用简单的ASCII文本。所以,MIME信息由正常的Internet文本邮件组成,文本邮件拥有一些特别的符合RFC 822的信息头和格式化过的信息体(用ASCII 的子集来表示的附件)。这些MIME头给出了一种在邮件中表示附件的特别的方法。

MIME信息的剖析
一个普通的文本邮件的信息包含一个头部分(To: From: Subject: 等等)和一个体部分(Hello Mr.,等等)。在一个符合MIME的信息中,也包含一个信息头并不奇怪,邮件的各个部分叫做MIME段,每段前也缀以一个特别的头。MIME邮件只是基于RFC 822邮件的一个扩展。然而它有着自已的RFC规范集。
头字段
MIME头根据在邮件包中的位置,大体上分为MIME信息头和MIME段头。(译者:MIME信息头指整个邮件的头,而MIME段头只每个MIME段的头。)

MIME信息头有:
MIME-Version:
这个头提供了所用MIME的版本号。这个值习惯上为1.0。
Content-Type:
它定义了数据的类型,以便数据能被适当的处理。有效的类型有:text,
image,audio,video, applications,multipart和message。注意任何一个二进制附件都应该被叫做application/octet- stream。这个头的一些用例为:image/jpg, application/mswork,multipart/mixed,这只是很少的一部分。

Content-Transfer-Encoding:
这是所有头中最重要的一个,因为它说明了对数据所执行的编码方式,客
户/MUA 将用它对附件进行解码。对于每个附件,可以使用7bit,8bit,
binary ,quoted-printable,base64和custom中的一种编码方式。7bit编码是用在US ASCII字符集上的常用的一种编码方式,也就是,保持它的原样。8bit和binary编码一般不用。对人类可读的标准文本,如果传输要经过对格式有影响的网关时对其进行保护,可以使用quoted printable 。Base64是一种通用方法,在需要决定使用哪一种编码方法时,它提供了一个不用费脑子的选择;它通常用在二进制,非文本数据上。注意,任何非7bit 数据必须用一种模式编码,这样它就可以通过Internet邮件网关!
Content-ID:
如果Content-Type是message/external-body或multipart/alternative时,这个
头就有用了。它超出了本文的范围。
Content-Description:
这是一个可选的头。它是任何信息段内容的自由文本描述。描述必须使用us-ascii码。
Content-Disposition:
一个试验性的头,它用于给客户程序/MUA提供提示,来决定是否在行内显示附件或作为单独的附件。
MIME段头(出现在实际的MIME附件部分的头),除了MIME-Version头,可以拥有以上任何头字段。如果一个MIME头是信息块的一部分,它将作用于整个信息体。例如,如果Content-Transfer-Encoding显示在信息(指整个信息)头中,它应用于整个信息体,但是如果它显示在一个MIME段里,它"只能"用于那个段中.

文章来源:http://meymz.blog.163.com/blog/static/38120921200771022824489
posted @ 2007-08-13 20:18 ymz 阅读(81) | 评论 (0) | 编辑


[导入]WWW的核心——HTTP协议

众所周知,Internet的基本协议是TCP/IP协议,目前广泛采用的FTP、Archie Gopher等是建立在TCP/IP协议之上的应用层协议,不同的协议对应着不同的应用。<BR> WWW服务器使用的主要协议是HTTP协议,即超文体传输协议。由于HTTP协议支持的服务不限于WWW,还可以是其它服务,因而HTTP协议允许用户在统一的界面下,采用不同的协议访问不同的服务,如FTP、Archie、SMTP、NNTP等。另外,HTTP协议还可用于名字服务器和分布式对象管理。

2.1 HTTP协议简介
HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中,而且HTTP-NG(Next Generation of HTTP)的建议已经提出。
HTTP协议的主要特点可概括如下:
1.支持客户/服务器模式。
2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。
由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3.灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
4.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
5.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

2.2 HTTP协议的几个重要概念
1.连接(Connection):一个传输层的实际环流,它是建立在两个相互通讯的应用程序之间。
2.消息(Message):HTTP通讯的基本单位,包括一个结构化的八元组序列并通过连接传输。
3.请求(Request):一个从客户端到服务器的请求信息包括应用于资源的方法、资源的标识符和协议的版本号
4.响应(Response):一个从服务器返回的信息包括HTTP协议的版本号、请求的状态(例如“成功”或“没找到”)和文档的MIME类型。
5.资源(Resource):由URI标识的网络数据对象或服务。
6.实体(Entity):数据资源或来自服务资源的回映的一种特殊表示方法,它可能被包围在一个请求或响应信息中。一个实体包括实体头信息和实体的本身内容。
7.客户机(Client):一个为发送请求目的而建立连接的应用程序。
8.用户代理(User agent):初始化一个请求的客户机。它们是浏览器、编辑器或其它用户工具。
9.服务器(Server):一个接受连接并对请求返回信息的应用程序。
10.源服务器(Origin server):是一个给定资源可以在其上驻留或被创建的服务器。
11.代理(Proxy):一个中间程序,它可以充当一个服务器,也可以充当一个客户机,为其它客户机建立请求。请求是通过可能的翻译在内部或经过传递到其它的服务器中。一个代理在发送请求信息之前,必须解释并且如果可能重写它。
代理经常作为通过防火墙的客户机端的门户,代理还可以作为一个帮助应用来通过协议处理没有被用户代理完成的请求。
12.网关(Gateway):一个作为其它服务器中间媒介的服务器。与代理不同的是,网关接受请求就好象对被请求的资源来说它就是源服务器;发出请求的客户机并没有意识到它在同网关打交道。
网关经常作为通过防火墙的服务器端的门户,网关还可以作为一个协议翻译器以便存取那些存储在非HTTP系统中的资源。
13.通道(Tunnel):是作为两个连接中继的中介程序。一旦激活,通道便被认为不属于HTTP通讯,尽管通道可能是被一个HTTP请求初始化的。当被中继的连接两端关闭时,通道便消失。当一个门户(Portal)必须存在或中介(Intermediary)不能解释中继的通讯时通道被经常使用。
14.缓存(Cache):反应信息的局域存储。

2.3 HTTP协议的运作方式
HTTP协议是基于请求/响应范式的。一个客户机与服务器建立连接后,发送一个请求给服务器,请求方式的格式为,统一资源标识符、协议版本号,后边是 MIME信息包括请求修饰符、客户机信息和可能的内容。服务器接到请求后,给予相应的响应信息,其格式为一个状态行包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
许多HTTP通讯是由一个用户代理初始化的并且包括一个申请在源服务器上资源的请求。最简单的情况可能是在用户代理(UA)和源服务器(O)之间通过一个单独的连接来完成(见图2-1)。
图2-1
当一个或多个中介出现在请求/响应链中时,情况就变得复杂一些。中介由三种:代理(Proxy)、网关(Gateway)和通道(Tunnel)。一个代理根据URI的绝对格式来接受请求,重写全部或部分消息,通过URI的标识把已格式化过的请求发送到服务器。网关是一个接收代理,作为一些其它服务器的上层,并且如果必须的话,可以把请求翻译给下层的服务器协议。一个通道作为不改变消息的两个连接之间的中继点。当通讯需要通过一个中介(例如:防火墙等)或者是中介不能识别消息的内容时,通道经常被使用。 图2-2
上面的图2-2表明了在用户代理(UA)和源服务器(O)之间有三个中介(A,B和C)。一个通过整个链的请求或响应消息必须经过四个连接段。这个区别是重要的,因为一些HTTP通讯选择可能应用于最近的连接、没有通道的邻居,应用于链的终点或应用于沿链的所有连接。尽管图2-2是线性的,每个参与者都可能从事多重的、并发的通讯。例如,B可能从许多客户机接收请求而不通过A,并且/或者不通过C把请求送到A,在同时它还可能处理A的请求。
任何针对不作为通道的汇聚可能为处理请求启用一个内部缓存。缓存的效果是请求/响应链被缩短,条件是沿链的参与者之一具有一个缓存的响应作用于那个请求。下图说明结果链,其条件是针对一个未被UA或A加缓存的请求,B有一个经过C来自O的一个前期响应的缓存拷贝。
图2-3
在Internet上,HTTP通讯通常发生在TCP/IP连接之上。缺省端口是TCP 80,但其它的端口也是可用的。但这并不预示着HTTP协议在Internet或其它网络的其它协议之上才能完成。HTTP只预示着一个可靠的传输。
以上简要介绍了HTTP协议的宏观运作方式,下面介绍一下HTTP协议的内部操作过程。
首先,简单介绍基于HTTP协议的客户/服务器模式的信息交换过程,如图2-4所示,它分四个过程,建立连接、发送请求信息、发送响应信息、关闭连接。
图2-4
在WWW中,“客户”与“服务器”是一个相对的概念,只存在于一个特定的连接期间,即在某个连接中的客户在另一个连接中可能作为服务器。WWW服务器运行时,一直在TCP80端口(WWW的缺省端口)监听,等待连接的出现。
下面,讨论HTTP协议下客户/服务器模式中信息交换的实现。 1.建立连接连接的建立是通过申请套接字(Socket)实现的。客户打开一个套接字并把它约束在一个端口上,如果成功,就相当于建立了一个虚拟文件。以后就可以在该虚拟文件上写数据并通过网络向外传送。
2.发送请求
打开一个连接后,客户机把请求消息送到服务器的停留端口上,完成提出请求动作。
HTTP/1.0 请求消息的格式为:
请求消息=请求行(通用信息|请求头|实体头) CRLF[实体内容]
请求 行=方法 请求URL HTTP版本号 CRLF
方 法=GET|HEAD|POST|扩展方法
U R L=协议名称+宿主名+目录与文件名
请求行中的方法描述指定资源中应该执行的动作,常用的方法有GET、HEAD和POST。不同的请求对象对应GET的结果是不同的,对应关系如下:
对象 GET的结果
文件 文件的内容
程序 该程序的执行结果
数据库查询 查询结果
HEAD——要求服务器查找某对象的元信息,而不是对象本身。
POST——从客户机向服务器传送数据,在要求服务器和CGI做进一步处理时会用到POST方法。POST主要用于发送HTML文本中FORM的内容,让CGI程序处理。
一个请求的例子为:
GET http://networking.zju.edu.cn/zju/index.htm HTTP/1.0
头信息又称为元信息,即信息的信息,利用元信息可以实现有条件的请求或应答 。
请求头——告诉服务器怎样解释本次请求,主要包括用户可以接受的数据类型、压缩方法和语言等。
实体头——实体信息类型、长度、压缩方法、最后一次修改时间、数据有效期等。
实体——请求或应答对象本身。
3.发送响应
服务器在处理完客户的请求之后,要向客户机发送响应消息。
HTTP/1.0的响应消息格式如下:
响应消息=状态行(通用信息头|响应头|实体头) CRLF 〔实体内容〕
状 态 行=HTTP版本号 状态码 原因叙述
状态码表示响应类型
1×× 保留
2×× 表示请求成功地接收
3×× 为完成请求客户需进一步细化请求
4×× 客户错误
5×× 服务器错误
响应头的信息包括:服务程序名,通知客户请求的URL需要认证,请求的资源何时能使用。
4.关闭连接
客户和服务器双方都可以通过关闭套接字来结束TCP/IP对话

文章来源:http://meymz.blog.163.com/blog/static/38120921200771010744568
posted @ 2007-08-13 20:18 ymz 阅读(45) | 评论 (0) | 编辑


[导入]超文本传输协议HTTP
用于支持WWW浏览的网络协议为HTTP,这是一种最基本的客户机/服务器的访问协议。浏览器向服务器发送请求,而服务器回应相应的网页。HTTP协议从1990年开始出现,发展到当前的HTTP1.1标准,已经有了相当多的扩展,然而其最基本的实现是非常简单的,服务器需要进行的额外处理相当少,这也是为什么Web服务器软件如此众多的原因之一。


请求方法

通常,HTTP协议使用端口80来提供客户访问,因此也可以使用其他的网络软件,如telnet,模拟客户向服务器发送请求,来查看HTTP的传输方式。

$telnetwebserver80
Trying192.168.0.1...
Connectedtowebserver.
Escapecharacteris'^]'.
GET/index.html

 

  当telnet显示了Connect等信息建立了连接之后,服务器就等待使用者输入请求,而不进行任何提示。上例中,使用者输入GET/index.html指令,则服务器立即将相应的网页返回,然后关闭连接。

客户程序向服务器发送的请求可以有不同的类型,这样服务器可以根据不同的请求类型进行不同的处理。在HTTP1.0中,定义了三种最基本的请求类型, GET、POST和HEAD,这些请求方法的实现方式均与上例相同,客户程序用大写指令将请求发送给服务器,后面跟随具体的数据。

  GET请求最为常见,它后面跟随一个网页的位置,服务器接受请求并返回其请求的页面。除了页面位置作参数之外,请求还可以跟随协议的版本如HTTP/1.0等作为参数,以发送给服务器更多的信息。

  POST请求要求服务器接收大量的信息,除了POST后面跟随的参数之外,浏览器还会在后面持续发送数据,让服务器进行处理。通常,POST方法是和CGI程序分不开的,服务器应该启动一个CGI程序来处理POST发送来的数据。

HEAD请求在客户程序和服务器之间进行交流,而不会返回具体的文档。当使用GET和POST方法时,服务器最后都将结果文档返回给客户程序,浏览器将刷新显示。而HEAD请求则不同,它仅仅交流一些内部数据,这些数据不会影响浏览的过程。因此HEAD方法通常不单独使用,而是和其他的请求方法一起起到辅助作用。一些搜寻引擎使用的自动搜索机器人使用这个方法来获得网页的标志信息,或者进行安全认证时,使用这个方法来传递认证信息。

除了这三种最常见的访问方法之外,在HTTP1.1中还定义了更多的访问方法类型,如PUT,用于将网页放置到正确位置,DELETE用于删除相关文档等。这些方法并不常用,因而大部分Web服务器软件并没有实现他们。然而对于特定场合他们还是非常有用的,例如使用软件编辑网页时,网页编辑器可以使用这些方法,管理不同的网页。

  如果服务器不支持客户发送的请求方法,服务器将返回错误并立即关闭连接。


服务器对HTTP的处理方式

  HTTP协议的这种请求/回应的模式,使得服务器只能根据客户程序的请求发送回信息,这样的好处是客户具备很大的自由度,可以任意访问服务器上的信息。因此就存在多个客户同时访问一个服务器的问题。

在Unix下,由一个守护进程来监视来自客户程序的请求,当守护进程接受到一个请求时,就建立一个新的进程对请求进行处理。通常服务器能创建足够多的新进程来回应客户的请求,然而如果同时发送请求的客户太多,那么服务器就有可能出现超载的情况,创建进程的速度跟不上众多客户发送请求的速度,这样就造成了服务器对外表现反应迟缓。此外,为了提高用户使用浏览器时的性能,现代浏览器还支持并发的访问方式,浏览一个网页时同时建立多个连接,以迅速获得一个网页上的多个图标,这样能更快速完成整个网页的传输。但是对服务器来讲,更增加了瞬间负载。

  显然,造成这个问题的关键是服务器对HTTP协议的处理方式,一次请求就要建立一个连接,在网页上充满了多个较小的图象文件的时候,那么服务器和客户程序之间的大部分工作是用于建立连接,而真正用于传递数据的工作却很轻松。因此,更好的利用现有连接,减少建立连接的消耗,就需要能在一次连接中回应多个请求。在HTTP1.1中提供了这种持续连接的方式,而下一代HTTP协议:HTTP-NG更增加了有关会话控制、丰富的内容协商等方式的支持,来提供更高效率的连接。

除了针对每次请求都建立一个新进程的处理方式之外,HTTP守护进程也能使用其他的方式处理多个请求,例如使用多线程,或者使用异步方式在不同请求之间进行切换,就能在一个进程内处理多个请求。虽然比起建立新进程来讲,这样消耗的处理器资源略微减少,但是并不能从根本上消除并发访问带来的处理器资源不足的问题。一般使用线程和异步方式的程序较为复杂,不能很容易扩充对新特性的支持,并有可能因为程序内部要自己进行同步等原因也会造成资源消耗。使用这些方式,虽然对处理静态的网页有好处,但对于执行CGI程序,仍然要创建子进程进行处理。因此,大部分运行在Unix上的守护程序仍然使用多进程的方式,这种方式简单却有效。

  即使对于使用多进程方式进行处理的Web服务器,也有不同的处理方式。Unix系统中提供了超级服务器进程 inetd,因此简单的Web服务器可以使用inetd来启动真正的Web服务器。然而,inetd效率不高,使用inetd的服务器不能用作高负载的服务器系统,因此高负载的Web服务器,本身来监听客户连接请求,并负责启动子进程真正处理客户的请求。

  如果选择的服务器程序的确需要使用inetd来启动,可以选择与inetd功能相同,但效率更高的超级服务器进程tcpserver,它可以比inetd更高效的启动服务进程。

文章来源:http://meymz.blog.163.com/blog/static/38120921200771085543206
posted @ 2007-08-13 20:18 ymz 阅读(35) | 评论 (0) | 编辑


[导入]HTTP协议基础
HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送WWW方式的数据,关于HTTP协议的详细内容请参考RFC2616。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求,请求头包含请求的方法、URI、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。服务器以一个状态行作为响应,相应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息、实体元信息以及可能的实体内容。

通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。这两种类型的消息由一个起始行,一个或者多个头域,一个只是头域结束的空行和可选的消息体组成。HTTP的头域包括通用头,请求头,响应头和实体头四个部分。每个头域由一个域名,冒号(:)和域值三部分组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。

通用头域

通用头域包含请求和响应消息都支持的头域,通用头域包含Cache-Control、Connection、Date、Pragma、Transfer- Encoding、Upgrade、Via。对通用头域的扩展要求通讯双方都支持此扩展,如果存在不支持的通用头域,一般将会作为实体头域处理。下面简单介绍几个在UPnP消息中使用的通用头域。

Cache-Control头域

Cache-Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令包括no- cache、no-store、max-age、max-stale、min-fresh、only-if-cached,响应消息中的指令包括 public、private、no-cache、no-store、no-transform、must-revalidate、proxy- revalidate、max-age。各个消息中的指令含义如下:

Public指示响应可被任何缓存区缓存。
Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache指示请求或响应消息不能缓存
no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。

Date头域

Date头域表示消息发送的时间,时间的描述格式由rfc822定义。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时,换算成本地时间,需要知道用户所在的时区。

Pragma头域

Pragma头域用来包含实现特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1协议中,它的含义和Cache-Control:no-cache相同。

请求消息

请求消息的第一行为下面的格式:
MethodSPRequest- URISPHTTP-VersionCRLFMethod表示对于Request-URI完成的方法,这个字段是大小写敏感的,包括OPTIONS、 GET、HEAD、POST、PUT、DELETE、TRACE。方法GET和HEAD应该被所有的通用WEB服务器支持,其他所有方法的实现是可选的。 GET方法取回由Request-URI标识的信息。HEAD方法也是取回由Request-URI标识的信息,只是可以在响应时,不返回消息体。 POST方法可以请求服务器接收包含在请求中的实体信息,可以用于提交表单,向新闻组、BBS、邮件群组和数据库发送消息。

SP表示空格。Request-URI遵循URI格式,在此字段为星号(*)时,说明请求并不用于某个特定的资源地址,而是用于服务器本身。HTTP- Version表示支持的HTTP版本,例如为HTTP/1.1。CRLF表示换行回车符。请求头域允许客户端向服务器传递关于请求或者关于客户机的附加信息。请求头域可能包含下列字段Accept、Accept-Charset、Accept-Encoding、Accept-Language、 Authorization、From、Host、If-Modified-Since、If-Match、If-None-Match、If- Range、If-Range、If-Unmodified-Since、Max-Forwards、Proxy-Authorization、 Range、Referer、User-Agent。对请求头域的扩展要求通讯双方都支持,如果存在不支持的请求头域,一般将会作为实体头域处理。

典型的请求消息:


GEThttp://class/download.microtool.de:80/somedata.exe
Host:download.microtool.de
Accept:*/*
Pragma:no-cache
Cache-Control:no-cache
Referer:http://class/download.microtool.de/
User-Agent:Mozilla/4.04[en](Win95;I;Nav)
Range:bytes=554554-

上例第一行表示HTTP客户端(可能是浏览器、下载程序)通过GET方法获得指定URL下的文件。棕色的部分表示请求头域的信息,绿色的部分表示通用头部分。

Host头域

Host头域指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回。

Referer头域

Referer 头域允许客户端指定请求uri的源资源地址,这可以允许服务器生成回退链表,可用来登陆、优化cache等。他也允许废除的或错误的连接由于维护的目的被追踪。如果请求的uri没有自己的uri地址,Referer不能被发送。如果指定的是部分uri地址,则此地址应该是一个相对地址。

Range头域

Range头域可以请求实体的一个或者多个子范围。例如,

表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999

但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200(OK)。

User-Agent头域

User-Agent头域的内容包含发出请求的用户信息。

响应消息

响应消息的第一行为下面的格式:

HTTP-VersionSPStatus-CodeSPReason-PhraseCRLF
HTTP- Version表示支持的HTTP版本,例如为HTTP/1.1。Status-Code是一个三个数字的结果代码。Reason-Phrase给 Status-Code提供一个简单的文本描述。Status-Code主要用于机器自动识别,Reason-Phrase主要用于帮助用户理解。 Status-Code的第一个数字定义响应的类别,后两个数字没有分类的作用。第一个数字可能取5个不同的值:

1xx:信息响应类,表示接收到请求并且继续处理
2xx:处理成功响应类,表示动作被成功接收、理解和接受
3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理
4xx:客户端错误,客户请求包含语法错误或者是不能正确执行
5xx:服务端错误,服务器不能正确执行一个正确的请求

响应头域允许服务器传递不能放在状态行的附加信息,这些域主要描述服务器的信息和Request-URI进一步的信息。响应头域包含Age、 Location、Proxy-Authenticate、Public、Retry-After、Server、Vary、Warning、WWW- Authenticate。对响应头域的扩展要求通讯双方都支持,如果存在不支持的响应头域,一般将会作为实体头域处理。

典型的响应消息:


HTTP/1.0200OK
Date:Mon,31Dec200104:25:57GMT
Server:Apache/1.3.14(Unix)
Content-type:text/html
Last-modified:Tue,17Apr200106:46:28GMT
Etag:"a030f020ac7c01:1e9f"
Content-length:39725426
Content-range:bytes554554-40279979/40279980
上例第一行表示HTTP服务端响应一个GET方法。棕色的部分表示响应头域的信息,绿色的部分表示通用头部分,红色的部分表示实体头域的信息。

Location响应头

Location响应头用于重定向接收者到一个新URI地址。

Server响应头

Server响应头包含处理请求的原始服务器的软件信息。此域能包含多个产品标识和注释,产品标识一般按照重要性排序。

实体

请求消息和响应消息都可以包含实体信息,实体信息一般由实体头域和实体组成。实体头域包含关于实体的原信息,实体头包括Allow、Content- Base、Content-Encoding、Content-Language、Content-Length、Content-Location、 Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、 extension-header。extension-header允许客户端定义新的实体头,但是这些域可能无法未接受方识别。实体可以是一个经过编码的字节流,它的编码方式由Content-Encoding或Content-Type定义,它的长度由Content-Length或 Content-Range定义。

Content-Type实体头

Content-Type实体头用于向接收方指示实体的介质类型,指定HEAD方法送到接收方的实体介质类型,或GET方法发送的请求介质类型Content-Range实体头

Content-Range实体头用于指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式:

Content-Range:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-legth
例如,传送头500个字节次字段的形式:Content-Range:bytes0-499/1234如果一个http消息包含此节(例如,对范围请求的响应或对一系列范围的重叠请求),Content-Range表示传送的范围,Content-Length表示实际传送的字节数。

Last-modified实体头

Last-modified实体头指定服务器上保存内容的最后修订时间。

文章来源:http://meymz.blog.163.com/blog/static/38120921200771085352566
posted @ 2007-08-13 20:18 ymz 阅读(29) | 评论 (0) | 编辑


[导入]如何在子线程中操作窗体上的控件
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=626584


一般来说,直接在子线程中对窗体上的控件操作是会出现异常,这是由于子线程和运行窗体的线程是不同的空间,因此想要在子线程来操作窗体上的控件,是不可能简单的通过控件对象名来操作,但不是说不能进行操作,微软提供了Invoke的方法,其作用就是让子线程告诉窗体线程来完成相应的控件操作。

 

现在用一个用线程控制的进程条来说明,大致的步骤如下:

1.  创建Invoke函数,大致如下:

        /// <summary>

        /// Delegate function to be invoked by main thread

        /// </summary>

        private void InvokeFun()

        {

            if( prgBar.Value < 100 )

                prgBar.Value = prgBar.Value + 1;

        }

 

2.  子线程入口函数:

        /// <summary>

        /// Thread function interface

        /// </summary>

        private void ThreadFun()

        {

            //Create invoke method by specific function

            MethodInvoker mi = new MethodInvoker( this.InvokeFun );

 

            for( int i = 0; i < 100; i++ )

            {

                this.BeginInvoke( mi );

                Thread.Sleep( 100 );

            }

        }

 

3.  创建子线程:

            Thread thdProcess = new Thread( new ThreadStart( ThreadFun ) );

            thdProcess.Start();

 

       备注:

              using System.Threading;

              private System.Windows.Forms.ProgressBar prgBar;

如何弹出一个模式窗口来显示进度条

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=757351


最近看了好多人问这方面的问题,以前我也写过一篇blog,里面说了如何在子线程中控制进度条。但目前大多数环境,需要弹出模式窗口,来显示进度条,那么只需要在原先的基础上稍作修改即可。

 

首先是进度条窗体,需要在上面添加进度条,然后去掉ControlBox。除此外,还要增加一个方法,用来控制进度条的增加幅度,具体如下:

    /// <summary>

    /// Increase process bar

    /// </summary>

    /// <param name="nValue">the value increased</param>

    /// <returns></returns>

    public bool Increase( int nValue )

    {

        if( nValue > 0 )

        {

            if( prcBar.Value + nValue < prcBar.Maximum )

            {

                prcBar.Value += nValue;

                return true;

            }

            else

            {

                prcBar.Value = prcBar.Maximum;

                this.Close();

                return false;

            }

        }

        return false;

    }

 

接着就是主窗体了,如何进行操作了,首先需要定义两个私有成员,一个委托。其中一个私有成员是保存当前进度条窗体对象,另一个是保存委托方法(即增加进度条尺度),具体如下:

    private frmProcessBar myProcessBar = null;

    private delegate bool IncreaseHandle( int nValue );

    private IncreaseHandle myIncrease = null;

 

接着要在主窗体中提供函数来打开进度条窗体,如下:

    /// <summary>

    /// Open process bar window

    /// </summary>

    private void ShowProcessBar()

    {

        myProcessBar = new frmProcessBar();

 

        // Init increase event

        myIncrease = new IncreaseHandle( myProcessBar.Increase );

        myProcessBar.ShowDialog();

        myProcessBar = null;

    }

 

那么现在就可以开始创建线程来运行,具体如下:

    /// <summary>

    /// Sub thread function

    /// </summary>

    private void ThreadFun()

    {

        MethodInvoker mi = new MethodInvoker( ShowProcessBar );

        this.BeginInvoke( mi );

 

        Thread.Sleep( 1000 );//Sleep a while to show window

 

        bool blnIncreased = false;

        object objReturn = null;

        do

        {

            Thread.Sleep( 50 );

            objReturn = this.Invoke( this.myIncrease,

                new object[]{ 2 } );

            blnIncreased = (bool)objReturn ;

        }

        while( blnIncreased );

    }

      

       注意以上,在打开进度条窗体和增加进度条进度的时候,一个用的是BeginInvoke,一个是Invoke,这里的区别是BeginInvoke不需要等待方法运行完毕,而Invoke是要等待方法运行完毕。还有一点,此处用返回值来判断进度条是否到头了,如果需要有其他的控制,可以类似前面的方法来进行扩展。

 

启动线程,可以如下:

    Thread thdSub = new Thread( new ThreadStart( ThreadFun ) );

    thdSub.Start();

 

这样,一个用模式打开进度条窗体就做完了。

 


文章来源:http://meymz.blog.163.com/blog/static/3812092120077533856854
posted @ 2007-08-13 20:18 ymz 阅读(147) | 评论 (0) | 编辑


[导入]【转】Thread的问题
     摘要: Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1111267C#是一门支持多线程的语言,因此线程的使用也是比较常见的。由于线程的知识在Win32编程的时候已经说得过多,所以在.Net中很少介绍这部分(可能.Net不觉得这部分是它所特有的)。那么线程相关的问题大致有如下四类(这篇文章只讨论单线程、单线程与UI线程这两方面的问题)。问... 阅读全文
posted @ 2007-08-13 20:18 ymz 阅读(27) | 评论 (0) | 编辑


[导入]Socket编程与线程基础
     摘要: Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=886720Socket编程:Microsoft.Net Framework为应用程序访问Internet提供了分层的、可扩展的以及受管辖的网络服务,其名字空间System.Net和System.Net.Sockets包含丰富的类可以开发多种网络应用程序。.Net类采用的分层结构允许应... 阅读全文
posted @ 2007-08-13 20:18 ymz 阅读(57) | 评论 (0) | 编辑


[导入]【原】C#里获取客户端IP,端口号的简单示例
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;//为了IPEndPoint而添加的引用
using System.Net.Sockets;

namespace GetClntIP
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpListener tcpListener = new TcpListener(9000);//监听的端口号,可根据需要修改
            tcpListener.Start();

            //loop for listen
            while (true)
            {
                Socket sock = tcpListener.AcceptSocket();

                //服务器当前时间
                DateTime connTime = DateTime.Now;
                Console.WriteLine("The time is :"+connTime.ToString());

                System.Net.IPAddress ipAdd;
                int port;

                //获得连接当前用户的IP以及端口号
                ipAdd = (sock.RemoteEndPoint as IPEndPoint).Address;
                port = (sock.RemoteEndPoint as IPEndPoint).Port;

                Console.WriteLine("The client IP :"+ipAdd.ToString ());
                Console.WriteLine("The client port :" + port.ToString());
            }

            tcpListener.Stop();
        }
    }
}

 

[附]获取本地IP:

//获得本地局域网IP地址

IPAddress [] AddressList=Dns.GetHostByName(Dns.GetHostName()).AddressList;

if(AddressList.Length<1)

{

       retern "";

}

retern AddressList[0].ToString();//注意这里数组参数为0

 

//获得拨号动态分配IP地址

IPAdress [] AddressList=Dns.GetHostByName(Dns.GetHostName()).AddressList;

if(AddressList.Length<2)

{

        return "";

}

return AddressList[1].ToString();//注意这里数组参数为1


文章来源:http://meymz.blog.163.com/blog/static/381209212007711021129
posted @ 2007-08-13 20:18 ymz 阅读(1245) | 评论 (0) | 编辑


[导入][转]用C#实现基于TCP协议的网络通讯
  TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程。然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实现不是一天两天的功夫,所幸的是在.net framework环境下,我们不必要去追究TCP协议底层的实现,一样可以很方便的编写出基于TCP协议进行网络通讯的程序。

  要进行基于TCP协议的网络通讯,首先必须建立同远程主机的连接,连接地址通常包括两部分——主机名和端口,如www.yesky.com:80中,www.yesky.com就是主机名,80指主机的80端口,当然,主机名也可以用IP地址代替。当连接建立之后,就可以使用这个连接去发送和接收数据包,TCP协议的作用就是保证这些数据包能到达终点并且能按照正确的顺序组装起来。

  

  在.net framework的类库(Class Library)中,提供了两个用于TCP网络通讯的类,分别是TcpClient和TcpListener。由其英文意义显而易见,TcpClient类是基于TCP协议的客户端类,而TcpListener是服务器端,监听(Listen)客户端传来的连接请求。TcpClient类通过TCP协议与服务器进行通讯并获取信息,它的内部封装了一个Socket类的实例,这个Socket对象被用来使用TCP协议向服务器请求和获取数据。因为与远程主机的交互是以数据流的形式出现的,所以传输的数据可以使用.net framework中流处理技术读写。在我们下边的例子中,你可以看到使用NetworkStream类操作数据流的方法。

  

  在下面的例子中,我们将建立一个时间服务器,包括服务器端程序和客户端程序。服务器端监听客户端的连接请求,建立连接以后向客户端发送当前的系统时间。

  

  先运行服务器端程序,下面截图显示了服务器端程序运行的状况:

  然后运行客户端程序,客户端首先发送连接请求到服务器端,服务器端回应后发送当前时间到客户端。

  发送完成后,服务器端继续等待下一次连接。

  通过这个例子我们可以了解TcpClient类的基本用法,要使用这个类,必须使用System.Net.Socket命名空间,本例用到的三个命名空间如下:

  

  using System;

  using System.Net.Sockets;

  using System.Text;//从字节数组中获取字符串时使用该命名空间中的类

  

  首先讨论一下客户端程序,开始我们必须初始化一个TcpClient类的实例:

  

  TcpClient client = new TcpClient(hostName, portNum);

  

  然后使用TcpClient类的GetStream()方法获取数据流,并且用它初始化一个NetworkStream类的实例:

  

  NetworkStream ns = client.GetStream();

  

  注意,当使用主机名和端口号初始化TcpClient类的实例时,直到跟服务器建立了连接,这个实例才算真正建立,程序才能往下执行。如果因为网络不通,服务器不存在,服务器端口未开放等等原因而不能连接,程序将抛出异常并且中断执行。

  

  建立数据流之后,我们可以使用NetworkStream类的Read()方法从流中读取数据,使用Write()方法向流中写入数据。读取数据时,首先应该建立一个缓冲区,具体的说,就是建立一个byte型的数组用来存放从流中读取的数据。Read()方法的原型描述如下:

  

  public override int Read(in byte[] buffer,int offset,int size)

  

  buffer是缓冲数组,offset是数据(字节流)在缓冲数组中存放的开始位置,size是读取的字节数目,返回值是读取的字节数。在本例中,简单地使用该方法来读取服务器反馈的信息:

  

  byte[] bytes = new byte[1024];//建立缓冲区

  int bytesRead = ns.Read(bytes, 0, bytes.Length);//读取字节流

  

  然后显示到屏幕上:

  

  Console.WriteLine(Encoding.ASCII.GetString(bytes,0,bytesRead));

  

  最后不要忘记关闭连接:

  

  client.Close();

  

  下面是本例完整的程序清单:

  

  using System;

  using System.Net.Sockets;

  using System.Text;

  

  namespace TcpClientExample

  {

  public class TcpTimeClient

  {

  private const int portNum = 13;//服务器端口,可以随意修改

  private const string hostName = "127.0.0.1";//服务器地址,127.0.0.1指本机

  

  [STAThread]

  static void Main(string[] args)

  {

  try

  {

  Console.Write("Try to connect to "+hostName+":"+portNum.ToString()+"/r/n");

  TcpClient client = new TcpClient(hostName, portNum);

  NetworkStream ns = client.GetStream();

  byte[] bytes = new byte[1024];

  int bytesRead = ns.Read(bytes, 0, bytes.Length);

  

  Console.WriteLine(Encoding.ASCII.GetString(bytes,0,bytesRead));

  

  client.Close();

  Console.ReadLine();//由于是控制台程序,故为了清楚的看到结果,可以加上这句

  

  }

  catch (Exception e)

  {

  Console.WriteLine(e.ToString());

  }

  }

  }

  }

  

  上面这个例子清晰地演示了客户端程序的编写要点,下面我们讨论一下如何建立服务器程序。这个例子将使用TcpListener类,在13号端口监听,一旦有客户端连接,将立即向客户端发送当前服务器的时间信息。

  

  TcpListener的关键在于AcceptTcpClient()方法,该方法将检测端口是否有未处理的连接请求,如果有未处理的连接请求,该方法将使服务器同客户端建立连接,并且返回一个TcpClient对象,通过这个对象的GetStream方法建立同客户端通讯的数据流。事实上,TcpListener类还提供一个更为灵活的方法AcceptSocket(),当然灵活的代价是复杂,对于比较简单的程序,AcceptTcpClient()已经足够用了。此外,TcpListener类提供Start()方法开始监听,提供Stop()方法停止监听。

  

  首先我们使用端口初始化一个TcpListener实例,并且开始在13端口监听:

  

  private const int portNum = 13;

  TcpListener listener = new TcpListener(portNum);

  listener.Start();//开始监听

  

  如果有未处理的连接请求,使用AcceptTcpClient方法进行处理,并且获取数据流:

  

  TcpClient client = listener.AcceptTcpClient();

  NetworkStream ns = client.GetStream();

  

  然后,获取本机时间,并保存在字节数组中,使用NetworkStream.Write()方法写入数据流,然后客户端就可以通过Read()方法从数据流中获取这段信息:

  

  byte[] byteTime = Encoding.ASCII.GetBytes(DateTime.Now.ToString());

  ns.Write(byteTime, 0, byteTime.Length);

  ns.Close();//不要忘记关闭数据流和连接

  client.Close();

  

  服务器端程序完整的程序清单如下:

  

  using System;

  using System.Net.Sockets;

  using System.Text;

  

  

  namespace TimeServer

  {

  class TimeServer

  {

  private const int portNum = 13;

  

  [STAThread]

  static void Main(string[] args)

  {

  bool done = false;

  TcpListener listener = new TcpListener(portNum);

  listener.Start();

  while (!done)

  {

  Console.Write("Waiting for connection...");

  TcpClient client = listener.AcceptTcpClient();

  

  Console.WriteLine("Connection accepted.");

  NetworkStream ns = client.GetStream();

  

  byte[] byteTime = Encoding.ASCII.GetBytes(DateTime.Now.ToString());

  

  try

  {

  ns.Write(byteTime, 0, byteTime.Length);

  ns.Close();

  client.Close();

  }

  catch (Exception e)

  {

  Console.WriteLine(e.ToString());

  }

  }

  

  listener.Stop();

  }

  }

  }

  

  把上面两段程序分别编译运行,OK,我们已经用C#实现了基于TCP协议的网络通讯,怎么样?很简单吧!

  

  使用上面介绍的基本方法,我们可以很容易的编写出一些很有用的程序,如FTP,电子邮件收发,点对点即时通讯等等,你甚至可以自己编制一个QQ来!


文章来源:http://meymz.blog.163.com/blog/static/381209212007630853987
posted @ 2007-08-13 20:18 ymz 阅读(38) | 评论 (0) | 编辑


[导入]一些名词解释
IGS: International GNSS Service

EUREF:EUREF is the IAG Reference Frame Sub-Commission for Europe,EUREF deals with the definition, realization and maintenance of the European Reference Frame - the geodetic infrastructure for multinational projects requiring precise geo-referencing

IAG:国际地貌学家协会


文章来源:http://meymz.blog.163.com/blog/static/38120921200762765219948
posted @ 2007-08-13 20:18 ymz 阅读(12) | 评论 (0) | 编辑


[导入]C#属性
 

[注]转载于http://hi.baidu.com/x8online/blog/item/61839dd494e81701a08bb781.html
   


 

 

一。C#中属性概念:

       对于对象的用户,属性显示为字段,访问该属性需要完全相同的语法。对于类的实现者,属性是一个或两个代码块,表示一个 get 访问器和/或一个 set 访问器。当读取属性时,执行 get 访问器的代码块;当向属性分配一个新值时,执行 set 访问器的代码块。不具有 set 访问器的属性被视为只读属性。不具有 get 访问器的属性被视为只写属性。同时具有这两个访问器的属性是读写属性。

      与字段不同,属性不作为变量类分类,所以不能将属性作为  ref参数和 OUT参数传递。

二。属性在类模块内的声明语法:

      字段访问级别。属性的类型。属性的名称

     {

        get {}

        set {}

     }

     get 访问器:get 访问器体与方法相似,必须返回属性类型的值,执行get访问器相当于读取字段的值。当引用属性时,除非该属性为赋值目标,否则将调用 get 访问器以读取该属性的值

class Person

{   

       private string name;  // the name field   

       public string Name    // the Name property  

      {       

              get        {            return name;        }   

      }

}

Person p1 = new Person();

//...
System.Console.Write(p1.Name);  // the get accessor is invoked here

get访问器必须以return或throw语句终止。并且控制权不能离开访问器体

 

set访问器体:

 类似于返回类型void的方法。使用VALUE的隐式参数,此参数的类型是该属性的类型。如:

 

class Person

{

    private string name;  // the name field

    public string Name    // the Name property

    {

        get

        {

            return name;

        }

        set

        {

            name = value;

        }

    }

}

 set 访问器中,对局部变量声明使用隐式参数名称 value 是错误的。

三。说明

 

1.属性可被标记为public,private,protected,internal,或protected internal

2.可以使用static关键字声明静态属性,可以使的调用放随时使用该属性,即不存在类的实例也可以使用属性

3,可以使用vistual关键字把属性声明为虚属性,这样派生类可以使用overrid来重写属性的行为。

4。重写虚属性可以是sealed的,表示他对派生类不再是虚拟的。属性可以声明为abstract,这意味着在类中没有实现,派生类必须编写自己的实现

 
 


 


文章来源:http://meymz.blog.163.com/blog/static/3812092120076271075410
posted @ 2007-08-13 20:18 ymz 阅读(102) | 评论 (0) | 编辑


[导入]SOAP
[注]转自:http://baike.baidu.com/view/60663.htm

 

SOAP:简单对象访问协议

(SOAP:Simple Object Access Protocol)

简单对象访问协议(SOAP)是一种轻量的、简单的、基于 XML 的协议,它被设计成在 WEB 上交换结构化的和固化的信息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议( HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。

SOAP 包括三个部分:

SOAP 封装:它定义了一个框架 , 该框架描述了消息中的内容是什么,谁应当处理它以及它是可选的还是必须的。

SOAP 编码规则:它定义了一种序列化的机制,用于交换应用程序所定义的数据类型的实例。

SOAP RPC 表示:它定义了用于表示远程过程调用和应答的协定。

SOAP 消息基本上是从发送端到接收端的单向传输,但它们常常结合起来执行类似于请求 / 应答的模式。所有的 SOAP 消息都使用 XML 编码。一条 SOAP 消息就是一个包含有一个必需的 SOAP 的封装包,一个可选的 SOAP 标头和一个必需的 SOAP 体块的 XML 文档。

把 SOAP 绑定到 HTTP 提供了同时利用 SOAP 的样式和分散的灵活性的特点以及 HTTP 的丰富的特征库的优点。在 HTTP 上传送 SOAP 并不是说 SOAP 会覆盖现有的 HTTP 语义,而是 HTTP 上的 SOAP 语义会自然的映射到 HTTP 语义。在使用 HTTP 作为协议绑定的场合中, RPC 请求映射到 HTTP 请求上,而 RPC 应答映射到 HTTP 应答。然而,在 RPC 上使用 SOAP 并不仅限于 HTTP 协议绑定。

协议结构

SOAP 消息格式:

SOAP 标头

<SOAP-ENV: Envelope

Attributes>

<SOAP-ENV:Body

Attributes

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>
目前主要在web服务中运用。


文章来源:http://meymz.blog.163.com/blog/static/38120921200762643622287
posted @ 2007-08-13 20:18 ymz 阅读(15) | 评论 (0) | 编辑


[导入]C#中调试的常用方法
[注]转自http://topic.csdn.net/t/20021106/16/1153909.html#

 

调试的方法很多,而且因人而异.我说说我的常用的调试方法.  
   
  1.用MessageBox.Show()把想要的东西打出来.  
  2.想楼上诸位所说,用工具自带的辅助调试期.  
  3.屏蔽掉一部分代码,确定范围.  
  4.抓异常,看是什么原因.  
  (稍麻烦点,但是系统大了绝对有用)  
  5.使用Assert,trace类.  
  6.写入日值.  

 


 

Trackback:http://publishblog.blogchina.com/blog/tb.b?diaryID=4055634

 

            不用visual studio.net调试c#程序

                                         

1.用csc.exe编译c#源文件,命令如下:

csc /out:d:/c#/Test.exe /target:exe /debug+ /recurse:d:/c#/Test.cs

注意,要有参数/debug+;

2.启动D:/Program Files/Microsoft.NET/SDK/v1.1/GuiDebug/DbgCLR.exe,当然,目录根据你的.net framework SDK的安装位置不同 而不同;

3.文件-->打开-->文件,选择d:/c#/Test.cs,调试-->要调试的程序:d:/c#/Test.exe;

4.在Test.cs中设置断点,启动~~~~~~~~~

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值