1. 程式人生 > >windows網路程式設計之併發式伺服器

windows網路程式設計之併發式伺服器

SocketFrame.h:

#pragma once
#include "winsock2.h"
#include "stdio.h"
#pragma comment(lib,"ws2_32.lib")
//定義網路框架程式中所需的巨集
#define TRUE			1
#define FALSE			0
#define	MAXLINE		    200	    // max text line length 
#define	DEFAULT_NAMELEN 100 //預設的名字長度
//首部結構定義
typedef struct tagIPHDR
{
	UCHAR hdr_len :4;  // length of the header
    UCHAR version :4;  // version of IP
	UCHAR	TOS;	   // Type of service
	USHORT	TotLen;	   // Total length
	USHORT	ID;		   // Identification
	USHORT	FlagOff;   // Flags and fragment offset
	UCHAR	TTL;	   // Time-to-live
	UCHAR	Protocol;  // Protocol
	USHORT	Checksum;  // Checksum
	ULONG IPSrc;	   // Internet address, source
	ULONG IPDst;	   // Internet address, destination
} IPHDR, *PIPHDR;

typedef struct tagUDPHDR	//UDP頭定義
{
	USHORT src_portno; 
	USHORT dst_portno; 
	USHORT udp_length; 
	USHORT udp_checksum; 
} UDPHDR,*PUDPHDR;

typedef struct tagTCPHDR		//TCP首部定義
{
	USHORT  sport;            //Source port 
    USHORT  dport;            //Destination port 
    ULONG   seq;              //Sequence number 
    ULONG   ack;              //Ack number 
    BYTE    hlen;             // TCP header len (num of bytes << 2) 
    BYTE    flags;            // Option flags 
    USHORT  window;           // Flow control credit (num of bytes) 
    USHORT  check;            // Checksum 
    USHORT  urgent;           // Urgent data pointer 
} TCPHDR,*PTCPHDR;
//TCP標誌欄位定義
#define TFIN        0x01    // Option flags: no more data 
#define TSYN        0x02    // sync sequence nums 
#define TRST        0x04    // reset connection 
#define TPUSH       0x08    // push buffered data 
#define TACK        0x10    // acknowledgement 
#define TURGE       0x20    // urgent 


typedef struct tagFHDR		//UDP偽首部定義
{
	ULONG IPSrc;	
	ULONG IPDst;	
	UCHAR zero;
	UCHAR protocol;
	USHORT udp_length;
} FHDR,*PFHDR;


//ICMP資料報頭
typedef struct tagICMPHDR
{
    UCHAR type;  //8位型別
    UCHAR code;  //8位程式碼
    USHORT cksum;  //16位校驗和
    USHORT id;   //16位識別符號
    USHORT seq;  //16位序列號
} ICMPHDR,*PICMPHDR;

#pragma pack()
//ICMP型別欄位
const BYTE ICMP_ECHO_REQUEST = 8; //請求回顯
const BYTE ICMP_ECHO_REPLY  = 0; //回顯應答
const BYTE ICMP_TIMEOUT   = 11; //傳輸超時
const DWORD DEF_ICMP_TIMEOUT = 3000; //預設超時時間,單位ms
const int DEF_ICMP_DATA_SIZE = 32; //預設ICMP資料部分長度
const int MAX_ICMP_PACKET_SIZE = 1024; //最大ICMP資料報的大小
const int DEF_MAX_HOP = 30;    //最大跳站數
class CSocketFrame
{
public:
	CSocketFrame(void);
	~CSocketFrame(void);
	int set_address(char * hname, char * sname, struct sockaddr_in * sap, char * protocol);
	int start_up(void);
	int clean_up(void);
	int quit(SOCKET s);
	USHORT check_sum(USHORT *pchBuffer, int iSize);
	SOCKET tcp_server( char *hname, char *sname );
	SOCKET udp_server( char *hname, char *sname );
	int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen);
	int recvvl(SOCKET s, char * recvbuf, unsigned int recvbuflen);
	SOCKET tcp_client( char *hname, char *sname );
	SOCKET udp_client( char *hname, char *sname, BOOL flag);
    SOCKET raw_socket( BOOL bSendflag, BOOL bRecvflag, int iProtocol, sockaddr_in *pLocalIP);
};
SocketFrame.cpp:
#include "StdAfx.h"
#include "SocketFrame.h"
#include "ws2tcpip.h"
#include "mstcpip.h"
CSocketFrame::CSocketFrame(void)
{
}

