Русские Блоги
UDP означает протокол пользовательских дейтаграмм, который представляет собой протокол передачи без установления соединения. По сравнению с TCP, его преимущества и недостатки:
Преимущества: высокая скорость передачи, низкое потребление ресурсов, простое программирование, широко используется при передаче аудио- и видеоданных.
Недостатки: при низком качестве сети потеря пакетов будет серьезной, что приведет к потере или повреждению данных.
Процесс общения
Поскольку UDP не требует установления соединения, процесс связи будет немного отличаться:
Как видно из рисунка, использование UDP для связи позволяет осуществлять прямую связь без установления соединения между обеими сторонами.
Два, функция API
Используйте UDP для сетевого программирования, Linux также предоставляет некоторые функции.
2.1 sendto отправка
Это функция отправки данных, используемая в режиме UDP.Отличие от режима TCP состоит в том, что для каждой отправки требуется такая информация, как адрес назначения.
#include #include ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
- sockfd: дескриптор файла сокета.
- buf: отправленный контент.
- len: длина отправленных данных.
- flags: flags, вы можете использовать этот параметр для установки различных методов передачи данных, обычно используйте 0.
- dest_addr: информация об адресе назначения, инкапсулированная в структуре socketaddr.
- addrlen: размер информации о целевом адресе.
- Возвращаемое значение: вернуть количество отправленных байтов в случае успеха, -1 в случае неудачи и установить для соответствующей ошибки значение errno.
2.2 recvfrom получения
#include #include ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
- sockfd: дескриптор файла сокета.
- buf: буфер данных, используемый для хранения полученных данных.
- len: длина получаемых данных.
- flags: flags, вы можете использовать этот параметр для установки различных методов передачи данных, обычно используйте 0.
- src_addr: информация об адресе источника, инкапсулированная в структуре socketaddr.
- addrlen: размер информации об адресе источника,указатель。
- Возвращаемое значение: вернуть количество байтов, полученных в случае успеха, -1 в случае неудачи и установить для соответствующей ошибки значение errno.
См. Другие функции в предыдущем блоге:
Три, пример связи UDP
client.c (сначала отправить):
#include #include #include #include #include #include typedef struct sockaddr* saddrp; int main(int argc, char const *argv[]) int sockfd = socket(AF_INET,SOCK_DGRAM,0); if (0 > sockfd) perror("socket"); return -1; > struct sockaddr_in addr = >; addr.sin_family = AF_INET; addr.sin_port = htons(12345); addr.sin_addr.s_addr = inet_addr("127.0.0.1"); socklen_t addr_len = sizeof(struct sockaddr_in); while(1) char buf[255] = >; printf("Please input cData:"); gets(buf); sendto(sockfd,buf,strlen(buf)+1,0,(saddrp)&addr,sizeof(addr)); if(0 == strncmp(buf, "end", 3)) break; recvfrom(sockfd,buf,sizeof(buf),0,(saddrp)&addr,&addr_len); printf("cRecv:%s\n",buf); printf("the ipaddr = %#x\n", addr.sin_addr.s_addr); printf("the port = %d\n", addr.sin_port); if(0 == strncmp(buf, "end", 3)) break; > close(sockfd); return 0; >
server.c (сначала получить):
#include #include #include #include #include #include typedef struct sockaddr* saddrp; int main(int argc, char const *argv[]) // Создаем сокет int sockfd = socket(AF_INET,SOCK_DGRAM,0); if (0 > sockfd) perror("sockfd"); return -1; > // Подготавливаем адрес struct sockaddr_in addr = >; addr.sin_family = AF_INET;//ipv4 addr.sin_port = htons(12345);// Номер порта addr.sin_addr.s_addr = htonl(INADDR_ANY);// Получаем ip автоматически // Привязка int ret = bind(sockfd,(saddrp)&addr,sizeof(addr)); if (0 > ret) perror("bind"); return -1; > struct sockaddr_in src_addr =>; socklen_t addr_len = sizeof(struct sockaddr_in); while(1) char buf[255] = >; // Получение данных и IP-адреса источника recvfrom(sockfd,buf,sizeof(buf),0,(saddrp)&src_addr,&addr_len); printf("sRecv:%s\n",buf); if (0 == strncmp(buf,"end",3)) break; // Отправляем данные на целевой адрес printf("Please input sData to send:"); gets(buf); sendto(sockfd,buf,strlen(buf)+1,0,(saddrp)&src_addr,addr_len); if (0 == strncmp(buf,"end", 3)) break; > // Закрываем объект сокета close(sockfd); return 0; >
результат операции:
client:
server:
анализ:
Клиент сначала отправляет данные на сервер. После того, как область обслуживания получает данные, она знает адресную информацию клиента, а затем данные могут быть отправлены клиенту. Как правило, клиент отправляет данные первым, а сервер первым их получает. Введите конец, и обе стороны будут судить о конце.
How to Program raw UDP sockets in C on Linux
Raw udp sockets are used to constructed udp packets with a application defined custom header. It is useful in security related network applications.
The udp header can be found in RFC 768 and has a very simple structure as shown below.
0 7 8 15 16 23 24 31 +--------+--------+--------+--------+ | Source | Destination | | Port | Port | +--------+--------+--------+--------+ | | | | Length | Checksum | +--------+--------+--------+--------+ | | data octets . +---------------- . User Datagram Header Format
The length is the length of the udp header + data in bytes. So the udp header itself is 8 byte long and data can be upto 65536 byte long. The checksum is calculated the same way as the checksum of a tcp header, using a pseudo header.
The code example shown here is for Linux. Raw socket support is not fully available in the windows socket api (winsock). It is there but with a lot of restrictions.
Examples shown here would construct the IP header along with the udp socket. So its more like a raw IP packet that encapsulates UDP format data inside itself.
Code
A raw UDP socket can be simply created using the socket function
int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
In such a socket, the IP header shall be provided by the kernel. The application has to provide the UDP header + Data.
If the IP_HDRINCL option is set on it, then the application would need to provide the IP header too.
Here is the full program that uses a raw udp socket to send out data.
/* Raw UDP sockets */ #include //for printf #include //memset #include //for socket ofcourse #include //for exit(0); #include //For errno - the error number #include //Provides declarations for udp header #include //Provides declarations for ip header /* 96 bit (12 bytes) pseudo header needed for udp header checksum calculation */ struct pseudo_header < u_int32_t source_address; u_int32_t dest_address; u_int8_t placeholder; u_int8_t protocol; u_int16_t udp_length; >; /* Generic checksum calculation function */ unsigned short csum(unsigned short *ptr,int nbytes) < register long sum; unsigned short oddbyte; register short answer; sum=0; while(nbytes>1) < sum+=*ptr++; nbytes-=2; >if(nbytes==1) < oddbyte=0; *((u_char*)&oddbyte)=*(u_char*)ptr; sum+=oddbyte; >sum = (sum>>16)+(sum & 0xffff); sum = sum + (sum>>16); answer=(short)~sum; return(answer); > int main (void) < //Create a raw socket of type IPPROTO int s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); if(s == -1) < //socket creation failed, may be because of non-root privileges perror("Failed to create raw socket"); exit(1); >//Datagram to represent the packet char datagram[4096] , source_ip[32] , *data , *pseudogram; //zero out the packet buffer memset (datagram, 0, 4096); //IP header struct iphdr *iph = (struct iphdr *) datagram; //UDP header struct udphdr *udph = (struct udphdr *) (datagram + sizeof (struct ip)); struct sockaddr_in sin; struct pseudo_header psh; //Data part data = datagram + sizeof(struct iphdr) + sizeof(struct udphdr); strcpy(data , "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); //some address resolution strcpy(source_ip , "192.168.1.2"); sin.sin_family = AF_INET; sin.sin_port = htons(80); sin.sin_addr.s_addr = inet_addr ("192.168.1.1"); //Fill in the IP Header iph->ihl = 5; iph->version = 4; iph->tos = 0; iph->tot_len = sizeof (struct iphdr) + sizeof (struct udphdr) + strlen(data); iph->id = htonl (54321); //Id of this packet iph->frag_off = 0; iph->ttl = 255; iph->protocol = IPPROTO_UDP; iph->check = 0; //Set to 0 before calculating checksum iph->saddr = inet_addr ( source_ip ); //Spoof the source ip address iph->daddr = sin.sin_addr.s_addr; //Ip checksum iph->check = csum ((unsigned short *) datagram, iph->tot_len); //UDP header udph->source = htons (6666); udph->dest = htons (8622); udph->len = htons(8 + strlen(data)); //tcp header size udph->check = 0; //leave checksum 0 now, filled later by pseudo header //Now the UDP checksum using the pseudo header psh.source_address = inet_addr( source_ip ); psh.dest_address = sin.sin_addr.s_addr; psh.placeholder = 0; psh.protocol = IPPROTO_UDP; psh.udp_length = htons(sizeof(struct udphdr) + strlen(data) ); int psize = sizeof(struct pseudo_header) + sizeof(struct udphdr) + strlen(data); pseudogram = malloc(psize); memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header)); memcpy(pseudogram + sizeof(struct pseudo_header) , udph , sizeof(struct udphdr) + strlen(data)); udph->check = csum( (unsigned short*) pseudogram , psize); //loop if you want to flood :) //while (1) < //Send the packet if (sendto (s, datagram, iph->tot_len , 0, (struct sockaddr *) &sin, sizeof (sin)) < 0) < perror("sendto failed"); >//Data send successfully else < printf ("Packet Send. Length : %d \n" , iph->tot_len); > > return 0; > //Complete
Run the program with root user/privileges.
On Ubuntu use sudo.
Notes
To check the raw udp packets, use a packets sniffer like Wireshark. It should show all the packets generated by the above program.
If you only need to construct the UDP header then use IPPROTO_UDP as the protocol. Firewalls like firestarter can block such raw packets from transmitting, so disable them before testing the program.
A common application of raw udp sockets is udp based tracerouting where an application sends out udp packets with increasing ttl values in Ip header. We shall see an example of such a program in another article.
A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected] .
9 Comments
- Stefan September 14, 2021 at 8:08 pm Hi madmat, Sorry, I think you’re wrong:
You oversee, that a value >0xFFFF can be appear even after the first sum. So the addition of the bits >0xFFFF have to be done twice.
As an example, think about checksum of 0x3FFFF after addition of all bytes to be checksummed.
The first wrap gives you 3 + 0xFFFF = 0x10002, the second wrap gives you 1 + 2 = 0x0003 as final checksum.
This rule is valid for both, IP and UDP. regards,
Stefan
- Silver Moon Post author October 20, 2012 at 11:11 am the ip checksum is being calculated like this
iph->check = csum ((unsigned short *) datagram, iph->tot_len); the second parameter is the ip header length which specifies how much of the whole datagram should be used to calculate the checksum, which is only the ip header in this case.