用原始套接字收发数据包程序

通常我们看到的socket是拆包后的,只能看到用户数据,原始套接字raw就是给你看最原始的,以方便你进行干预。从用户的角度来看,SOCK_STREAM、SOCK_DGRAM这两类套接字似乎的确涵盖了TCP/IP应用的全部,因为基于TCP/IP的应用,从 协议栈的层次上讲,在传输层的确只可能建立于TCP或UDP协议之上(图),而SOCK_STREAM、SOCK_DGRAM又分别对应于TCP和 UDP,所以几乎所有的应用都可以用这两类套接字实现。

Raw Socket与标准套接字(SOCK_STREAM、SOCK_DGRAM)的区别在于前者直接置"根"于操作系统网络核心(Network Core),而SOCK_STREAM、SOCK_DGRAM则"悬浮"于TCP和UDP协议的外围,如图所示:

当我们使用Raw Socket的时候,可以完全自定义IP包,一切形式的包都可以"制造"出来。如果用原始套接字来传输数据的话,就要自己来写TCP或者UDP包的头部。




1、客户端程序

#include<stdio.h>

#include<netinet/tcp.h>

#include<netinet/ip.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include<stdlib.h>

#include<netinet/udp.h>

#include <string.h>

#define DESTPORT  1234

#define SOURCEPORT  8888

#define SOURCEADDR "127.0.0.1"

#define MAXDATASIZE 100

int main(int argc,char *argv[])

{

int sockfd,num;

struct sockaddr_in addr,peer;

int on;

on = 1;

int mm;

char buf[MAXDATASIZE];

int peerlen = sizeof(peer);

char msg[] = "welcome\n";

int msglen = strlen(msg);

char * shuju;

int nn;

if(argc !=2)

{

printf("Usage:%s<IP Address>\n",argv[0]);

exit(1);

}

bzero(&addr,sizeof(struct sockaddr_in));

addr.sin_family=AF_INET;

addr.sin_port=htons(DESTPORT);

inet_aton(argv[1],&addr.sin_addr);

/*创建套接字*/

if((sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))==-1)

{

perror("socket() error.");

exit(1);

}

/*设置IP_HDRINCL选项,构造自己的IP头部*/

setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));

char buffer[100];

struct ip *ip;

struct tcphdr *tcp;

int head_len;

int i;

head_len = sizeof(struct ip) + sizeof(struct tcphdr) + msglen;

bzero(buffer,100);

/*初始化IP的头部*/

ip = (struct ip *)buffer;

ip->ip_v = IPVERSION;

ip->ip_hl = sizeof(struct ip) >> 2;

ip->ip_tos = 0;

ip->ip_len = htons(head_len);

ip->ip_id = 0;

ip->ip_off = 0;

ip->ip_ttl = MAXTTL;

ip->ip_p = IPPROTO_TCP;

ip->ip_sum = 0;

ip->ip_dst =  addr.sin_addr;

ip->ip_src.s_addr = inet_addr(SOURCEADDR);

printf("dest address is %s\n",inet_ntoa(addr.sin_addr));

/*初始化TCP的头部*/

tcp = (struct tcphdr *)(buffer + sizeof(struct ip));

tcp->source = htons(SOURCEPORT);

tcp->dest = htons(DESTPORT);

tcp->seq = random();

tcp->ack_seq = 0;

tcp->doff = 5;

tcp->psh = 1;

tcp->check = 0;

/*shuju指针指向数据存储处*/

shuju =&buffer[sizeof(struct ip) + sizeof( struct tcphdr)];

memcpy(shuju,msg,msglen);

/* 发送数据*/

mm = sendto(sockfd,buffer,head_len,0,(struct sockaddr *)&addr,sizeof(struct sockaddr));

close(sockfd);

}

 

2、服务器端程序

#include<stdio.h>

#include<netinet/tcp.h>

#include<netinet/ip.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include<stdlib.h>

#include<netinet/udp.h>

#include <string.h>

#define DESTPORT  1234

#define SOURCEPORT  8888

#define SOURCEADDR "127.0.0.1"

#define MAXDATASIZE 100

int main()

{

int sockfd,num;

struct sockaddr_in addr,peer;

int on;

on = 1;

int mm;

char buf[MAXDATASIZE];

int peerlen = sizeof(peer);

char * shuju;

int nn;

/*创建一个TCP的原始套接字*/

if((sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))==-1)

{

perror("socket() error.");

exit(1);

}

/*设置IP_HDRINCL选项,以便构造自己的IP头部*/

setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));

char buffer[100];

struct ip *ip;

struct tcphdr *tcp;

int head_len;

int i;

/*初始化缓冲区*/

bzero(buffer,100);

/*初始化IP头部*/

ip = (struct ip *)buffer;

ip->ip_v = IPVERSION;

ip->ip_hl = sizeof(struct ip) >> 2;

ip->ip_tos = 0;

ip->ip_len = htons(head_len);

ip->ip_id = 0;

ip->ip_off = 0;

ip->ip_ttl = MAXTTL;

ip->ip_p = IPPROTO_TCP;

ip->ip_sum = 0;

ip->ip_dst.s_addr = inet_addr(SOURCEADDR);

ip->ip_src.s_addr = inet_addr(SOURCEADDR);

/*初始化TCP头部*/

tcp = (struct tcphdr *)(buffer + sizeof(struct ip));

tcp->source = htons(SOURCEPORT);

tcp->dest = htons(DESTPORT);

tcp->seq = random();

tcp->ack_seq = 0;

tcp->doff = 5;

tcp->psh = 1;

tcp->check = 0;

/*shuju指针指向数据存储处*/

shuju =&buffer[sizeof(struct ip) + sizeof( struct tcphdr)];

/*接收客户端发来的数据*/

while(1)

{

if((num = recvfrom(sockfd,buffer,MAXDATASIZE,0,(struct sockaddr *)&peer,&peerlen)) == -1)

{

printf("recvfrom() error\n");

exit(1);

}

/*输出客户端的数据*/

printf("client message: %s\n",shuju);

break;

}

/*关闭套接字*/

close(sockfd);

}

4Linux系统下运行结果

客服端发送一字符串“welcome”服务器端,结果如下:

客户端为:


服务器端为;

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值