学习笔记12-学习《精通UNIX下C语言编程及项目实践》

来源:互联网 发布:淘宝卖家搞笑回复 编辑:程序博客网 时间:2024/06/05 18:56

UDP协议使用函数sendto发送数据, 使用函数recvfrom接收数据.

  函数sendto的原型如下

代码:
#include <sys/types.h>
#include <sys/socket.h>
int  sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);

  通过函数sendto发送数据时总能立即成功返回. 注意, 这里'成功'的含义是指数据被成功的发送, 并不保证对方套接字能成功的接收, 甚至不保证对方套接字协议信息的正确性.

  函数recvfrom的原型如下

代码:
#include <sys/types.h>
#include <sys/socket.h>
int  recvfrom(int  s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);

  函数recvfrom默认以阻塞方式读取数据, 成功时返回接收数据的字节长度. 成功时返回0, 否则返回-1并置errno.

  UDP协议的基础编程模型

  UPD是无连接的通信协议, 也采用客户机-服务器模式, 几个关键步骤如下:

  (1) 创建UDP服务器套接字

  在UDP通信模型中,服务器端进程需要完成创建套接字, 命名套接字, 等操作后才能调用接收客户端的数据, 这里整合成一个函数CreateUdpSock.

代码:
int CreateUdpSock(int *pnSock, int nPort){
        struct sockaddr_in addrin;
        struct sockaddr *paddr = (struct sockaddr *)&addrin;
 
        assert(pnSock != NULL && nPort > 0);
        memset(&addrin, 0, sizeof(addrin));
        addrin.sin_family = AF_INET;
        addrin.sin_addr.s_addr = htonl(INADDR_ANY);
        addrin.sin_port = htons(nPort);
 
        assert((*pnSock = socket(AF_INET, SOCK_DGRAM, 0)) > 0);
        if(bind(*pnSock, paddr, sizeof(addrin)) >= 0)
                 return 0;
        close(*pnSock);
 
        return 1;
}

  其中, 参数pSock回传创建的侦听套接字描述符, 整型nPort指定了套接字的侦听端口. 调用成功时返回0, 否则关闭套接字并返回其他值.

  (2)发送UDP协议数据

  这里设计了一个封装了sendto系统调用的函数SendMsgByUdp.

代码:
int SendMsgByUdp(void *pMsg, int nSize, char *szAddr, int nPort){
        int nSock, ret;
        struct sockaddr_in addrin;
 
        assert(pMsg != NULL && nSize > 0 && szAddr != NULL && nPort > 0);
 
        assert((nSock = socket(AF_INET, SOCK_DGRAM, 0)) > 0);
        memset(&addrin, 0, sizeof(struct sockaddr));
        addrin.sin_family = AF_INET;
        addrin.sin_addr.s_addr = inet_addr(szAddr);
        addrin.sin_port = htons(nPort);
        if(sendto(nSock, pMsg, nSize, 0, (struct sockaddr *)&addrin, sizeof(addrin)) > 0)
                 ret = 0;
        else
                 ret = 1;
        close(nSock);
 
        return(ret);
}

  该函数向目的地址发送UDP数据包, 其中指针pMsg指向待发送的数据, 该数据长nSize字节, 目的地址的IP是szAddr, 目的端口是nPort. 调用成功时返回0, 否则返回其他值.

  (3) 接收UDP协议数据

  这里同样封装了系统调用recvfrom, 函数名为RecvMsgByUdp.

代码:
int RecvMsgByUdp(int nFile, void *pData, int *pnSize){
        int nSize;
 
        assert(nFile > 0);
        if((*pnSize = recvfrom(nFile, pData, *pnSize, 0, NULL, NULL)) > 0)
                 return 0;
        else
                 return 1;
}

  其中, nFile是服务器端的UDP套接字描述符, 指针pData指向接收数据的内存缓冲区, 参数pnSize是该缓冲区可容纳的最大容量. 调用成功时返回0, 并将接收到的数据存储在pData中, 参数pnSize回传接收到的字节长度; 否则返回其他值.

  (4) 实例

  这里设计一个基于UDP的客户端-服务器端的例子. 客户端进程每隔一秒向服务器发送数据报信息. 服务器进程先创建端口号为9999的UDP套接字, 然后循环接收数据报信息并打印在屏幕上.

  这里, 为了编程方便我们定义了一个头文件udp.h封装了上面三个函数.

代码:
[bill@billstone Unix_study]$ cat udp.h
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
 
int CreateUdpSock(int *pnSock, int nPort);
int SendMsgByUdp(void *pMsg, int nSize, char *szAddr, int nPort);
int RecvMsgByUdp(int nFile, void *pData, int *pnSize);
[bill@billstone Unix_study]$

  UDP客户端程序如下

代码:
[bill@billstone Unix_study]$ cat udpk1.c
#include "udp.h"
 
int main(void)
{
        int ret, i = 0;
        char szBuf[100];
 
        while(1){
                sprintf(szBuf, "第%d次发送", i);
                ret = SendMsgByUdp(szBuf, strlen(szBuf), "127.0.0.1", 9999);
                if(ret == 0)
                        printf("Send UDP Success: %s/n", szBuf);
                else
                        printf("Send UDP Failed: %s/n", szBuf);
 
                sleep(1);
                i++;
        }
}

[bill@billstone Unix_study]$
  UDP服务器端程序如下

代码:
[bill@billstone Unix_study]$ cat udps1.c
#include "udp.h"
 
int main(void)
{
        int nSock, nSize;
        char szBuf[256];
 
        CreateUdpSock(&nSock, 9999);
        nSize = sizeof(szBuf);
        memset(szBuf, 0, sizeof(szBuf));
        while(RecvMsgByUdp(nSock, szBuf, &nSize) == 0){
                printf("Recv UDP Data: [%d] [%s]/n", strlen(szBuf), szBuf);
                nSize = sizeof(szBuf);
                memset(szBuf, 0, sizeof(szBuf));
        }
 
        close(nSock);
 
        return 0;
}
[bill@billstone Unix_study]$

  首先运行客户端程序udpk1

代码:
[bill@billstone Unix_study]$ gcc -o udpk1 udpk1.c udp.c
[bill@billstone Unix_study]$ ./udpk1
Send UDP Success: 第0次发送
Send UDP Success: 第1次发送
Send UDP Success: 第2次发送
Send UDP Success: 第3次发送
Send UDP Success: 第4次发送      // 此时服务器程序才开始执行并实际接收数据
Send UDP Success: 第5次发送
Send UDP Success: 第6次发送
Send UDP Success: 第7次发送
Send UDP Success: 第8次发送
Send UDP Success: 第9次发送

  接着在另一个终端运行服务器程序udps1

代码:
[bill@billstone Unix_study]$ gcc -o udps1 udps1.c udp.c
[bill@billstone Unix_study]$ ./udps1
Recv UDP Data: [9] [第4次发送]
Recv UDP Data: [9] [第5次发送]
Recv UDP Data: [9] [第6次发送]
Recv UDP Data: [9] [第7次发送]
Recv UDP Data: [9] [第8次发送]
原创粉丝点击