基于UDP协议之——socket编程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于UDP协议之——socket编程相关的知识,希望对你有一定的参考价值。
一. socket API
前面一篇《基于TCP协议之——socket编程》http://2627lounuo.blog.51cto.com/10696599/1775559已经花了大量的篇幅讲述了socket和使用基本的socket API所需要注意的问题,这里就不再赘述了。下面主要谈论的是UDP和TCP在socket编程中的不同之处;
1. 创建sock
和TCP面向连接的可靠的字节流传输服务不同的是,UDP是无连接的不可靠的数据报传输服务;虽然有所不同,但同样在进程间通信的时候需要提供出socket接口来进行数据的传输,也就是第一步仍然是创建出一个socket:
函数参数中,
domain是底层所使用的协议类型,这里仍然是AF_INET代表IPv4;
type在这里因为是UDP基于数据报的,所以为SOCK_DGRAM;
protocol为0,因为前面两个参数已经可知协议是什么了;
2. 绑定
虽然是无连接的传输服务,但所需要的网络信息是不可少的,因此仍然需要有本地的网络地址信息struct sockaddr_in的结构体类型来存放,也是需要和创建出来的socket进行绑定连接的:
函数参数中,
sockfd是创建出来的socket描述符;
addr为下面结构体的地址;
addrlen为结构体大小;
结构体中,
sin_family是底层所使用的协议,和在socket中的第一个参数一样为AF_INET;
sin_port为指定的端口号,这里是需要进行网络字节序的转换的,详情在TCP的那篇讲述中;
sin_addr同样是一个结构体,里面的成员应是指定的IP地址;
3.接收数据
正是因为UDP的特点,因此在UDP的连接传输数据中是不需要对网络中的连接请求进行监听的,也就更不需要进行accept处理连接了,这里在绑定好socket和本地信息之后,对于服务器来说就可以直接进行数据的接收了,所使用的函数是recvfrom:
函数参数中,
sockfd是创建出来的socket描述符;
buf是存放接收到数据的一个缓冲区;
len是表明一次接收数据报的长度大小;
flags是标志位属性,当置为0的时候表示已阻塞式来接收;
src_addr表示发送数据一方的网络地址信息;
addrlen是src_len的一个长度信息;
函数失败返回-1并置错误码,成功返回相应的size;
4. 发送数据
对于客户端来说,因为是UDP传输,因此也就不需要进行连接请求,当创建好一个socket和目的端的网络地址信息之后,就可以直接进行发送数据了:
函数参数中,
sockfd是创建出的socket描述符;
buf是要发送的数据缓冲区;
len是发送数据的长度;
flags置为0表示以默认状态;
dest_addr是目的端的网络地址信息结构体;
addrlen是dest_addr的长度大小;
二. 栗子时间
正是因为UDP的不可靠性,才使其使用起来比TCP要方便,因为UDP并不需要维护连接和差错的校验;同样可以用上面的socket API来设计服务器端和客户端的数据传输:
服务器端设计:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> void usage(const char *argv)//参数出错判断 { printf("%s [ip] [port]\n", argv); exit(0); } int create_server_sock(int ip, int port)//创建server端socket { int sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock < 0) { perror("socket"); exit(1); } struct sockaddr_in server;//创建server端网络地址信息 server.sin_family = AF_INET; server.sin_port = htons(port); server.sin_addr.s_addr = ip; if(bind(sock, (struct sockaddr*)&server, sizeof(server)) < 0)//绑定 { perror("bind"); exit(2); } return sock; } int main(int argc, char *argv[]) { if(argc != 3) usage(argv[0]); int port = atoi(argv[2]); int ip = inet_addr(argv[1]); int server_sock = create_server_sock(ip, port); char buf[1024]; struct sockaddr_in client; socklen_t len = sizeof(client); while(1) { //读取数据 memset(buf, ‘\0‘, sizeof(buf)); ssize_t size = recvfrom(server_sock, buf, sizeof(buf)-1, 0, (struct sockaddr*)&client, &len); if(size < 0) perror("recvfrom"); printf("client [%s] [%d]# %s\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port), buf); } return 0; }
客户端设计:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> void usage(const char *argv)//同样进行参数差错判断 { printf("%s [ip] [port]\n"); exit(0); } int main(int argc, char *argv[]) { if(argc != 3) usage(argv[0]); int port = atoi(argv[2]); int ip = inet_addr(argv[1]); int client_sock = socket(AF_INET, SOCK_DGRAM, 0); //创建client端socket if(client_sock < 0) { perror("socket"); exit(1); } struct sockaddr_in server;//创建目的server端的网络地址信息 server.sin_family = AF_INET; server.sin_port = htons(port); server.sin_addr.s_addr = ip; char buf[1024]; while(1) { memset(buf, ‘\0‘, sizeof(buf)); printf("client# "); fflush(stdout); gets(buf); //发送数据 ssize_t size = sendto(client_sock, buf, sizeof(buf), 0, (struct sockaddr*)&server, sizeof(server)); if(size < 0) perror("sendto"); } return 0; }
运行程序:
因为UDP是没有连接的,因此客户端和服务器端对彼此来说是互不干扰互不影响的,也就是无论双方的哪一方关闭了,另外一方同样可以进行相应的操作,只是对方无法响应或者处理而已。
三. 端口号
上面的server端程序运行时是自定义的一个端口号,而client端没有绑定其端口号是系统随机指定的;那么端口号在系统中到底是怎么一回事呢?
我们知道,在TCP报文中,有一个16位的源端口号和目的端口号,因此,端口号应该是从0到2的16次方-1,也就是0~65535,其中:
公认端口(Well Known Ports):从0到1023,这类端口也常称之为常用端口,它们紧密绑定(binding)于一些特定的服务。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯,而23号端口是Telnet服务专用的;
注册端口(Registered Ports):从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的,这些端口多数没有明确的定义服务对象,不同程序可根据实际需要自己定义。例如:许多系统处理动态端口从1024左右开始;
动态或私有端口(Dynamic and/or Private Ports):从49152到65535。理论上,不应为服务分配这些端口。实际上,有些较为特殊的程序,特别是一些木马程序就非常喜欢用这些端口,因为这些端口常常不被引起注意,容易隐蔽;
任何TCP/IP实现所提供的服务都是1-1023之间的端口号,这些端口号由IANA分配管理。其中,低于255的端口号保留用于公共应用;255到1023的端口号分配给各个公司,用于特殊应用;对于高于1023的端口号,称为临时端口号,IANA未做规定。
常用的保留TCP端口号有:
HTTP 80,FTP 20/21,Telnet 23,SMTP 25,DNS 53等。
常用的保留UDP端口号有:
DNS 53,BootP 67(server)/ 68(client),TFTP 69,SNMP 161等。
不同的端口号可根据不同的前段数字划分为不同的系别,例如:
8系端口含义
端口:80
服务:HTTP
说明:用于网页浏览。木马Executor开放此端口。
8080端口
端口说明:8080端口同80端口,是被用于www代理服务的,可以实现网页浏览,经常在访问某个网站或使用代理服务器的时候,会加上“:8080”端口号。
端口漏洞:8080端口可以被各种病毒程序所利用,比如Brown Orifice(BrO)特洛伊木马病毒可以利用8080端口完全遥控被感染的计算机。另外,RemoConChubo,RingZero木马也可以利用该端口进行攻击。
操作建议:一般我们是使用80端口进行网页浏览的,为了避免病毒的攻击,我们可以关闭该端口。
《完》
本文出自 “敲完代码好睡觉zzz” 博客,请务必保留此出处http://2627lounuo.blog.51cto.com/10696599/1775959
以上是关于基于UDP协议之——socket编程的主要内容,如果未能解决你的问题,请参考以下文章
java 25 - 4 网络编程之 UDP协议传输的代码优化