Linux 基本UDP套接字编程

来源:互联网 发布:淘宝匿名评价id采集 编辑:程序博客网 时间:2024/03/29 08:52

UDP(User Datagram Protocol) : 用户数据报协议,是一种无连接,不可靠的数据传输服务。与TCP不同的是,它不需要建立连接就可以直接传输数据,也就不存在关闭连接之类的问题。
常见的UDP程序有:DNS,NFS,SNMP。

一、recvfrom 和 sendto 函数

  recvfrom 和 sendto 函数主要用于UDP数据的读写操作,一个用于接收网络数据,一个用于向对方发生数据。虽然都可以用于TCP,但一般不这么做。

/* recvfrom  * 返回值:成功则为读到的字节数(可以为0),错误则为-1; * 函数定义: */# include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags,                 struct sockaddr *from, socklen_t *addrlen);/* 说明 * sockfd:要接收数据的套接字描述符; * buff:接收数据缓冲区; * nbytes:请求接收数据字节个数(缓冲区大小); * flag:设置一些参数,一般为0; * from:数据发送者的套接字地址结构; * addrlen:from的长度/字节数(值-结果参数); * recvfrom的返回值可以是0,即没有数据只有数据报头部的UDP报文; * 若是不关心数据发送者地址,from可以为空,相应addrlen也要为空; *//* sendto * 返回值:成功则为发送的字节数(可以为0),错误则为-1; * 函数定义: */# include <sys/socket.h>ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,               const struct sockaddr *to, socklen_t addrlen);/* 说明 * sockfd:要发送数据的套接字描述符; * buff:发送数据缓冲区; * nbytes:可以发送数据字节个数; * flag:设置一些参数,一般为0; * from:数据接收者的套接字地址结构; * addrlen:from的长度/字节数,注意是整数不是指针; * sendto的返回值也可以是0,即写一个空的数据报; */

二、简单的udp回射客户/服务器程序

  基于UDP协议的客户/服务器程序与基于TCP的不同,不需要建立连接,也不需要分别对不同的客户端进行分别处理,只是简单的进行数据发送和接收。一般来说大多数TCP服务器都是并发的,因为它需要与多个客户端保持连接,而UDP服务器多是迭代的,即只需要不停的循环进行接收数据就行。UDP层中隐含有排队发生,每一个UDP套接字都有一个接收缓冲区,到达的数据报都进入这个缓冲区,而recvfrom函数就从这个缓冲区按照顺序读取数据报。因此不管是哪一个客户端发来的数据都放在缓冲区,由recvfrom函数来读取,不需要类似tcp中建立子进程来处理每一个客户端。

/* 服务器 */#include <sys/socket.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <netinet/in.h>   //INADDR_ANYvoid udp_echo(int sockfd, struct sockaddr* pcliaddr, socklen_t clilen);int main(int argc, char **argv){    int sockfd;    struct sockaddr_in servaddr, cliaddr;    sockfd = socket(AF_INET, SOCK_DGRAM, 0);    bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    servaddr.sin_port = htons(6666);    bind(sockfd, (struct sockaddr*) &servaddr, sizeof(servaddr));    udp_echo(sockfd, (struct sockaddr*) &cliaddr, sizeof(cliaddr));    return 0;}void udp_echo(int sockfd, struct sockaddr* pcliaddr, socklen_t clilen){    int n;    char buf[256];    for(;;)    {        n = recvfrom(sockfd, buf, 256, 0, pcliaddr, &clilen);        write(fileno(stdout), buf, n);        sendto(sockfd, buf, n, 0, pcliaddr, clilen);    }}
/* 客户端 */#include <stdio.h>#include <sys/socket.h>#include <unistd.h>#include <string.h>#include <netinet/in.h>int main(int argc, char** argcv){    int sockfd;    struct sockaddr_in servaddr;    char *ipaddr = "192.168.110.128";    int n, m;    bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(6666);    inet_pton(AF_INET, ipaddr, &servaddr.sin_addr);    //ip address need to be transfromed from char* to bytes    sockfd = socket(AF_INET, SOCK_DGRAM, 0);    char sendbuf[256], recvbuf[256];    while((n = read(fileno(stdin), sendbuf, 256)) != 0)    {        sendto(sockfd, sendbuf, n, 0, (struct sockaddr*) &servaddr, sizeof(servaddr));        m = recvfrom(sockfd, recvbuf, 256, 0, NULL, NULL);        write(fileno(stdout), recvbuf, m);    }}

对于客户端来说,进程并没有为套接字sockfd绑定一个本地端口地址,与TCP一样,内核会给它分配一个临时端口,可以在代码中显示的调用bind,但是一般不会这样做。
需要注意的是,对于IO来说,一般都存在缓冲区,因此有时最好不要连续调用读取函数,不然可能不会快速输出,需要强行输入缓冲区内容可以用fflush函数。

三、UDP connect

  udp中可以调用connect函数,不过结果与tcp中不大一样,没有3路握手过程,内核只是检查是否存在立即可知的错误,记录对端的IP地址和端口号,然后立即返回到调用进程。
  调用connect之后,不再使用sendto和recvfrom来进行数据的读写,一般用read,write,send等函数;当然一定要用sendto也是可以的,但是不需要再指定目的地址,因为连接之后,套机字已经保存了目的端地址,也因为如此,连接的套接字只能与固定端进行数据交换,需要更改目的地址可以多次调用connect;
  再次调用connect有两个目的:指定新的IP地址和端口号(tcp套接字connect只能调用一次),断开套接字(套接字地址结构的地址族设置为AF_UNSPEC)。
  调用connect的好处:已连接的套接字能够接收到返回的异步错误,未连接的不能,提高传输效率,增加系统稳定性(不会把非目的地址发来的数据当作返回数据)。
  UDP 调用 connect的作用;

0 0
原创粉丝点击