Socket编程实践(12) --UDP编程基础
UDP特点
无连接,面向数据报(基于消息,不会粘包)的传输数据服务;
不可靠(可能会丢包, 乱序, 反复), 但因此普通情况下UDP更加高效;
UDP客户/服务器模型
UDP-API使用
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
/**实践: 实现一个基于UDP的echo回声server/client**/
//server端代码
void echoServer(int sockfd);
int main()
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
err_exit("socket error"); struct sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(8001);
if (bind(sockfd, (const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1)
err_exit("bind error"); echoServer(sockfd);
}
void echoServer(int sockfd)
{
char buf[BUFSIZ];
ssize_t recvBytes = 0;
struct sockaddr_in clientAddr;
socklen_t addrLen;
while (true)
{
memset(buf, 0, sizeof(buf));
addrLen = sizeof(clientAddr);
memset(&clientAddr, 0, addrLen);
recvBytes = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&clientAddr, &addrLen);
//假设recvBytes=0, 并不代表对端连接关闭, 由于UDP是无连接的
if (recvBytes < 0)
{
if (errno == EINTR)
continue;
else
err_exit("recvfrom error");
} cout << buf ;
if (sendto(sockfd, buf, recvBytes, 0,
(const struct sockaddr *)&clientAddr, addrLen) == -1)
err_exit("sendto error");
}
}
/**client端代码**/
void echoClient(int sockfd);
int main()
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
err_exit("socket error");
echoClient(sockfd);
cout << "Client exiting..." << endl;
}
void echoClient(int sockfd)
{
struct sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servAddr.sin_port = htons(8001);
char buf[BUFSIZ] = {0};
while (fgets(buf, sizeof(buf), stdin) != NULL)
{
if (sendto(sockfd, buf, strlen(buf), 0,
(const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1)
err_exit("sendto error");
memset(buf, 0, sizeof(buf));
int recvBytes = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
if (recvBytes == -1)
{
if (errno == EINTR)
continue;
else
err_exit("recvfrom error");
}
cout << buf ;
memset(buf, 0, sizeof(buf));
}
}
实践解析:编译执行server,在两个终端里各开一个client与server交互,能够看到server具有并发服务的能力。用<Ctrl+C>关闭server,然后再执行server,此时client还能和server联系上。和前面TCP程序的执行结果相比較。我们能够体会无连接的含义。udp 协议来说。server与client 的界限更模糊了,仅仅要知道对等方地址(ip和port) 都能够主动发数据。
UDP编程注意事项
1.UDP报文可能会丢失(超时重传)、反复、乱序(维护一个序号)
2.UDP缺乏流量控制:当缓冲区写满以后,由于UDP没有流量控制机制,因此会覆盖缓冲区。
3.UDP协议数据报文截断:假设对端发送的UDP数据报大于本地接收缓冲区,报文可能被截断,后面的部分会丢失(而不是像我们想象的下一次能够接收到)。
4.recvfrom能够返回0。并不代表连接关闭。由于UDP是无连接的, 代表发送端没有发送不论什么数据[sendto能够发送数据0包(仅仅含有UDP+IP首部40B)]。
5.ICMP异步错误
观察现象:使用上例,关闭UDP服务端,启动客户端,从键盘接受数据后。再发送数据。假设recvfrom中flags标志为0, 且client端没有调用connect的情况下, UDP客户端堵塞在recvfrom位置(见測试代码3);
说明:
1)UDP发送报文的时,仅仅把数据copy到发送缓冲区。
在服务器没有起来的情况下,能够发送成功。
2)所谓ICMP异步错误是指:发送的报文的时候,没有错误,接受报文recvfrom的时候,回收到ICMP应答.
3)异步的错误。无法返回未连接的套接字, 因此假设上例我们调用了connect, 是能够收到该异步ICMP报文的;
6.UDP调用connect
1)UDP调用connet,并没有三次握手,仅仅是维护了一个(和对等方的)状态信息, 因此我们能够看到即使server没有开启, client端的connect依旧还能够正确返回的!(測试代码如測试代码2)
2)一但调用connect, 发送能够使用send/write, 接收能够使用recv/read函数(见測试代码3)
7.UDP外出接口的确定:
假设客户端有多个IP地址。由connect /sendto 函数提供的远程地址的參数。系统会选择一个合适的出口。比方Server的IP是192.168.2.10, 而客户端如今的IP有 192.168.1.32 和 192.168.2.75 那么会自己主动选择192.168.2.75 这个IP出去。
/**測试1: 測试注意点3, UDP报文截断, recvfrom返回-1, errno值为EAGAIN**/
int main()
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
err_exit("socket error"); struct sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(8001);
if (bind(sockfd, (const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1)
err_exit("bind error");
//给自己发送数据
if (sendto(sockfd, "ABCDE", 5, 0,
(const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1)
err_exit("sendto error"); for (int i = 0; i < 5; ++i)
{
char ch;
int recvBytes = recvfrom(sockfd, &ch, 1, MSG_DONTWAIT, NULL, NULL);
if (recvBytes == -1)
{
if (errno == EINTR)
continue;
else if (errno == EAGAIN)
err_exit("recvfrom error");
}
else
cout << "char = " << ch << ", recvBytes = " << recvBytes << endl;
}
}
/**測试2:将client端echoClient函数的代码改造例如以下, 注意是在server端尚未开启时执行该程序**/
void echoClient(int sockfd)
{
struct sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servAddr.sin_port = htons(8001);
// UDP client端调用connect
if (connect(sockfd, (const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1)
err_exit("connect error"); char buf[BUFSIZ] = {0};
while (fgets(buf, sizeof(buf), stdin) != NULL)
{
if (sendto(sockfd, buf, strlen(buf), 0,
(const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1)
err_exit("sendto error");
memset(buf, 0, sizeof(buf));
int recvBytes = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
if (recvBytes == -1)
err_exit("recvfrom error");
cout << buf ;
memset(buf, 0, sizeof(buf));
}
}
/**測试3: client端在调用connect之后调用send, 而不是send**/
void echoClient(int sockfd)
{
struct sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servAddr.sin_port = htons(8001);
// UDP client端调用connect
if (connect(sockfd, (const struct sockaddr *)&servAddr, sizeof(servAddr)) == -1)
err_exit("connect error"); char buf[BUFSIZ] = {0};
while (fgets(buf, sizeof(buf), stdin) != NULL)
{
if (send(sockfd, buf, strlen(buf), 0) == -1)
err_exit("send error");
memset(buf, 0, sizeof(buf));
int recvBytes = recv(sockfd, buf, sizeof(buf), 0);
if (recvBytes == -1)
err_exit("recv error");
cout << buf ;
memset(buf, 0, sizeof(buf));
}
}
版权声明:本文博客原创文章,博客,未经同意,不得转载。
Socket编程实践(12) --UDP编程基础的更多相关文章
- 基于socket的TCP和UDP编程
一.概述 TCP(传输控制协议)和UDP(用户数据报协议是网络体系结构TCP/IP模型中传输层一层中的两个不同的通信协议. TCP:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工的字节流 ...
- (转)基于socket的TCP和UDP编程
一.概述 TCP(传输控制协议)和UDP(用户数据报协议是网络体系结构TCP/IP模型中传输层一层中的两个不同的通信协议. TCP:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工的字节流 ...
- Linux系统编程(12)——shell基础
Shell的作用是解释执行用户的命令,用户输入一条命令,Shell就解释执行一条,这种方式称为交互式(Interactive),Shell还有一种执行命令的方式称为批处理(Batch),用户事先写一个 ...
- 第二章 C语言编程实践
上章回顾 宏定义特点和注意细节 条件编译特点和主要用处 文件包含的路径查询规则 C语言扩展宏定义的用法 第二章 第二章 C语言编程实践 C语言编程实践 预习检查 异或的运算符是什么 宏定义最主要的特点 ...
- linux系统socket通信编程实践
简单介绍并实现了基于UDP(TCP)的windows(UNIX下流程基本一致)下的服务端和客户端的程序,本文继续探讨关于UDP编程的一些细节. 下图是一个简单的UDP客户/服务器模型: 我在这里也实现 ...
- 【深入浅出Linux网络编程】 “实践 -- TCP & UDP”
通过上一篇博客的学习,你应该对基于epoll的事件触发机制有所掌握,并且通过阅读sio.c/sio.h应该也学会了如何封装epoll以及如何通过设计令epoll更加实用(用户回调,用户参数). 简单回 ...
- C# socket编程实践
C# socket编程实践——支持广播的简单socket服务器 在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# ...
- Socket编程实践(6) --TCP服务端注意事项
僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...
- 【网络编程1】网络编程基础-TCP、UDP编程
网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...
随机推荐
- 浅谈MySQL 数据库性能优化
MySQL数据库是 IO 密集型的程序,和其他数据库一样,主要功能就是数据的持久化以及数据的管理工作.本文侧重通过优化MySQL 数据库缓存参数如查询缓存,表缓存,日志缓存,索引缓存,innodb缓存 ...
- RobotFramework 自定义Library
RobotFramework 主要使用Python,这里简单自定义Library,以扩充RobotFramework的功能 新建一个python类,自定义需要的方法 例如: 保存成TestLibrar ...
- 2015 Multi-University Training Contest 1
最近真是太废柴了,题没做几道,也没学什么新知识,多校做了三场也没总结~诶!好好学吧! 多校第一场感觉被完虐...orz... Hdu 5288 OO’s Sequence 题目链接:http://ac ...
- Java Design Demo -简单的队列-异步多任务队列(java android)
简单的单线程队列 -- 工作的时候遇到劣质打印机.给打印机发消息,打印机就会打印,如果在打印机还在打印的时候,就 再发消息打印,就会出现消息丢失.所以需要给上一个任务一些处理的间隔时间. 单线程的消息 ...
- 【UVA】12299-RMQ with Shifts(线段树)
改动的时候因为数据非常小,所以能够直接暴力改动,查询的时候利用线段树即可了. 14337858 option=com_onlinejudge&Itemid=8&page=show_pr ...
- 细说在兄弟连搞上PHP的那些事儿
(据说大家都是这么开头的)又到6月份了,想想自己毕业已经整整一年时间了,这一年可能会是我一生中印象最最深刻的一年,从满怀希望地踏入社会到自信满满 地开始第一份工作再到2个多月后又灰溜溜地辞去工作,回到 ...
- clear、REFRESH、free区别
clear可以清楚一个工作区或变量.但是如果该内表是带表头的,清空内表时需要在该内表后加[].例如:clear gt_tab[]. free可以清空带表头的内表但是不会清空这个带表头内表的表头,但是也 ...
- 【Unity3D】【NGUI】UILabel
原文:http://www.tasharen.com/forum/index.php?topic=6706.0 NGUI讨论群:333417608 概述 UILabel是用来显示文本的脚本,继承自UI ...
- win32创建控件的一些问题
在我们使用CreateWindow();像一般控件建Windows扩展控件的时候我们会发现控件没有创建成功 这是因为我们没有对Windows扩展控件库进行初始化,这要我们使用InitCommonCon ...
- 低头看—SQL视频
迷迷糊糊半个月过去了,耿大姐的数据库视频也在一知半解中看完.虽然耿大妈讲的很详细,很细心,但是我还是时不时的犯困(还不如看儿童英语动画片有精神呢)视频看是看完了,但东西不是自己的.这个时候就需要“颗粒 ...