原始套接字实现

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#ifdef WIN32
#include <Winsock2.h>
#include <ws2ipdef.h>
#include <WS2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#endif

#ifdef WIN32
#pragma comment(lib,"ws2_32.lib")
#else
#define SOCKET_ERROR -1
typedef int BOOL;
#endif



#define  DEFAULT_PORT 10089


typedef struct ip_head
{ 
	union 
	{  
		unsigned char Version;  
		unsigned char HeadLen; 
	};
	unsigned char ServiceType; 
	unsigned short TotalLen; 
	unsigned short Identifier; 
	union 
	{  
		unsigned short Flags; 
		unsigned short FragOffset; 
	}; 
	unsigned char TimeToLive; 
	unsigned char Protocol; 
	unsigned short HeadChecksum; 
	unsigned int SourceAddr; 
	unsigned int DestinAddr; 
}IP_HEADER;


/*
 * TCP header.
 * Per RFC 793, September, 1981.
 */
typedef struct tcp_head {
	u_short th_sport;               /* source port */
	u_short th_dport;               /* destination port */
	unsigned int th_seq;                 /* sequence number */
	unsigned int th_ack;                 /* acknowledgement number */
	unsigned char th_lenres;
	u_char  th_flags;
	u_short th_win;                 /* window */
	u_short th_sum;                 /* checksum */
	u_short th_urp;                 /* urgent pointer */
}TCP_HEADER;

//tcp伪报头
typedef struct psd_hdr
{
	unsigned long saddr;
	unsigned long daddr;
	char mbz;
	char ptcl; //协议类型
	unsigned short tcpl;  //tcp长度
}PSD_HEADER;

typedef struct udp_head
{
	unsigned short uh_sport;
	unsigned short uh_dport;
	unsigned short uh_len;
	unsigned short uh_sum;
}UDP_HEADER;

struct tcpiphdr
{
	ip_head ipHead;
	tcp_head tcpHead;
};

void InitSocket()
{
#ifdef WIN32
	WSADATA wsaData; 
	WORD wVersionRequested = MAKEWORD(2, 2); 
	int err = WSAStartup(wVersionRequested, &wsaData); 
	if(err != 0)
	{
		printf("windows socket DLL initiates faild\n");
		exit(-1);
	}
#endif
}


int GetErrNo()
{
#ifdef WIN32
	return WSAGetLastError();
#else
	return errno;
#endif
}

const char* GetErrStr(int errnum)
{
	#ifdef WIN32
	return "请查看msdn帮助文档或定义winerror.h";
#else
	return strerror(errnum);
#endif
}
//设置虚拟IP地址
void SetVirtualIp(int fd, const char* userIp)
{
	bool flag = true;
	if(setsockopt(fd,IPPROTO_IP,IP_HDRINCL,(char *) &flag,sizeof(flag))==SOCKET_ERROR)
	{
		printf("windows socket DLL initiates faild\n");
		exit(-1);
	}
}

unsigned short CheckSum(unsigned short *buffer, int size)
{
	unsigned long cksum = 0;
	while(size > 0)
	{
		cksum += *buffer;
		size -= sizeof(unsigned short );
	}
	if (size)
	{
		cksum += *(unsigned short*)buffer;
	}
	cksum = (cksum >> 16) + (cksum & 0xffff);
	cksum += (cksum >> 16);
	return (unsigned short )(~cksum);
}

void WriteTcpPkg(const char* sourceIp, const char* destIp, unsigned int destPort,  char* szBuffer )
{
	int iTotalSize = sizeof(ip_head) + sizeof(tcp_head);
	int iIpVersion = 4;
	int iIpSize = sizeof(ip_head)/sizeof(unsigned long);
	ip_head ipHead;
	tcp_head tcpHead;
	psd_hdr psdHead;

	ipHead.Version = (iIpVersion << 4) | iIpSize;
	//ipHead.ServiceType = 0;
	ipHead.TotalLen = htons(iTotalSize);
	ipHead.Identifier = 1;
	ipHead.FragOffset = 0;
	ipHead.TimeToLive = 128;
	ipHead.Protocol = IPPROTO_TCP;
	ipHead.HeadChecksum = 0;
	ipHead.SourceAddr =	inet_addr(sourceIp);
	ipHead.DestinAddr = inet_addr(destIp);

	tcpHead.th_sport = htons(DEFAULT_PORT);
	tcpHead.th_dport = htons(destPort);
	tcpHead.th_seq = htonl(0x12345678);
	tcpHead.th_ack = 0;
	tcpHead.th_lenres = (sizeof(tcp_head)/4 << 4 | 0);
	tcpHead.th_flags = 2;  //标志位探测  2是syn
	tcpHead.th_win = htons(512);
	tcpHead.th_urp = 0;
	tcpHead.th_sum = 0;

	psdHead.saddr = ipHead.SourceAddr;
	psdHead.daddr = ipHead.DestinAddr;
	psdHead.mbz = 0;
	psdHead.ptcl = IPPROTO_TCP;
	psdHead.tcpl = htons(sizeof(tcp_head));

	//计算校验和
	memcpy(szBuffer, &psdHead, sizeof(psdHead)); 
	memcpy(szBuffer + sizeof(psdHead), &tcpHead, sizeof(tcpHead)); 
	tcpHead.th_sum = CheckSum( (unsigned short*)szBuffer, sizeof(psdHead) + sizeof(tcpHead));


	memcpy(szBuffer, &ipHead, sizeof(ipHead));
	memcpy(szBuffer + sizeof(ipHead), &tcpHead, sizeof(tcpHead));
	memset(szBuffer + sizeof(ipHead) + sizeof(tcpHead), 0, sizeof(int)); 
	ipHead.HeadChecksum = CheckSum((unsigned short*)szBuffer, sizeof(ipHead) + sizeof(tcpHead));

	memcpy(szBuffer, &ipHead, sizeof(ipHead));
}