CSocketFrame::~CSocketFrame(void)
{
}
/********************************************************
函式名:set_address
輸入引數:char * hname:主機名 or 點分十進位制表示的IP地址
          char * sname:埠號
		  struct sockaddr_in * sap:以sockaddr_in結構儲存的地址(輸出引數)
          char * protocol:字串形式描述的協議型別,如"tcp"
輸出引數:0表示成功,-1表示失敗。
功能:根據給定的主機名或點分十進位制表示的IP地址獲得以sockaddr_in結構儲存的地址
*********************************************************/
int CSocketFrame::set_address(char * hname, char * sname, struct sockaddr_in * sap, char * protocol)
{
	struct servent *sp;
	struct hostent *hp;
	char *endptr;
	unsigned short port;
	unsigned long ulAddr = INADDR_NONE;


    //對地址結構socketaddr_in初始化為0,並設定地址族為AF_INET
	memset( sap,0, sizeof( *sap ) );
	sap->sin_family = AF_INET;
	
	if ( hname != NULL )
	{
		//如果hname不為空,假定給出的hname為點分十進位制表示的數字地址,轉換地址為sockaddr_in型別
		ulAddr = inet_addr(hname);
		if ( ulAddr == INADDR_NONE || ulAddr == INADDR_ANY) {
			//printf("inet_addr 函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
			//呼叫錯誤,表明給出的是主機名,呼叫gethostbyname獲得主機地址
			hp = gethostbyname( hname );
			if ( hp == NULL ) {
				printf("未知的主機名,錯誤號: %d\n", WSAGetLastError());
				return -1;
			}
			sap->sin_addr = *( struct in_addr * )hp->h_addr;
        }      
		else
			sap->sin_addr.S_un.S_addr=ulAddr;		
	}
	else
		//如果呼叫者沒有指定一個主機名或地址,則設定地址為通配地址INADDR_ANY
		sap->sin_addr.s_addr = htonl( INADDR_ANY );
	//嘗試轉換sname為一個整數
	port = (unsigned short )strtol( sname, &endptr, 0 );
	if ( *endptr == '\0' )
	{
		//如果成功則轉換為網路位元組順序
		sap->sin_port = htons( port );
	}
	else
	{
		//如果失敗,則假定是一個服務名稱,通過呼叫getservbyname獲得埠號
		sp = getservbyname( sname, protocol );
		if ( sp == NULL ) {
			printf("未知的服務,錯誤號: %d\n", WSAGetLastError());
			return -1;
		}
		sap->sin_port = sp->s_port;
	}
	return 0;
}

