学习笔记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次发送]
- 学习笔记12-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记01-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记02-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记03-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记04-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记05-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记06-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记07-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记08-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记09-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记10-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记11-学习《精通UNIX下C语言编程及项目实践》
- 学习笔记13-学习《精通UNIX下C语言编程及项目实践》
- 学习《精通UNIX下C语言编程及项目实践》- 总结与展望(提供下载)
- Unix初学者的迷惑——《精通Unix下C语言编程与项目实践》读
- Unix与Windows是什么关系——《精通Unix下C语言编程与项目实践
- Unix朝代歌《精通Unix下C语言编程与项目实践》读书笔记(7)
- 《精通Unix下C语言编程与项目实践》读书笔记(1)
- 学习笔记10-学习《精通UNIX下C语言编程及项目实践》
- 数据结构-稀疏矩阵(三元组表示)
- ubuntu 8.04建立mysql C开发环境
- 学习笔记11-学习《精通UNIX下C语言编程及项目实践》
- KMP算法实现
- 学习笔记12-学习《精通UNIX下C语言编程及项目实践》
- javascript 当前日期加(天、周、月、年)
- 数据库实用主义
- msdn技术资源库的一个小错误 Membership.GetAllUsers
- 学习笔记13-学习《精通UNIX下C语言编程及项目实践》
- java怎么获取剪切板数据代码示例
- 2>&1是怎么回事
- select中添加、删除option
- javascript 局部页面打印实现代码