您的位置:首页 > 理论基础 > 计算机网络

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);
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息