/********************************************************
函式名:start_up
輸入引數:無
輸出引數:0:成功,-1:失敗
功能:初始化Windows Sockets DLL,協商版本號
*********************************************************/
int CSocketFrame::start_up(void)
{
	WORD wVersionRequested;
    WSADATA wsaData;
    int iResult;

    // 使用 MAKEWORD(lowbyte, highbyte) 巨集,在Windef.h 中宣告
    wVersionRequested = MAKEWORD(2, 2);

    iResult = WSAStartup(wVersionRequested, &wsaData);
    if (iResult != 0) {
        //告知使用者無法找到合適可用的Winsock DLL
        printf("WSAStartup 函式呼叫錯誤,錯誤號: %d\n",  WSAGetLastError());
        return -1;
    }

    // 確認WinSock Dll支援版本2.2
    // 注意,如果DLL支援的版本比2.2更高,根據使用者呼叫前的需求,仍然返回2.2版本號,儲存於wsaData.wVersion

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        // 告知使用者無法找到可用的WinSock DLL.                              
        printf("無法找到可用的Winsock.dll版本\n");
        WSACleanup();
        return -1;
    }

	return 0;
}
/********************************************************
函式名:clean_up
輸入引數:無
輸出引數:0:成功,-1:失敗
功能:終止Windows Sockets DLL的使用,釋放資源 
*********************************************************/
int CSocketFrame::clean_up(void)
{
	int iResult;
	iResult = WSACleanup();
    if (iResult == SOCKET_ERROR) {
        // WSACleanup呼叫失敗                  
        printf("WSACleanup 函式呼叫錯誤,錯誤號: %d\n",  WSAGetLastError());
        return -1;
    }

	return 0;
}
/********************************************************
函式名:quit
輸入引數:SOCKET s:伺服器的連線套接字
輸出引數:0:成功,-1:失敗
功能:關閉套接字,釋放dll
*********************************************************/
int CSocketFrame::quit(SOCKET s)
{
    int iResult=0;
    iResult = closesocket(s);
    if (iResult == SOCKET_ERROR){
        printf("closesocket 函式呼叫錯誤,錯誤號:%d\n", WSAGetLastError());
        return -1;
    }
    iResult = clean_up();
    return iResult;
}
/********************************************************
函式名:tcp_server
輸入引數:char * hname:伺服器主機名 or 點分十進位制表示的IP地址
          char * sname:服務埠號
輸出引數:建立伺服器端流式套接字並配置,-1:表示失敗
功能:建立流式套接字,根據使用者輸入的地址和埠號,繫結套接字的服務地址
      將其轉換為監聽狀態
*********************************************************/
SOCKET CSocketFrame::tcp_server( char *hname, char *sname )
{
	sockaddr_in local;
	SOCKET ListenSocket;
	const int on = 1;
	int iResult = 0;

    //為伺服器的本地地址local設定使用者輸入的IP和埠號
	if (set_address( hname, sname, &local, "tcp" ) !=0 )
		return -1;
	
	//建立套接字
	ListenSocket = socket( AF_INET, SOCK_STREAM, 0 );
	if (ListenSocket == INVALID_SOCKET) {
        printf("socket 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

	//設定伺服器地址可重用選項
	iResult = setsockopt( ListenSocket, SOL_SOCKET, SO_REUSEADDR, ( char * )&on, sizeof( on ));
	if ( iResult == SOCKET_ERROR){
		printf("setsockopt函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
        quit(ListenSocket);
        return -1;
    }

    //繫結伺服器地址
	iResult = bind( ListenSocket, (struct sockaddr *) & local, sizeof (local));
    if (iResult == SOCKET_ERROR) {
        printf("bind 函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
        quit(ListenSocket);
        return -1;
    }

	//設定伺服器為監聽狀態,監聽佇列長度為NLISTEN
    iResult = listen(ListenSocket, SOMAXCONN);
	if (iResult == SOCKET_ERROR){
		printf("listen 函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
		quit(ListenSocket);
		return -1;
    }

	return ListenSocket;
}
/********************************************************
函式名:tcp_client
輸入引數:char * hname:伺服器主機名 or 點分十進位制表示的IP地址
          char * sname:服務埠號
輸出引數:建立客戶端流式套接字,-1:表示失敗
功能:建立流式套接字,根據使用者輸入的地址和埠號,向服務地址
      請求建立連線
*********************************************************/
SOCKET CSocketFrame::tcp_client( char *hname, char *sname )
{
	struct sockaddr_in peer;
	SOCKET ClientSocket;
	int iResult = 0;

	//指明伺服器的地址peer為使用者輸入的IP和埠號
	if (set_address( hname, sname, &peer, "tcp" ) !=0 )
		return -1;

	//建立套接字
	ClientSocket = socket( AF_INET, SOCK_STREAM, 0 );
	if (ClientSocket == INVALID_SOCKET) {
        printf("socket 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

    //請求向伺服器建立連線
	iResult =connect( ClientSocket, ( struct sockaddr * )&peer, sizeof( peer ) );
	if (iResult == SOCKET_ERROR){
		printf("connect 函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
		quit(ClientSocket);
		return -1;
    }

	return ClientSocket;
}
/********************************************************
函式名:recvn
輸入引數:SOCKET s:伺服器的連線套接字
          char * recvbuf:存放接收到資料的緩衝區 
		  int fixedlen:固定的預接收資料長度
輸出引數:>0:實際接收到的位元組數,-1:失敗
功能:在流式套接字中接收固定長度的資料
********************************************************/
int CSocketFrame::recvn(SOCKET s, char * recvbuf, unsigned int fixedlen)
{
	int iResult;//儲存單次recv操作的返回值
	int cnt;//用於統計相對於固定長度,剩餘多少位元組尚未接收
	cnt = fixedlen;
	while ( cnt > 0 )
	{
        iResult = recv(s, recvbuf, cnt, 0);
        if ( iResult < 0 )
		{
			//資料接收出現錯誤,返回失敗
			printf("接收發生錯誤: %d\n", WSAGetLastError());
		    return -1;
		}
	    if ( iResult == 0 )
		{
			//對方關閉連線,返回已接收到的小於fixedlen的位元組數
	        printf("連線關閉\n");
			return fixedlen - cnt;
		}
	    //printf("接收到的位元組數: %d\n", iResult);
		//接收快取指標向後移動
		recvbuf +=iResult;
		//更新cnt值
		cnt -=iResult;         
	}
	return fixedlen;
}
/********************************************************
函式名:recvvl
輸入引數:SOCKET s:伺服器的連線套接字
          char * recvbuf:存放接收到資料的緩衝區 
		  int recvbuflen:接收緩衝區長度
輸出引數:>0:實際接收到的位元組數,-1:失敗,0:連線關閉
功能:在流式套接字中接收可變長度的資料
********************************************************/
int CSocketFrame::recvvl(SOCKET s, char * recvbuf, unsigned int recvbuflen)
{
	int iResult;//儲存單次recv操作的返回值
	unsigned int reclen; //用於儲存報文頭部儲存的長度資訊
	//獲取接收報文長度資訊
    iResult = recvn(s, ( char * )&reclen, sizeof( unsigned int ));
	if ( iResult !=sizeof ( unsigned int ))
	{
		//如果長度欄位在接收時沒有返回一個整型資料就返回0(連線關閉)或-1(發生錯誤)
		if ( iResult == -1 )
		{
	         printf("接收發生錯誤: %d\n", WSAGetLastError());
		     return -1;
		}
		else
		{
			 printf("連線關閉\n");
             return 0;
		}
	}
	//轉換網路位元組順序到主機位元組順序
	reclen = ntohl( reclen );
	if ( reclen > recvbuflen )
	{
		//如果recvbuf沒有足夠的空間儲存變長訊息,則接收該訊息並丟棄,返回錯誤
		while ( reclen > 0)
		{
			iResult = recvn( s, recvbuf, recvbuflen );
			if ( iResult != recvbuflen )
			{
				//如果變長訊息在接收時沒有返回足夠的資料就返回0(連線關閉)或-1(發生錯誤)
				if ( iResult == -1 )
				{
					 printf("接收發生錯誤: %d\n", WSAGetLastError());
					 return -1;
				}
				else
				{
					 printf("連線關閉\n");
					 return 0;
				}
			}
			reclen -= recvbuflen;
			//處理最後一段資料長度
			if ( reclen < recvbuflen )
				recvbuflen = reclen;
		}
		printf("可變長度的訊息超出預分配的接收快取\r\n");
		return -1;
	}
	//接收可變長訊息
	iResult = recvn( s, recvbuf, reclen );
	if ( iResult != reclen )
	{
        //如果訊息在接收時沒有返回足夠的資料就返回0(連線關閉)或-1(發生錯誤)
		if ( iResult == -1 )
		{
	         printf("接收發生錯誤: %d\n", WSAGetLastError());
		     return -1;
		}
		else
		{
			 printf("連線關閉\n");
             return 0;
		}
	}
	return iResult;
}
/********************************************************
函式名:udp_server
輸入引數:char * hname:伺服器主機名 or 點分十進位制表示的IP地址
          char * sname:服務埠號
輸出引數:建立伺服器端流式套接字並配置,-1:表示失敗
功能:建立流式套接字,根據使用者輸入的地址和埠號,繫結套接字的服務地址
      將其轉換為監聽狀態
*********************************************************/
SOCKET CSocketFrame::udp_server( char *hname, char *sname )
{
	sockaddr_in local;
	SOCKET ServerSocket;
	const int on = 1;
	int iResult = 0;

    //為伺服器的本地地址local設定使用者輸入的IP和埠號
	if (set_address( hname, sname, &local, "udp" ) !=0 )
		return -1;
	
	//建立套接字
	ServerSocket = socket( AF_INET, SOCK_DGRAM, 0 );
	if (ServerSocket == INVALID_SOCKET) {
        printf("socket 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

	//設定伺服器地址可重用選項
	iResult = setsockopt( ServerSocket, SOL_SOCKET, SO_REUSEADDR, ( char * )&on, sizeof( on ));
	if ( iResult == SOCKET_ERROR){
		printf("setsockopt函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
        quit(ServerSocket);
        return -1;
    }

    //繫結伺服器地址
	iResult = bind( ServerSocket, (struct sockaddr *) & local, sizeof (local));
    if (iResult == SOCKET_ERROR) {
        printf("bind 函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
        quit(ServerSocket);
        return -1;
    }

	return ServerSocket;
}
/********************************************************
函式名:udp_client
輸入引數:char * hname:伺服器主機名 or 點分十進位制表示的IP地址
          char * sname:服務埠號
		  BOOL flag:工作模式標識,true表示連線模式,false表示非連線模式
輸出引數:建立客戶端流式套接字,-1:表示失敗
功能:建立資料報套接字,根據使用者輸入的地址和埠號
*********************************************************/
SOCKET CSocketFrame::udp_client( char *hname, char *sname, BOOL flag)
{
	struct sockaddr_in peer;
	SOCKET ClientSocket;
	int iResult = -1;

	//指明伺服器的地址peer為使用者輸入的IP和埠號
	if (set_address( hname, sname, &peer, "udp" ) ==1 )
		return -1;

	//建立套接字
	ClientSocket = socket( AF_INET, SOCK_DGRAM, 0 );
	if (ClientSocket == INVALID_SOCKET) {
        printf("socket 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

	if( flag == TRUE)
	{
		//連線模式
		//請求向伺服器建立連線
	    iResult =connect( ClientSocket, ( struct sockaddr * )&peer, sizeof( peer ) );
	    if (iResult == SOCKET_ERROR){
		    printf("connect 函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
		    quit(ClientSocket);
		    return -1;
		}
    }
	
	return ClientSocket;
}
/********************************************************
函式名:check_sum
輸入引數:
		  USHORT *pchBuffer:待計算校驗和的緩衝區
		  int iSize:待計算校驗和緩衝區長度
輸出引數:校驗和
功能:計算校驗和
*********************************************************/
USHORT CSocketFrame::check_sum(USHORT *pchBuffer, int iSize)
{
    unsigned long ulCksum=0;
    while (iSize > 1) 
    {
        ulCksum += *pchBuffer++;
        iSize -= sizeof(USHORT);
    }
    if (iSize) 
    {
        ulCksum += *(UCHAR*)pchBuffer;
    }
    ulCksum = (ulCksum >> 16) + (ulCksum & 0xffff);
    ulCksum += (ulCksum >>16);
    return (USHORT)(~ulCksum);
}
/********************************************************
函式名:raw_socket
輸入引數:
		  BOOL bSendflag:首部控制選項
		  BOOL bRecvflag:接收控制選項
		  int iProtocol:協議設定,具體內容參考MSDN對協議的定義,如#define IPPROTO_IP 0
		  sockaddr_in *pLocalIP:指向本地IP地址的指標,返回引數,如果存在多個介面地址,獲取使用者選擇的本地地址
輸出引數:建立客戶端流式套接字,-1:表示失敗
功能:建立資料報套接字,根據使用者輸入的地址和埠號
*********************************************************/
SOCKET CSocketFrame::raw_socket( BOOL bSendflag, BOOL bRecvflag, int iProtocol, sockaddr_in *pLocalIP)
{
	SOCKET RawSocket;
	int iResult = 0;
	struct hostent *local;
    char HostName[DEFAULT_NAMELEN];
	struct in_addr addr;
    int in=0,i=0;
    DWORD dwBufferLen[10];
 	DWORD Optval= 1 ;
    DWORD dwBytesReturned = 0 ;


	//建立套接字
	RawSocket = socket( AF_INET, SOCK_RAW, iProtocol );
	if (RawSocket == INVALID_SOCKET) {
        printf("socket 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

	if( bSendflag == TRUE)
	{
		//設定IP_HDRINCL表示要構造IP頭,需#include "ws2tcpip.h"
	    iResult = setsockopt(RawSocket,IPPROTO_IP,IP_HDRINCL,(char*)&bSendflag,sizeof(bSendflag));
		if (iResult == SOCKET_ERROR){
		    printf("setsockopt 函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
		    quit(RawSocket);
		    return -1;
		}
    }
	if( bRecvflag == TRUE)
	{
		//設定I/O控制選項,接收全部IP包
		//獲取本機名稱
	    memset( HostName, 0, DEFAULT_NAMELEN);
	    iResult = gethostname( HostName, sizeof(HostName));
	    if ( iResult ==SOCKET_ERROR) {
	      printf("gethostname 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
	      quit(RawSocket);
	      return -1;
	    }
	
	    //獲取本機可用IP
	    local = gethostbyname( HostName);
	    printf ("\n本機可用的IP地址為:\n");
	    if( local ==NULL)
	    {
	        printf("gethostbyname 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
	        quit(RawSocket);
	        return -1;
	    }
	    while (local->h_addr_list[i] != 0) {
	        addr.s_addr = *(u_long *) local->h_addr_list[i++];
	        printf("\tIP Address #%d: %s\n", i, inet_ntoa(addr));
	    }
	   
	    printf ("\n請選擇捕獲資料待使用的介面號:");
	    scanf_s( "%d", &in);
	    
	    memset( pLocalIP, 0, sizeof(sockaddr_in));
	    memcpy( &pLocalIP->sin_addr.S_un.S_addr, local->h_addr_list[in-1], sizeof(pLocalIP->sin_addr.S_un.S_addr));
	    pLocalIP->sin_family = AF_INET;
	    pLocalIP->sin_port = 0;
	
	    //繫結本地地址
	    iResult = bind( RawSocket, (struct sockaddr *) pLocalIP, sizeof(sockaddr_in));
	    if( iResult == SOCKET_ERROR){
	        printf("bind 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
	        quit(RawSocket);
			return -1;
	    }
	    printf(" \n成功繫結套接字和#%d號介面地址", in);
	
	    //設定套接字接收命令
	    iResult = WSAIoctl(RawSocket, SIO_RCVALL , &Optval, sizeof(Optval),  &dwBufferLen, sizeof(dwBufferLen), &dwBytesReturned , NULL , NULL );
	    if ( iResult == SOCKET_ERROR ){
	        printf("WSAIoctl 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
	        quit(RawSocket);
			return -1;
	    }
	}
	return RawSocket;
}


ConcurrentEchoTCPServer.h:

#pragma once
#include "winsock2.h"
#include "stdio.h"
#pragma comment(lib,"ws2_32.lib")
//定義網路框架程式中所需的巨集
#define TRUE			1
#define FALSE			0
#define	MAXLINE		    200	    // max text line length 
#define	DEFAULT_NAMELEN 100 //預設的名字長度
//首部結構定義
typedef struct tagIPHDR
{
	UCHAR hdr_len :4;  // length of the header
    UCHAR version :4;  // version of IP
	UCHAR	TOS;	   // Type of service
	USHORT	TotLen;	   // Total length
	USHORT	ID;		   // Identification
	USHORT	FlagOff;   // Flags and fragment offset
	UCHAR	TTL;	   // Time-to-live
	UCHAR	Protocol;  // Protocol
	USHORT	Checksum;  // Checksum
	ULONG IPSrc;	   // Internet address, source
	ULONG IPDst;	   // Internet address, destination
} IPHDR, *PIPHDR;

typedef struct tagUDPHDR	//UDP頭定義
{
	USHORT src_portno; 
	USHORT dst_portno; 
	USHORT udp_length; 
	USHORT udp_checksum; 
} UDPHDR,*PUDPHDR;

typedef struct tagTCPHDR		//TCP首部定義
{
	USHORT  sport;            //Source port 
    USHORT  dport;            //Destination port 
    ULONG   seq;              //Sequence number 
    ULONG   ack;              //Ack number 
    BYTE    hlen;             // TCP header len (num of bytes << 2) 
    BYTE    flags;            // Option flags 
    USHORT  window;           // Flow control credit (num of bytes) 
    USHORT  check;            // Checksum 
    USHORT  urgent;           // Urgent data pointer 
} TCPHDR,*PTCPHDR;
//TCP標誌欄位定義
#define TFIN        0x01    // Option flags: no more data 
#define TSYN        0x02    // sync sequence nums 
#define TRST        0x04    // reset connection 
#define TPUSH       0x08    // push buffered data 
#define TACK        0x10    // acknowledgement 
#define TURGE       0x20    // urgent 


typedef struct tagFHDR		//UDP偽首部定義
{
	ULONG IPSrc;	
	ULONG IPDst;	
	UCHAR zero;
	UCHAR protocol;
	USHORT udp_length;
} FHDR,*PFHDR;


//ICMP資料報頭
typedef struct tagICMPHDR
{
    UCHAR type;  //8位型別
    UCHAR code;  //8位程式碼
    USHORT cksum;  //16位校驗和
    USHORT id;   //16位識別符號
    USHORT seq;  //16位序列號
} ICMPHDR,*PICMPHDR;

#pragma pack()
//ICMP型別欄位
const BYTE ICMP_ECHO_REQUEST = 8; //請求回顯
const BYTE ICMP_ECHO_REPLY  = 0; //回顯應答
const BYTE ICMP_TIMEOUT   = 11; //傳輸超時
const DWORD DEF_ICMP_TIMEOUT = 3000; //預設超時時間,單位ms
const int DEF_ICMP_DATA_SIZE = 32; //預設ICMP資料部分長度
const int MAX_ICMP_PACKET_SIZE = 1024; //最大ICMP資料報的大小
const int DEF_MAX_HOP = 30;    //最大跳站數
class CSocketFrame
{
public:
	CSocketFrame(void);
	~CSocketFrame(void);
	int set_address(char * hname, char * sname, struct sockaddr_in * sap, char * protocol);
	int start_up(void);
	int clean_up(void);
	int quit(SOCKET s);
	USHORT check_sum(USHORT *pchBuffer, int iSize);
	SOCKET tcp_server( char *hname, char *sname );
	SOCKET udp_server( char *hname, char *sname );
	int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen);
	int recvvl(SOCKET s, char * recvbuf, unsigned int recvbuflen);
	SOCKET tcp_client( char *hname, char *sname );
	SOCKET udp_client( char *hname, char *sname, BOOL flag);
    SOCKET raw_socket( BOOL bSendflag, BOOL bRecvflag, int iProtocol, sockaddr_in *pLocalIP);
};


ConcurrentEchoTCPServer.c:

#include "stdafx.h"
#include "ConcurrentEchoTCPServer.h"
#include "SocketFrame.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define SERVERPORT "7210"
#include "winsock2.h"
// 唯一的應用程式物件

CWinApp theApp;

using namespace std;
UINT tcp_server_fun_echo( LPVOID pParam );
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;

	// 初始化 MFC 並在失敗時顯示錯誤
	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
	{
		// TODO: 更改錯誤程式碼以符合您的需要
		_tprintf(_T("錯誤: MFC 初始化失敗\n"));
		nRetCode = 1;
	}
	else
	{
		// TODO: 在此處為應用程式的行為編寫程式碼。
		CSocketFrame frame;
		int iResult = 0;
		SOCKET ListenSocket, ConnectSocket;
        CWinThread *pThread= NULL;
		//輸入引數合法性檢查
		if (argc != 1)
		{
			printf("usage: ConcurrentEchoTCPServer");
			return -1;
		}
		
		//Windows Sockets Dll初始化
		frame.start_up();

		//建立伺服器端的流式套接字並在指定埠號上監聽
		ListenSocket = frame.tcp_server( NULL,SERVERPORT );
		if ( ListenSocket == -1 )
			return -1;
        printf("伺服器準備好回射服務。。。\n");
		for ( ; ; ) {
            ConnectSocket = accept( ListenSocket, NULL, NULL );
			if(ConnectSocket != INVALID_SOCKET ){
				//建立連線成功
				printf("\r\n建立連線成功\n\n");
				//啟動回射執行緒
				pThread = AfxBeginThread( tcp_server_fun_echo, &ConnectSocket );
			}
			else{
				printf("accept 函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
				frame.quit( ListenSocket );
				return -1;
			}
		}
		frame.quit( ListenSocket );
	}

	return nRetCode;
}



/********************************************************
函式名:tcp_server_fun
輸入引數:SOCKET s:伺服器的連線套接字
輸出引數:0:成功,-1:失敗
功能:tcp_client回射客戶端的具體功能函式
********************************************************/
UINT tcp_server_fun_echo( LPVOID pParam )
{
	int iResult = 0;
	char    recvline[MAXLINE];
	int err;

	//將輸入引數轉換為連線套接字
	SOCKET s =*( (SOCKET *)pParam);
	do {
		memset( recvline, 0, MAXLINE );
		//接收資料
 	    iResult = recv( s, recvline, MAXLINE, 0);
		if (iResult > 0){
			printf("伺服器端接收到資料%s\n", recvline);
			
			//回射傳送已收到的資料
			iResult = send( s,recvline,MAXLINE, 0 );
			if(iResult == SOCKET_ERROR)
			{
				printf("send 函式呼叫錯誤,錯誤號: %ld\n", WSAGetLastError());
				err = closesocket(s);
				if (err == SOCKET_ERROR){
					printf("closesocket 函式呼叫錯誤,錯誤號:%d\n", WSAGetLastError());
				}
				iResult = -1;
			}
			else
			    printf("伺服器端傳送資料%s\n", recvline);
		}
		else{
			if (iResult == 0)
 	            printf("對方連線關閉,退出\n");
		    else{
 	            printf("recv 函式呼叫錯誤,錯誤號: %d\n", WSAGetLastError());
			    iResult = -1;
			}
			err = closesocket(s);
			if (err == SOCKET_ERROR){
				printf("closesocket 函式呼叫錯誤,錯誤號:%d\n", WSAGetLastError());
			}
			break;
		}
 	} while (iResult > 0);

	return iResult;
}