基于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

protocol0,因为前面两个参数已经可知协议是什么了;


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,其中:

  1. 公认端口(Well Known Ports):从0到1023,这类端口也常称之为常用端口,它们紧密绑定(binding)于一些特定的服务。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯,而23号端口是Telnet服务专用的;

  2. 注册端口(Registered Ports):从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的,这些端口多数没有明确的定义服务对象,不同程序可根据实际需要自己定义。例如:许多系统处理动态端口从1024左右开始;

  3. 动态或私有端口(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编程的主要内容,如果未能解决你的问题,请参考以下文章

网络编程之套接字socket

java 25 - 4 网络编程之 UDP协议传输的代码优化

基于UDP协议的socket套接字编程 基于socketserver实现并发的socket编程

网络 基于UDP协议的socket编程

基于UDP协议的socket套接字编程

基于TCP协议的Socket编程 / 基于UDP协议的Socket编程