void MyConnectTcp(sockaddr_in &servaddr)
{
	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	int r = connect(fd, (struct sockaddr*)&servaddr, sizeof(servaddr));
	int ret = sendto(fd, "hello", 5, 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
	struct sockaddr_in clientaddr;
	socklen_t size = sizeof(clientaddr);
	char szBuf[1024];
	ret = recvfrom(fd, szBuf, 1024, 0,(struct sockaddr*)&clientaddr, &size);
}

int main(int argc, char** argv)
{
	if(argc < 5)
	{
		printf("usage : tm_client servIp servPort data userIp\n");
		exit(-1);
	}
	char 	servIp[64], userIp[64], workfId[16];
	int port = atoi(argv[2]);
	strcpy(servIp, argv[1]);
	strcpy(workfId, argv[3]);
	strcpy(userIp, argv[4]);
	struct sockaddr_in servaddr;

	InitSocket();

	//创建原始套接字,需要有超级用户的权限
	int fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if( fd == SOCKET_ERROR)
	{
		printf("%s\n", GetErrStr(GetErrNo()));
		printf("socket函数执行失败\n");
		return 1;
	}
	
	//Header is included with data. 报文将由自己组装,以数据的形式进行发送
	BOOL flag = true;
	if(setsockopt(fd,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(flag))==SOCKET_ERROR)
	{
		printf("%d", GetErrNo());
		printf("set socket IP_HDRINCL faild\n");
		exit(-1);
	}

	//设置超时
	int nTimeOver = 1000;
	if(setsockopt(fd,SOL_SOCKET,SO_SNDTIMEO,(char *)&nTimeOver,sizeof(nTimeOver))==SOCKET_ERROR)
	{
		printf("%d", GetErrNo());
		printf("set socket SO_SNDTIMEO faild\n");
		exit(-1);
	}

	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr(servIp);
	servaddr.sin_port = htons(port);


	char szDataBuf[1024];
	
	memset(szDataBuf, 0, sizeof(szDataBuf));

	int ipsize = sizeof(ip_head), tcpsize = sizeof(tcp_head);
	int iTotalSize = sizeof(ip_head) + sizeof(tcp_head);
	WriteTcpPkg(userIp, servIp, port, szDataBuf);

	//此处在XP系统下会返回错误码WSAEINTR,表示通过WSACancelBlockingCall函数中断了,xp sp2及以上版本都会遇到这个问题
	if(sendto(fd, szDataBuf, iTotalSize, 0, (struct sockaddr*)&servaddr, sizeof(servaddr)) == SOCKET_ERROR)
	{
		printf("sendto message faild, errno = %d\n", GetErrNo());
		exit(-1);
	}


	struct sockaddr_in cliaddr;
	socklen_t cliLen = sizeof(cliaddr);

	if( recvfrom(fd,szDataBuf, sizeof(iTotalSize), 0, (struct sockaddr*)&cliaddr, &cliLen) < 0)
	{
		printf("recvfrom message faild\n");
		exit(-1);
	}

#ifdef WIN32
	closesocket(fd);
	WSACleanup();
#else
  close(fd);
#endif
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
博客地址:https://blog.csdn.net/weixin_41749063/article/details/104023987 摘要: ## 1.简介 其实Qt网络模块中自带的[QTcpsocket](https://doc.qt.io/archives/qt-5.6/qtcpsocket.html)已经封装的很好了,避免了用原始套接字编程繁琐的过程,不过大部分应用场景我们希望将收发数据 运行在一个单独的线程,不阻塞界面或其他线程。所以结合Qt自带的QThread类 简单的封装了一下QTcpsocket,使其工作 在单独线程,并加入了心跳和断线重连机制。 ## 2.EasyClient类说明 由于接触QT时间不是很长,所以写的不是很好,可能里边也有Bug,不过觉得这个思路应该是可行的。封装的比较简单,若有问题完全可以自己改改。主要提供一个思路就是的将一个继承自QObject的子类转移到一个线程,使其在这个线程里进行事件循环,开一个定时器用于发送心跳和重连。 经过采坑发现,定时器的开和关需放在实例化EasyClient子类的那个线程(下面称之为主线程),不能在事件循环线程中(下面称之为子线程中)否则会有错。实例化QTcpsock需要在 子线程中进行,否则会出现无法正常通信的现象。 使用时只需要实例化一个EasyClient的子类,重写SendHeartBeat()和DisPoseReceiveData()两个纯虚函数,SendHeartBeat()决定发什么内容作为心跳指令,当有数据可读时会全部读入,只需要在DisPoseReceiveData()做处理即可。.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值