SylixOS UDP网络应用编程

        在使用UDP编写应用程序时与TCP编写应用程序有着本质的差异,其原因在于这两个传输层之间的差异:UDP是无连接不可靠的数据报协议,不同于TCP提供的面向连接的可靠字节流。然而有些场合更适合使用UDP,使用UDP编写的一些流行的应用程序有DNS(域名系统)、NFS(网络文件系统)、SNMP(简单网络管理协议)。

        客户端无需与服务器端建立连接,但是需要指定目的地址(服务器地址),并且客户端只管给服务器发送数据报。

        服务器端不接受来自客户端的连接,而是只管等待来自某个客户端的数据。

如图 3.3所示给出了典型的UDP客户端与服务器端程序的函数使

         bind函数把一个本地协议地址赋予一个套接字,协议地址的含义只取决于协议本身。对于网际协议,协议地址是32位的IPv4地址或128位IPv6地址与16位的TCP或UDP端口的组合。调用bind函数可以指定IP地址或端口,可以两者都指定,也可以都不指定。

#include <sys/socket.h>
int  bind(int s, const struct sockaddr *name, socklen_t namelen);
函数bind原型分析:
	此函数成功返回0,失败返回-1并设置错误号;
	参数s是套接字(socket函数返回);
	参数name是一个指向特定协议域的sockaddr结构体类型的指针;
	参数namelen 表示name结构的长度。

        如图 所示给出了典型的UDP客户端与服务器端程序中recvfrom与sendto使用过程(类似于标准的read与write函数,不过需要额外三个参数)。

#include <sys/socket.h>
ssize_t  recvfrom(int  s, void	*mem, size_t  len, int  flags,
                     struct sockaddr *from, socklen_t *fromlen);
函数recvfrom原型分析:
	此函数成功时返回读取到数据的字节数,失败时返回-1并设置错误号;
	参数s是套接字(socket函数返回);
	参数mem是指向读入缓冲区的指针;
	参数len表示读取数据的字节长度;
	参数flags用于指定消息类型,当不关心此参数时可以将其设置为0,如果需要关心此参数请将其值配置为以下值:
	MSG_PEEK:数据预读但不删除数据;
	MSG_WAITALL:等待所有数据到达后才返回;
	MSG_OOB:带外数据;
	MSG_DONTWAIT:不阻塞的接收数据;
	MSG_MORE:有更多的数据需要发送。
	参数from用于表示UDP数据报发送者的协议地址(例如IP地址及端口号);
	参数fromlen用于指定from地址大小的指针。

        由于UDP是无连接的,因此recvfrom函数返回值为0也是有可能的。如果from参数是一个空指针,那么相应的长度参数fromlen也必须是一个空指针,表示我们并不关心数据发送者的协议地址。

#include <sys/socket.h>
ssize_t  sendto(int  s, const void *data, size_t  size, int  flags,
                   const struct sockaddr *to, socklen_t  tolen);
函数sendto原型分析:
	此函数成功时返回读取到数据的字节数,失败时返回-1并设置错误号;
	参数s是套接字(socket函数返回);
	参数data是指向写入数据缓冲区的指针;
	参数size表示写入数据的字节长度;
	参数flags用于指定消息类型,当不关心此参数时可以将其设置为0,如果需要关心此参数请将其值配置为以下值:
	MSG_PEEK:数据预读但不删除数据;
	MSG_WAITALL:等待所有数据到达后才返回;
	MSG_OOB:带外数据;
	MSG_DONTWAIT	:不阻塞的接收数据;
	MSG_MORE:有更多的数据需要发送。
	参数to用于表示UDP数据报接收者的协议地址(例如IP地址及端口号);
	参数tolen用于指定to地址长度。

         sendto函数写一个长度为0的数据报是可行的,这导致一个只包含IP头部(对于IPv4通常为20个字节,对于IPv6通常为40个字节)和一个8字节UDP头部但没有数据的IP数据报。

        UDP回射程序模型,如下图所示。客户端与服务器遵循该流程完成回射数据的接收与回显。

        客户端程序使用sendto函数将“SylixOS Hello!”发送给服务器,并使用recvfrom读回服务器的回射,最后将收到的回射信息“SylixOS Hello!”输出。

        服务器程序使用recvfrom函数读入来自客户端的“SylixOS Hello!”数据,并通过sendto把收到的数据发送给客户端程序。

                                                       UDP回射服务器程序

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>

#define __UDP_ECHO_TYPE_CLIENT         	1  		/* 客户端模式                	*/
#define __UDP_ECHO_TYPE_SERVER        	2 		/* 服务器模式                	*/
												/* 当前模式选择              	*/
