第六章 面向非连接的协议
6.1 非连接通信的优点:
1)更加简单:不需要建立连接
2)富有弹性:每一次的消息发送都可以定向到不同的接收者
3)高效:因为不需要建立和拆除连接,所以避免了大量在网络中传递消息分组的开销
4)快速:因为不需要建立和拆除连接,所以只有消息本身被发送.
5)具备广播能力.
6.2 缺点:
1)通信过程不可靠.
2)多数据报的无序性.
3)消息的尺寸有限制:理论上UDP数据报的最大尺寸略小于64KB,但实际上许多UNIX主机只提供32KB的最大尺寸,有的甚至只有8KB,最终套接口接收程序还会把这个最大尺寸限制为接收缓冲区的大小,许多程序UDP消息尺寸只有512B或更小.
6.3 sendto(2)函数
#include <sys/types.h>
#include <sys/socket.h>
int sendto(int s, const void *msg, int len, unsigned flags,
const struct sockaddr *to, int tolen)
参数s是套接口的描述符,它是由socket()函数生成的
参数msg是指向容纳所要发送的数据报信息的缓冲区的指针
参数len数据报信息的长度
参数flags一般为0
参数to接收端的地址
参数tolen地址长度
调用成功,返回值为发送的字节数,失败返回-1,可以通过检查errno的值来判断错误原因.
sendto()的flags参数的值
flags 十六进制 意义
0 0x0000 正常--无特定选项
MSG_OOB 0x0001 外理带外数据
MSG_DONTROUTE 0x0004 旁路路由,使用直接接口
MSG_DONTWAIT 0x0040 非阻塞,等待写入
MSG_NOSIGNAL 0x4000 当另一端断开连接时,不生成SIGPIPE信号
6.4 recvfrom(2)函数
#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int s, const void *buf, int len, unsigned flags,
const struct sockaddr *from, int fromlen)
参数s是套接口的描述符
参数buf是指向容纳所要接收的数据报信息的缓冲区的指针
参数len缓冲区的长度
参数flags一般为0
参数from发送端的地址
参数fromlen地址长度
调用成功,返回值为接收的字节数,失败返回-1,可以通过检查errno的值来判断错误原因.
sendto()的flags参数的值
flags 十六进制 意义
0 0x0000 正常--无特定选项
MSG_OOB 0x0001 外理带外数据
MSG_PEEK 0x0002 读入一个数据报,但不从内核中删除
MSG_WAITALL 0x0100 进行阻塞,直到所有的要求满足
MSG_ERRQUEUE 0x2000 从错误队列中接收一个分组
MSG_NOSIGNAL 0x4000 当另一端断开连接时,不生成SIGPIPE信号
代码实例
clien
server
6.1 非连接通信的优点:
1)更加简单:不需要建立连接
2)富有弹性:每一次的消息发送都可以定向到不同的接收者
3)高效:因为不需要建立和拆除连接,所以避免了大量在网络中传递消息分组的开销
4)快速:因为不需要建立和拆除连接,所以只有消息本身被发送.
5)具备广播能力.
6.2 缺点:
1)通信过程不可靠.
2)多数据报的无序性.
3)消息的尺寸有限制:理论上UDP数据报的最大尺寸略小于64KB,但实际上许多UNIX主机只提供32KB的最大尺寸,有的甚至只有8KB,最终套接口接收程序还会把这个最大尺寸限制为接收缓冲区的大小,许多程序UDP消息尺寸只有512B或更小.
6.3 sendto(2)函数
#include <sys/types.h>
#include <sys/socket.h>
int sendto(int s, const void *msg, int len, unsigned flags,
const struct sockaddr *to, int tolen)
参数s是套接口的描述符,它是由socket()函数生成的
参数msg是指向容纳所要发送的数据报信息的缓冲区的指针
参数len数据报信息的长度
参数flags一般为0
参数to接收端的地址
参数tolen地址长度
调用成功,返回值为发送的字节数,失败返回-1,可以通过检查errno的值来判断错误原因.
sendto()的flags参数的值
flags 十六进制 意义
0 0x0000 正常--无特定选项
MSG_OOB 0x0001 外理带外数据
MSG_DONTROUTE 0x0004 旁路路由,使用直接接口
MSG_DONTWAIT 0x0040 非阻塞,等待写入
MSG_NOSIGNAL 0x4000 当另一端断开连接时,不生成SIGPIPE信号
6.4 recvfrom(2)函数
#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int s, const void *buf, int len, unsigned flags,
const struct sockaddr *from, int fromlen)
参数s是套接口的描述符
参数buf是指向容纳所要接收的数据报信息的缓冲区的指针
参数len缓冲区的长度
参数flags一般为0
参数from发送端的地址
参数fromlen地址长度
调用成功,返回值为接收的字节数,失败返回-1,可以通过检查errno的值来判断错误原因.
sendto()的flags参数的值
flags 十六进制 意义
0 0x0000 正常--无特定选项
MSG_OOB 0x0001 外理带外数据
MSG_PEEK 0x0002 读入一个数据报,但不从内核中删除
MSG_WAITALL 0x0100 进行阻塞,直到所有的要求满足
MSG_ERRQUEUE 0x2000 从错误队列中接收一个分组
MSG_NOSIGNAL 0x4000 当另一端断开连接时,不生成SIGPIPE信号
代码实例
clien
#include
<
stdio.h
>
#include < unistd.h >
#include < stdlib.h >
#include < errno.h >
#include < string .h >
#include < time.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < arpa / inet.h >
static void bail( const char * on_what)
{
fputs(strerror(errno), stderr);
fputs(":", stderr);
fputs(on_what, stderr);
fputc(' ', stderr);
exit(1);
}
int main( int argc, char * argv[])
{
int z;
int x;
char *srvr_addr = NULL;
struct sockaddr_in adr_srvr;
struct sockaddr_in adr;
int len_inet;
int s;
char dgram[512];
if (argc >= 2)
{
srvr_addr = argv[1];
}else
{
srvr_addr = "127.0.0.23";
}
memset(&adr_srvr, 0, sizeof(adr_srvr));
adr_srvr.sin_family = AF_INET;
adr_srvr.sin_port = htons(9090);
adr_srvr.sin_addr.s_addr = inet_addr(srvr_addr);
if (INADDR_NONE == adr_srvr.sin_addr.s_addr)
bail("bad address.");
len_inet = sizeof(adr_srvr);
s = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == s)
bail("socket()");
for (;;)
{
fputs(" Enter format string: ", stdout);
if(!fgets(dgram, sizeof(dgram), stdin))
break;
z = strlen(dgram);
if (z > 0 && dgram[--z] == ' ')
dgram[z] = 0;
z = sendto(s, dgram, strlen(dgram), 0,
(struct sockaddr *)&adr_srvr, len_inet);
if (z < 0)
bail("sendto()");
if (!strcasecmp(dgram, "quit"))
break;
x = sizeof(adr);
z = recvfrom(s, dgram, sizeof(dgram), 0,
(struct sockaddr *)&adr, &x);
if (z < 0)
bail("recvfrom()");
dgram[z] = 0;
printf("Result from %s port %u : '%s' ",
inet_ntoa(adr.sin_addr),
(unsigned)ntohs(adr.sin_port), dgram);
}
close(s);
putchar(' ');
return 0;
}
/*
测试:
$./udpserver 192.168.1.6
//上面的IP是服务器IP,没有则是程序中默认的IP
$./udpclient 192.168.1.6
Enter format string:%D
........
//上面的和下面的%D,%A(%D)是要输入的时间的格式
Enter format string:%A(%D)
...........
Enter format string:quit
//退出
*/
#include < unistd.h >
#include < stdlib.h >
#include < errno.h >
#include < string .h >
#include < time.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < arpa / inet.h >
static void bail( const char * on_what)
{
fputs(strerror(errno), stderr);
fputs(":", stderr);
fputs(on_what, stderr);
fputc(' ', stderr);
exit(1);
}
int main( int argc, char * argv[])
{
int z;
int x;
char *srvr_addr = NULL;
struct sockaddr_in adr_srvr;
struct sockaddr_in adr;
int len_inet;
int s;
char dgram[512];
if (argc >= 2)
{
srvr_addr = argv[1];
}else
{
srvr_addr = "127.0.0.23";
}
memset(&adr_srvr, 0, sizeof(adr_srvr));
adr_srvr.sin_family = AF_INET;
adr_srvr.sin_port = htons(9090);
adr_srvr.sin_addr.s_addr = inet_addr(srvr_addr);
if (INADDR_NONE == adr_srvr.sin_addr.s_addr)
bail("bad address.");
len_inet = sizeof(adr_srvr);
s = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == s)
bail("socket()");
for (;;)
{
fputs(" Enter format string: ", stdout);
if(!fgets(dgram, sizeof(dgram), stdin))
break;
z = strlen(dgram);
if (z > 0 && dgram[--z] == ' ')
dgram[z] = 0;
z = sendto(s, dgram, strlen(dgram), 0,
(struct sockaddr *)&adr_srvr, len_inet);
if (z < 0)
bail("sendto()");
if (!strcasecmp(dgram, "quit"))
break;
x = sizeof(adr);
z = recvfrom(s, dgram, sizeof(dgram), 0,
(struct sockaddr *)&adr, &x);
if (z < 0)
bail("recvfrom()");
dgram[z] = 0;
printf("Result from %s port %u : '%s' ",
inet_ntoa(adr.sin_addr),
(unsigned)ntohs(adr.sin_port), dgram);
}
close(s);
putchar(' ');
return 0;
}
/*
测试:
$./udpserver 192.168.1.6
//上面的IP是服务器IP,没有则是程序中默认的IP
$./udpclient 192.168.1.6
Enter format string:%D
........
//上面的和下面的%D,%A(%D)是要输入的时间的格式
Enter format string:%A(%D)
...........
Enter format string:quit
//退出
*/
server
#include
<
stdio.h
>
#include < stdlib.h >
#include < string .h >
#include < errno.h >
#include < unistd.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < arpa / inet.h >
#include < time.h >
static void bail( char * on_what)
{
fputs(strerror(errno), stderr);
fputs(":", stderr);
fputs(on_what, stderr);
fputc(' ', stderr);
exit(1);
}
int main( int argc, char * argv[])
{
int z;
char *srvr_addr = NULL;
struct sockaddr_in adr_inet;
struct sockaddr_in adr_clnt;
int len_inet;
int s;
char dgram[512];
char dtfmt[512];
time_t td;
struct tm tm;
if (argc >= 2)
{
srvr_addr = argv[1];
}else{
srvr_addr = "127.0.0.23";
}
s = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == s)
bail("socket()");
memset(&adr_inet, 0, sizeof(adr_inet));
adr_inet.sin_family = AF_INET;
adr_inet.sin_port = htons(9090);
adr_inet.sin_addr.s_addr = inet_addr(srvr_addr);
if (adr_inet.sin_addr.s_addr == INADDR_NONE)
bail("bad address.");
len_inet = sizeof(adr_inet);
z = bind(s, (struct sockaddr *)&adr_inet, len_inet);
if (-1 == z)
bail("bind()");
for (;;)
{
len_inet = sizeof(adr_clnt);
z = recvfrom(s, dgram, sizeof(dgram), 0,
(struct sockaddr *)&adr_clnt, &len_inet);
if (z < 0)
bail("recvfrom()");
printf("Client from %s port %u; ", inet_ntoa(adr_clnt.sin_addr),
(unsigned)ntohs(adr_clnt.sin_port));
dgram[z] = 0;
//结束服务器
if (!strcasecmp(dgram, "quit"))
break;
time(&td); //获取当前时间
tm = *localtime(&td);
//格式化结果, 结果最大尺寸, 输入日期格式, 输入日期值
strftime(dtfmt, sizeof(dtfmt), dgram, &tm);
z = sendto(s, dtfmt, strlen(dtfmt), 0,
(struct sockaddr *)&adr_clnt, len_inet);
if (z < 0)
bail("sendto()");
}
close(s);
return 0;
}
#include < stdlib.h >
#include < string .h >
#include < errno.h >
#include < unistd.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < arpa / inet.h >
#include < time.h >
static void bail( char * on_what)
{
fputs(strerror(errno), stderr);
fputs(":", stderr);
fputs(on_what, stderr);
fputc(' ', stderr);
exit(1);
}
int main( int argc, char * argv[])
{
int z;
char *srvr_addr = NULL;
struct sockaddr_in adr_inet;
struct sockaddr_in adr_clnt;
int len_inet;
int s;
char dgram[512];
char dtfmt[512];
time_t td;
struct tm tm;
if (argc >= 2)
{
srvr_addr = argv[1];
}else{
srvr_addr = "127.0.0.23";
}
s = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == s)
bail("socket()");
memset(&adr_inet, 0, sizeof(adr_inet));
adr_inet.sin_family = AF_INET;
adr_inet.sin_port = htons(9090);
adr_inet.sin_addr.s_addr = inet_addr(srvr_addr);
if (adr_inet.sin_addr.s_addr == INADDR_NONE)
bail("bad address.");
len_inet = sizeof(adr_inet);
z = bind(s, (struct sockaddr *)&adr_inet, len_inet);
if (-1 == z)
bail("bind()");
for (;;)
{
len_inet = sizeof(adr_clnt);
z = recvfrom(s, dgram, sizeof(dgram), 0,
(struct sockaddr *)&adr_clnt, &len_inet);
if (z < 0)
bail("recvfrom()");
printf("Client from %s port %u; ", inet_ntoa(adr_clnt.sin_addr),
(unsigned)ntohs(adr_clnt.sin_port));
dgram[z] = 0;
//结束服务器
if (!strcasecmp(dgram, "quit"))
break;
time(&td); //获取当前时间
tm = *localtime(&td);
//格式化结果, 结果最大尺寸, 输入日期格式, 输入日期值
strftime(dtfmt, sizeof(dtfmt), dgram, &tm);
z = sendto(s, dtfmt, strlen(dtfmt), 0,
(struct sockaddr *)&adr_clnt, len_inet);
if (z < 0)
bail("sendto()");
}
close(s);
return 0;
}