windows網路程式設計之併發式伺服器
阿新 • • 發佈:2019-02-04
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;
}