Windows下基于原始套接字的回射客户端的源码
2016-12-12 22:35
453 查看
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; } else printf("Winsock 2.2 dll成功找到\n"); 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; } else printf("Winsock dll 释放成功\n"); 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:服务端口号 输出参数:创建服务器端流式套接字并配置,0:表示失败 功能:创建流式套接字,根据用户输入的地址和端口号,绑定套接字的服务地址 将其转换为监听状态 *********************************************************/ 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" ) ==1 ) return 0; //创建套接字 ListenSocket = socket( AF_INET, SOCK_STREAM, 0 ); if (ListenSocket == INVALID_SOCKET) { printf("socket 函数调用错误,错误号: %ld\n", WSAGetLastError()); clean_up(); return 0; } //设置服务器地址可重用选项 iResult = setsockopt( ListenSocket, SOL_SOCKET, SO_REUSEADDR, ( char * )&on, sizeof( on )); if ( iResult == SOCKET_ERROR){ printf("setsockopt函数调用错误,错误号: %d\n", WSAGetLastError()); quit(ListenSocket); return 0; } //绑定服务器地址 iResult = bind( ListenSocket, (struct sockaddr *) & local, sizeof (local)); if (iResult == SOCKET_ERROR) { printf("bind 函数调用错误,错误号: %d\n", WSAGetLastError()); quit(ListenSocket); return 0; } //设置服务器为监听状态,监听队列长度为NLISTEN iResult = listen(ListenSocket, SOMAXCONN); if (iResult == SOCKET_ERROR){ printf("listen 函数调用错误,错误号: %d\n", WSAGetLastError()); quit(ListenSocket); return 0; } return ListenSocket; } /******************************************************** 函数名:tcp_client 输入参数:char * hname:服务器主机名 or 点分十进制表示的IP地址 char * sname:服务端口号 输出参数:创建客户端流式套接字,0:表示失败 功能:创建流式套接字,根据用户输入的地址和端口号,向服务地址 请求建立连接 *********************************************************/ 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" ) ==1 ) return 0; //创建套接字 ClientSocket = socket( AF_INET, SOCK_STREAM, 0 ); if (ClientSocket == INVALID_SOCKET) { printf("socket 函数调用错误,错误号: %ld\n", WSAGetLastError()); clean_up(); return 0; } //请求向服务器建立连接 iResult =connect( ClientSocket, ( struct sockaddr * )&peer, sizeof( peer ) ); if (iResult == SOCKET_ERROR){ printf("connect 函数调用错误,错误号: %d\n", WSAGetLastError()); quit(ClientSocket); return 0; } 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:服务端口号 输出参数:创建服务器端流式套接字并配置,0:表示失败 功能:创建流式套接字,根据用户输入的地址和端口号,绑定套接字的服务地址 将其转换为监听状态 *********************************************************/ 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" ) ==1 ) return 0; //创建套接字 ServerSocket = socket( AF_INET, SOCK_DGRAM, 0 ); if (ServerSocket == INVALID_SOCKET) { printf("socket 函数调用错误,错误号: %ld\n", WSAGetLastError()); clean_up(); return 0; } //设置服务器地址可重用选项 iResult = setsockopt( ServerSocket, SOL_SOCKET, SO_REUSEADDR, ( char * )&on, sizeof( on )); if ( iResult == SOCKET_ERROR){ printf("setsockopt函数调用错误,错误号: %d\n", WSAGetLastError()); quit(ServerSocket); return 0; } //绑定服务器地址 iResult = bind( ServerSocket, (struct sockaddr *) & local, sizeof (local)); if (iResult == SOCKET_ERROR) { printf("bind 函数调用错误,错误号: %d\n", WSAGetLastError()); quit(ServerSocket); return 0; } return ServerSocket; } /******************************************************** 函数名:udp_client 输入参数:char * hname:服务器主机名 or 点分十进制表示的IP地址 char * sname:服务端口号 BOOL flag:工作模式标识,true表示连接模式,false表示非连接模式 输出参数:创建客户端流式套接字,0:表示失败 功能:创建数据报套接字,根据用户输入的地址和端口号 *********************************************************/ SOCKET CSocketFrame::udp_client( char *hname, char *sname, BOOL flag) { struct sockaddr_in peer; SOCKET ClientSocket; int iResult = 0; //指明服务器的地址peer为用户输入的IP和端口号 if (set_address( hname, sname, &peer, "udp" ) ==1 ) return 0; //创建套接字 ClientSocket = socket( AF_INET, SOCK_DGRAM, 0 ); if (ClientSocket == INVALID_SOCKET) { printf("socket 函数调用错误,错误号: %ld\n", WSAGetLastError()); clean_up(); return 0; } if( flag == TRUE) { //连接模式 //请求向服务器建立连接 iResult =connect( ClientSocket, ( struct sockaddr * )&peer, sizeof( peer ) ); if (iResult == SOCKET_ERROR){ printf("connect 函数调用错误,错误号: %d\n", WSAGetLastError()); quit(ClientSocket); return 0; } } 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地址的指针,返回参数,如果存在多个接口地址,获取用户选择的本地地址 输出参数:创建客户端流式套接字,0:表示失败 功能:创建数据报套接字,根据用户输入的地址和端口号 *********************************************************/ 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 0; } 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 0; } } 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 0; } //获取本机可用IP local = gethostbyname( HostName); printf ("\n本机可用的IP地址为:\n"); if( local ==NULL) { printf("gethostbyname 函数调用错误,错误号: %ld\n", WSAGetLastError()); quit(RawSocket); return 0; } 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 0; } 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 0; } } return RawSocket; }EchoUDPClientRaw.cpp:
// EchoUDPClientRaw.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "winsock2.h" #include "ws2tcpip.h" #include "stdio.h" #include "SocketFrame.h" #pragma pack(push,1) #define MAXLINE 200 // 发送和接收缓冲区的长度 #define INPUTLINE 100 // 输入文本的长度 #define ECHOPORT 7210//回射服务器的端口号 /******************************************************** 函数名:UDP_MakeProbePkt 输入参数:char *pUDPData:待填充的缓冲区指针,应填充包括IP首部在内的数据 char *pInputData:存储用户输入的字符串 UINT uSrcIP:源IP地址 UINT uDstIP:目的IP地址 USHORT uSrcPort:源端口号 USHORT uDestPort:目的端口号 输出参数:构造后的缓冲区有效字节长度。 功能:构造UDP协议的回射报文 *********************************************************/ int UDP_MakeProbePkt(char *pUDPData, char *pInputData, UINT uSrcIP, UINT uDestIP, USHORT uSrcPort, USHORT uDestPort) { IPHDR *IPhdr; //基本IP头定义 UDPHDR *UDPhdr; //UDP头定义 FHDR Fhdr; //伪头 char buf[MAXLINE]; //数据缓冲 char *data; int iRet=0; char *lpbuf = buf; CSocketFrame frame; /////////设置IP头 ZeroMemory(buf,sizeof(buf)); IPhdr=(IPHDR *)pUDPData; IPhdr->version = 4; IPhdr->hdr_len = 5; IPhdr->TOS = 0; IPhdr->TotLen = htons(sizeof(IPHDR)+sizeof(UDPHDR)+strlen(pInputData)); IPhdr->ID = (USHORT)GetCurrentThreadId(); IPhdr->FlagOff = 0; IPhdr->TTL = 0xff; IPhdr->Protocol = IPPROTO_UDP; IPhdr->Checksum = 0x0; //源地址为本机地址 IPhdr->IPSrc = uSrcIP; //目的地址为入参,可能变化 IPhdr->IPDst = uDestIP; ////////// 构造UDP包 UDPhdr=(UDPHDR *)(pUDPData+sizeof(IPHDR)); UDPhdr->dst_portno = htons(uDestPort); UDPhdr->src_portno = htons(uSrcPort); UDPhdr->udp_checksum = 0; //UDPhdr->udp_length = htons(sizeof(UDPHDR)); data=pUDPData+sizeof(IPHDR)+sizeof(UDPHDR); int ii=strlen(pInputData); memcpy(data,pInputData,strlen(pInputData)); UDPhdr->udp_length = htons(sizeof(UDPHDR)+strlen(pInputData)); //设置伪头 Fhdr.IPDst = IPhdr->IPDst; Fhdr.IPSrc = IPhdr->IPSrc; Fhdr.protocol = IPPROTO_UDP; Fhdr.udp_length = UDPhdr->udp_length; Fhdr.zero = 0x00; //计算UDP校验和 //校验和计算范围包括:UDP头,伪首部和用户数据 char *ptmp = buf; memcpy(ptmp,&Fhdr,sizeof(Fhdr)); ptmp += sizeof(Fhdr); memcpy(ptmp,UDPhdr,sizeof(UDPHDR)); ptmp +=sizeof(UDPHDR); memcpy(ptmp,data,strlen(pInputData)); UDPhdr->udp_checksum = frame.check_sum((USHORT*)buf,sizeof(Fhdr)+sizeof(UDPHDR)+strlen(pInputData)); iRet = sizeof(IPHDR) + sizeof(UDPHDR)+strlen(pInputData); return iRet; } /******************************************************** 函数名:UDP_Filter 输入参数:char *pUDPData:待填充的缓冲区指针,应填充包括IP首部在内的数据 UINT uServerIP:目标IP地址 USHORT uServerPort:目标端口号 输出参数:true表示找到回射应答,false表示当前收到的数据报并不是服务器的回射应答。 功能:对接收到的数据报进行过滤,获得由回射服务器发回的应答 *********************************************************/ BOOL UDP_Filter(char *pUDPData, UINT uServerIP,USHORT uServerPort) { IPHDR *pIPhdr; //基本IP头定义 UDPHDR *pUDPhdr; //UDP头定义 char *pData; UINT uSourceIP; //接收到包的源IP地址 USHORT uSourcePort; //接收到包的源端口 pIPhdr=(IPHDR *)pUDPData; uSourceIP = pIPhdr->IPSrc; pUDPhdr=(UDPHDR *)(pUDPData+sizeof(IPHDR)); uSourcePort = ntohs(pUDPhdr->src_portno); pData = pUDPData +sizeof(IPHDR) +sizeof(UDPHDR); if ( pIPhdr->Protocol ==17 && uSourceIP == uServerIP && uSourcePort == uServerPort) { //服务器返回的应答 printf("客户端接收到数据:%s \r\n", pData ); return true; } else return false; } /******************************************************** 函数名:UDP_Echo 输入参数:SOCKET sockSendRaw:用于发送UDP报文的原始套接字 SOCKET sockRecvRaw:用于接收响应的原始套接字 UINT uSrcIP:源IP地址 UINT uDstIP:目的IP地址 USHORT uSrcPort:源端口号 USHORT uDestPort:目的端口号 输出参数:0:成功,1:失败 功能:回射客户端的具体功能函数 *********************************************************/ BOOL UDP_Echo(SOCKET sockSendRaw,SOCKET sockRecvRaw, UINT uSrcIP,UINT uDstIP,USHORT uSrcPort,USHORT uDestPort) { //初始化参数 SOCKADDR_IN saDest; int len; int iResult=0; int bResult=FALSE; //申请缓冲区 char sendline[MAXLINE],recvline[MAXLINE],inputline[INPUTLINE]; memset(sendline,0,MAXLINE); memset(recvline,0,MAXLINE); memset(inputline,0,INPUTLINE); //设置目的地址 memset(&saDest,0 ,sizeof(saDest)); saDest.sin_family = AF_INET; saDest.sin_addr.s_addr = uDstIP; //构造分析数据报文 //循环发送用户的输入数据,并接收服务器返回的应答,直到用户输入"Q"结束 fflush(stdin); gets_s(inputline,INPUTLINE); if( *inputline == 'Q'){ printf("input end!\n"); return 0; } while(strlen(inputline)!=0) { len = UDP_MakeProbePkt(sendline, inputline, uSrcIP,uDstIP,uSrcPort,uDestPort); //发送回射请求 iResult = sendto(sockSendRaw,sendline,len,0,(SOCKADDR *)&saDest,sizeof(saDest)); if(iResult == SOCKET_ERROR) { printf("sendto 函数调用错误,错误号: %ld\n", WSAGetLastError()); return 1; } printf("\r\n客户端发送%d字节数据\r\n", iResult); memset(recvline,0,MAXLINE); while((iResult = recvfrom( sockRecvRaw, recvline, MAXLINE, 0, NULL, NULL )) >0) { //过滤出来自服务器端点地址的UDP回射应答并显示 bResult = UDP_Filter(recvline,uDstIP,uDestPort); if( bResult == TRUE ) break; else{ memset(recvline,0,MAXLINE); continue; } } if (iResult == SOCKET_ERROR){ printf("recvfrom 函数调用错误,错误号: %d\n", WSAGetLastError()); } //接收新的回射内容 memset(sendline,0,MAXLINE); memset(inputline,0,INPUTLINE); printf("\n请输入回射字符串:"); fflush(stdin); gets_s(inputline,INPUTLINE); if( *inputline == 'Q'){ printf("input end!\n"); return 0; } } return iResult; } int main(int argc, char* argv[]) { CSocketFrame frame; int iResult; SOCKET sockSendRaw,sockRecvRaw; sockaddr_in localaddr; //初始化参数 if (argc != 2) { fprintf(stderr,"\nUsage: EchoUDPClientRaw ***.***.***.***\n"); return 0; } //Windows Sockets Dll初始化 frame.start_up(); //创建原始套接字,并设置相应的选项 sockSendRaw = frame.raw_socket( TRUE, FALSE, IPPROTO_IP, NULL); if ( sockSendRaw == 0 ) return -1; sockRecvRaw = frame.raw_socket( FALSE, TRUE, IPPROTO_IP, &localaddr); if ( sockRecvRaw == 0 ) return -1; printf("套接字创建成功\n请输入回射字符串:"); //开始回射请求的发送与接收 //发送从构造IP头开始的UDP数据包,接收过滤,输出结果 iResult = UDP_Echo( sockSendRaw,sockRecvRaw, localaddr.sin_addr.S_un.S_addr ,inet_addr(argv[1]),ECHOPORT, ECHOPORT); if ( iResult ==1 ) printf("回射过程出错!\n"); //结束socket,释放资源 iResult = closesocket(sockSendRaw); if (iResult == SOCKET_ERROR){ printf("closesocket 函数调用错误,错误号:%d\n", WSAGetLastError()); return 1; } frame.quit( sockRecvRaw ); return iResult; }
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); };
相关文章推荐
- Windows下基于原始套接字的回射客户端
- TraceRoute(tracert)源码(基于原始套接字实现)
- Windows下基于流式套接字的时间同步服务器设计源码
- Windows 7 环境下基于原始套接字和ICMP的路由探测开发的问题【1】
- TraceRoute(tracert)源码(基于原始套接字实现)
- TCP/IP学习与实践[基于原始套接字的rootkit]
- 基于.NET平台的Windows编程实战系列课程导航及源码下载
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- C源码:一个原始套接字的实例 (http://www.fanqiang.com)
- 【原】基于Windows Media Player, 写自己的播放器【有源码,可下载整个工程】
- 基于 Json 的富客户端简易分页留言板设计,附源码
- 【Windows编程】【网络编程】【基于网络端口通信的客户端应用程序】解决方案【示意程序】
- 基于嗅探原理的原始套接字木马编写原理
- 【LIUNX】---TCP/IP学习与实践[基于原始套接字的rootkit]
- Windows套接字编程:基于TCP和UDP协议
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现