#define __UDP_ECHO_TYPE                	(__UDP_ECHO_TYPE_SERVER)
              									/* 客户端 IP 地址            	*/
#define __UDP_ECHO_IP_CLIENT           	"192.168.1.16"
              									/* 服务器 IP 地址            	*/
#define __UDP_ECHO_IP_SERVER          	"192.168.1.17"
#define __UDP_ECHO_PORT_CLIENT        	8000 	/* 客户端端口号                	*/
#define __UDP_ECHO_PORT_SERVER         	8001	/* 服务器端口号               	*/
#define __UDP_ECHO_BUFF_SIZE_CLIENT	257		/* 客户端接收缓冲区大小      	*/
#define __UDP_ECHO_BUFF_SIZE_SERVER   	257		/* 服务器接收缓冲区大小        	*/
static int  __UdpEchoServer (void)
{
    int                  	iRet      = -1;			/* 操作结果                    	*/
    int                	sockFd    = -1;     		/* socket 描述符          	  	*/
										/* 地址结构大小            	  	*/
socklen_t			uiAddrLen = sizeof(struct sockaddr_in);     
register ssize_t		sstRecv   = 0;      		/* 接收到的数据长度        	  	*/
												/* 接收缓冲区                  	*/
char                	cRecvBuff[__UDP_ECHO_BUFF_SIZE_SERVER] ={0};    
struct sockaddr_in 	sockaddrinLocal; 		/* 本地地址               	  	*/ 
struct sockaddr_in	sockaddrinRemote;   	/* 远端地址                   	*/

    fprintf(stdout, "UDP echo server start.\n");
												/* 创建 socket           	  	*/
sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 
if (sockFd < 0) {							/* 操作失败                   	*/
        printf("UDP echo server socket error.\n");
        return  (-1);    							/* 错误返回                  	*/
    }
    /*
     * 初始化本地地址结构
     */
    												/* 清空地址信息              	*/
    memset(&sockaddrinLocal, 0, sizeof(sockaddrinLocal));
    												/* 地址结构大小              	*/
    sockaddrinLocal.sin_len         	= sizeof(struct sockaddr_in);
sockaddrinLocal.sin_family     	= AF_INET;	/* 地址族                     	*/
												/* 网络地址                  	*/
sockaddrinLocal.sin_addr.s_addr	= INADDR_ANY;           
												/* 绑定服务器端口             	*/
    sockaddrinLocal.sin_port        	= htons(__UDP_ECHO_PORT_SERVER);
iRet = bind(sockFd,
                 (struct sockaddr *)&sockaddrinLocal,
			    sizeof(sockaddrinLocal)); 		/* 绑定本地地址与端口         	*/
    if (iRet < 0) {                    			/* 绑定操作失败               	*/
        close(sockFd);    						/* 关闭已经创建的 socket    	*/
        fprintf(stderr, "UDP echo server bind error.\n");
        return  (-1);                        		/* 错误返回                  	*/
    }
    for (;;) {
        											/* 清空接收缓冲区             	*/
memset(&cRecvBuff[0], 0, __UDP_ECHO_BUFF_SIZE_SERVER);                   
sstRecv = recvfrom(sockFd,
                               (void *)&cRecvBuff[0],
                               __UDP_ECHO_BUFF_SIZE_SERVER,
                               0,
                               (struct sockaddr *)&sockaddrinRemote,
                               &uiAddrLen);   		/* 从远端接收数据             	*/
        if (sstRecv <= 0) {                    		/* 接收数据失败                	*/
            if ((errno != ETIMEDOUT  ) &&
                (errno != EWOULDBLOCK)) {      	/* 非超时与非阻塞              	*/
                 close(sockFd);               		/* 关闭已经创建的 socket     	*/
                 fprintf(stderr, "UDP echo server recvfrom error.\n");
                 return  (-1);
            }
            continue;
        }
        sendto(sockFd,
                 (const void *)&cRecvBuff[0],
                 sstRecv,
                 0,
                 (const struct sockaddr *)&sockaddrinRemote,
                 uiAddrLen);
    }
    return  (0);
}

                                                UDP回射客户端程序

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>

#define __UDP_ECHO_TYPE_CLIENT        	1  		/* 客户端模式                 	*/
#define __UDP_ECHO_TYPE_SERVER       	2 		/* 服务器模式                 	*/
												/* 当前模式选择              	*/
#define __UDP_ECHO_TYPE                	(__UDP_ECHO_TYPE_CLIENT)
              									/* 客户端 IP 地址           	*/
