socket编程基础
Posted sunnypoem
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket编程基础相关的知识,希望对你有一定的参考价值。
参考:https://www.cnblogs.com/jikexianfeng/p/5729168.html
函数及参数意义
socket
int socket(int domain, int type, int protocol)
domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
sockaddr_in
struct sockaddr_in
{
short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/
unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了跟SOCKADDR结构在内存中对齐*/
};
sockaddr_in 与 sockaddr : https://blog.csdn.net/will130/article/details/53326740
in_addr
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
地址转换函数
UDP 发送
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/socket.h>
#define MAX_RCVBUF_LEN 60000 char dstIp[16] = "10.10.10.10"; unsigned short dstPort = 1083;
int main(int argc, char *argv[]) { int sock_send;
unsigned char buf[MAX_RCVBUF_LEN]; int sendLen = 20;
struct sockaddr_in serv_adr;
sock_send = socket(PF_INET, SOCK_DGRAM, 0); if(sock_send == -1) { exit(1); } memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family = AF_INET; inet_pton(AF_INET, dstIp, &serv_adr.sin_addr); serv_adr.sin_port = htons(dstPort);
sendto(sock_send, buf, sendLen, 0, (struct sockaddr*)&serv_adr, sizeof(serv_adr)); printf("send buf %d. ", sendLen); close(sock_send);
return 0; } |
UDP接收
void* udp_server(void *arg) { struct sockaddr_in sin; struct sockaddr_in rin; int sock_fd; int bindrt; int address_size; unsigned int i; unsigned char rcvbuf[MAX_RCVBUF_LEN]; char str[MAX_RCVBUF_LEN]; unsigned short uwMsglen;
bzero(&sin, sizeof(sin)); bzero(rcvbuf, MAX_RCVBUF_LEN); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(rcvPort);
sock_fd = socket(AF_INET, SOCK_DGRAM, 0); bindrt = bind(sock_fd, (struct sockaddr *)&sin, sizeof(sin));
while(1) { address_size = sizeof(rin); uwMsglen = recvfrom(sock_fd, rcvbuf, MAX_RCVBUF_LEN, 0, (struct sockaddr *)&rin, &address_size); if(-1 == uwMsglen) { perror("call to recvfrom. "); exit(1); } } } |
RAW SOCKET
两种raw socket应用:http://blog.chinaunix.net/uid-21757287-id-194935.html
1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包
2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧
参考了以下文章的代码。
源码:https://blog.csdn.net/luchengtao11/article/details/73878760
#include <unistd.h> #include <stdio.h> #include <sys/socket.h> #include <netinet/ip.h> #include <netinet/udp.h> #include<memory.h> #include<stdlib.h> #include <linux/if_ether.h> #include <linux/if_packet.h> // sockaddr_ll #include<arpa/inet.h> #include<netinet/if_ether.h> #include<iomanip> #include<iostream>
// The packet length #define PCKT_LEN 100
//UDP的伪头部 struct UDP_PSD_Header { u_int32_t src; u_int32_t des; u_int8_t mbz; u_int8_t ptcl; u_int16_t len; }; //计算校验和 unsigned short csum(unsigned short *buf, int nwords) { unsigned long sum; for (sum = 0; nwords > 0; nwords--) { sum += *buf++; } while(sum>>16){ sum = (sum & 0xffff) + (sum >>16); } return (unsigned short)(~sum); }
// Source IP, source port, target IP, target port from the command line arguments int main(int argc, char *argv[]) { int sd; char buffer[PCKT_LEN] ; //查询www.chongfer.cn的DNS报文 #if 1 unsigned char DNS[] = { 0xd8, 0xcb , 0x01, 0x00, 0x00, 0x01, 0x00 ,0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x08, 0x63, 0x68, 0x6f, 0x6e, 0x67, 0x66, 0x65, 0x72, 0x02, 0x63, 0x6e, 0x00, 0x00, 0x01, 0x00, 0x01 }; #endif struct iphdr *ip = (struct iphdr *) buffer; struct udphdr *udp = (struct udphdr *) (buffer + sizeof(struct iphdr)); // Source and destination addresses: IP and port struct sockaddr_in sin, din; int one = 1; const int *val = &one; //缓存清零 memset(buffer, 0, PCKT_LEN);
if (argc != 5) { printf("- Invalid parameters!!! "); printf("- Usage %s <source hostname/IP> <source port> <target hostname/IP> <target port> ", argv[0]); exit(-1); }
// Create a raw socket with UDP protocol sd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); if (sd < 0) { perror("socket() error"); // If something wrong just exit exit(-1); } else printf("socket() - Using SOCK_RAW socket and UDP protocol is OK. "); //IPPROTO_TP说明用户自己填写IP报文 //IP_HDRINCL表示由内核来计算IP报文的头部校验和,和填充那个IP的id if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int))) { perror("setsockopt() error"); exit(-1); } else printf("setsockopt() is OK. ");
// The source is redundant, may be used later if needed // The address family din.sin_family = AF_INET; // Port numbers din.sin_port = htons(atoi(argv[4])); // IP addresses din.sin_addr.s_addr = inet_addr(argv[3]);
// Fabricate the IP header or we can use the // standard header structures but assign our own values. ip->ihl = 5; ip->version = 4;//报头长度,4*32=128bit=16B ip->tos = 0; // 服务类型 ip->tot_len = htons ((sizeof(struct iphdr) + sizeof(struct udphdr)+sizeof(DNS))); //ip->id = htons(54321);//可以不写 ip->ttl = 64; // hops生存周期 ip->protocol = 17; // UDP ip->check = 0; // Source IP address, can use spoofed address here!!! ip->saddr = inet_addr(argv[1]); // The destination IP address ip->daddr = inet_addr(argv[3]);
// Fabricate the UDP header. Source port number, redundant udp->source = htons(atoi(argv[2]));//源端口 // Destination port number udp->dest = htons(atoi(argv[4]));//目的端口 udp->len = htons(sizeof(struct udphdr)+sizeof(DNS));//长度 //forUDPCheckSum用来计算UDP报文的校验和用 //UDP校验和需要计算 伪头部、UDP头部和数据部分 char * forUDPCheckSum = new char[sizeof(UDP_PSD_Header) + sizeof(udphdr)+sizeof(DNS) +1]; memset(forUDPCheckSum, 0, sizeof(UDP_PSD_Header) + sizeof(udphdr) + sizeof(DNS) +1 ); UDP_PSD_Header * udp_psd_Header = (UDP_PSD_Header *)forUDPCheckSum; udp_psd_Header->src = inet_addr(argv[1]); udp_psd_Header->des = inet_addr(argv[3]); udp_psd_Header->mbz = 0; udp_psd_Header->ptcl = 17; udp_psd_Header->len = htons(sizeof(udphdr)+sizeof(DNS)); memcpy(forUDPCheckSum + sizeof(UDP_PSD_Header), udp, sizeof(udphdr)); memcpy(forUDPCheckSum + sizeof(UDP_PSD_Header) + sizeof(udphdr), DNS, sizeof(DNS));
ip->check = csum((unsigned short *)ip, sizeof(iphdr)/2);//可以不用算 //计算UDP的校验和,因为报文长度可能为单数,所以计算的时候要补0 udp->check = csum((unsigned short *)forUDPCheckSum,(sizeof(udphdr)+sizeof(UDP_PSD_Header)+sizeof(DNS) +1)/2);
setuid(getpid());//如果不是root用户,需要获取权限 printf("Using Source IP: %s port: %u, Target IP: %s port: %u. ", argv[1], atoi(argv[2]), argv[3], atoi(argv[4])); std::cout << "Ip length:" << ip->tot_len << std::endl; int count; //将DNS报文拷贝进缓存区 memcpy(buffer + sizeof(iphdr) + sizeof(udphdr), DNS, sizeof(DNS));
if (sendto(sd, buffer, ip->tot_len, 0, (struct sockaddr *)&din, sizeof(din)) < 0) { perror("sendto() error"); exit(-1); } close(sd); return 0; } |
IP/UDP 头填充
IP、TCP、UDP头详解:https://blog.csdn.net/mrwangwang/article/details/8537775
/*IPv4 Header*/ struct IpHead { unsigned char verHeaderLen; /*version (0100 0110) and header len (4 bytes per unit)*/ unsigned char typeOfService;/*type of service: PPP D T R M Rsvd*/ unsigned short totalLen; /*Total length: header + data*/ unsigned short identifier; /*Identifier*/ unsigned short flagFragOff; /*Flags and Fragment Offset*/ unsigned char ttl; /*TTL*/ unsigned char protocol; /*Protocol: 1-ICMP 6-TCP 17-UDP*/ unsigned short checkSum; /*Header Checksum*/ unsigned int srcIp; unsigned int dstIp; char pData[0]; };
/*UDP Header*/ struct UdpHead { unsigned short srcPort; unsigned short dstPort; unsigned short dataLen; unsigned short checkSum; char pData[0]; }; |
IP头部校验和字段:
当发送 IP 包时,需要计算 IP 报头的校验和:
1 、把校验和字段置为 0 ;
2 、对 IP 头部中的每 16bit 进行二进制求和;
3 、 如果和的高 16bit 不为 0 ,则将和的高 16bit 和低 16bit 反复相加,直到和的高 16bit 为 0 ,从而获得一个 16bit 的值;
4 、将该 16bit 的值取反,存入校验和字段。
当接收 IP 包时,需要对报头进行确认,检查 IP 头是否有误,算法同上 2 、 3 步,然后判断取反的结果是否为 0 ,是则正确,否则有错。
unsigned short csum(unsigned short *buf, int nwords) { unsigned long sum; for (sum = 0; nwords > 0; nwords--) { sum += *buf++; }
while(sum>>16){ sum = (sum & 0xffff) + (sum >>16); }
return (unsigned short)(~sum); } |
UDP头中校验和字段:占16比特。用来对UDP头部和UDP数据进行校验。和TCP不同的是,对UDP来说,此字段是可选项,而TCP数据段中的校验和字段是必须有的。UDP头中的校验和可以设置为0,表示发送端没有计算校验和。
文件系统、linux协议栈、系统调用与socket
牛人总结:https://www.cnblogs.com/kakawater/p/7085122.html
以上是关于socket编程基础的主要内容,如果未能解决你的问题,请参考以下文章