#define __UDP_ECHO_IP_CLIENT         	"192.168.1.16"
              									/* 服务器 IP 地址            	*/
#define __UDP_ECHO_IP_SERVER           	"192.168.1.17"
#define __UDP_ECHO_PORT_CLIENT     		8000 	/* 客户端端口号              	*/
#define __UDP_ECHO_PORT_SERVER        	8001	/* 服务器端口号             	*/

#define __UDP_ECHO_BUFF_SIZE_CLIENT   	257		/* 客户端接收缓冲区大小      	*/
#define __UDP_ECHO_BUFF_SIZE_SERVER	257		/* 服务器接收缓冲区大小      	*/

static int  __UdpEchoClient (void)
{
    int                	 sockFd      = -1;     	/* socket 描述符         	  	*/
        											/* 地址结构大小               	*/
    socklen_t           	 uiAddrLen	= sizeof(struct sockaddr_in);
    register ssize_t   	 sstRecv     = 0;    		/* 接收到的数据长度           	*/
    register ssize_t  	 sstSend     = 0;  		/* 接收到的数据长度         	*/
    												/* 需要发送的字符串          	*/
    const char          	*pcSendData = "SylixOS Hello!\n"; 
    												/* 接收缓冲区                 	*/
    char                 	 cRecvBuff[__UDP_ECHO_BUFF_SIZE_CLIENT] ={0};
    struct sockaddr_in	 sockaddrinRemote;		/* 远端地址                  	*/
    
    fprintf(stdout, "UDP echo client start.\n");
                      								/* 创建 socket            	  	*/
    sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sockFd < 0) {
        fprintf(stderr, "UDP echo client socket error.\n");
        return  (-1);
    }
    /*
     * 初始化远端地址结构
     */
    memset(&sockaddrinRemote, 0, sizeof(sockaddrinRemote));
     											/* 地址转换错误               	*/
    if (!inet_aton(__UDP_ECHO_IP_SERVER, &sockaddrinRemote.sin_addr)) {
        close(sockFd);              				/* 关闭已经创建的 socket      	*/
        fprintf(stderr, "UDP echo client get addr error.\n");
        return  (-1);                   			/* 错误返回                   	*/
    }
          										/* 地址结构大小               	*/
    sockaddrinRemote.sin_len   	= sizeof(struct sockaddr_in);
    sockaddrinRemote.sin_family	= AF_INET;		/* 地址族                     	*/
       											/* 绑定服务器端口             	*/
    sockaddrinRemote.sin_port	= htons(__UDP_ECHO_PORT_SERVER);
    for (;;) {
        fprintf(stdout, "Send Data: %s", pcSendData);
        sstRecv = strlen(pcSendData);			/* 获取发送字符串长度         	*/
        sstSend = sendto(sockFd,
                            (const void *)pcSendData,
                             sstRecv,
                             0,
                            (const struct sockaddr *)&sockaddrinRemote,
                             uiAddrLen); 			/* 发送数据到指定的服务器端    	*/
        if (sstSend <= 0) { 						/* 发送数据失败               	*/
            if ((errno != ETIMEDOUT  ) &&
                (errno != EWOULDBLOCK)) {			/* 非超时与非阻塞              	*/
                 close(sockFd);                  	/* 关闭已经创建的 socket	  	*/
                
                 fprintf(stderr, "UDP echo client sendto error.\n");
                 return  (-1);    					/* 错误返回                   	*/
            }
            continue;        						/* 超时或非阻塞后重新运行      	*/
        }
        memset(&cRecvBuff[0], 0, __UDP_ECHO_BUFF_SIZE_CLIENT);
        sstRecv = recvfrom(sockFd,
                               (void *)&cRecvBuff[0],
                               __UDP_ECHO_BUFF_SIZE_SERVER,
                               0,
                              (struct sockaddr *)&sockaddrinRemote,
                               &uiAddrLen);		    	/* 从远端接收数据             	*/
        if (sstRecv <= 0) {     					/* 接收数据失败                	*/
            if ((errno != ETIMEDOUT  ) &&
                (errno != EWOULDBLOCK)) {   		/* 非超时与非阻塞               */
                 close(sockFd);      				/* 关闭已经创建的 socket       */
                 fprintf(stderr, "UDP echo client recvfrom error.\n");
                 return  (-1);      				/* 错误返回                     */
            }
            continue;                     			/* 超时或非阻塞后重新运行        */
        }
        fprintf(stdout, "Recv Data: ");
        cRecvBuff[sstRecv] = 0;
        fprintf(stdout, "%s\n", &cRecvBuff[0]);
        sleep(5);                     				/* 休眠一段时间                  */
    }
    return  (0);
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值