UDP应用程序客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数。

下图给出典型的UDP客户/服务器程序的函数调用。

recvfrom和sendto函数

这两个函数类似于标准的read和write函数,不过需要3个额外的参数

#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,const struct sockaddr *to, socklen_t addrlen);
//均返回:若成功则为读或写的字节数,若出错则为-1

UDP回射服务器程序

main函数

 #include    "unp.h"

 int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr, cliaddr; sockfd = Socket(AF_INET, SOCK_DGRAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); Bind(sockfd, (SA *) &servaddr, sizeof(servaddr)); dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
}

dg_echo函数

 #include    "unp.h"

 void
dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
int n;
socklen_t len;
char mesg[MAXLINE]; for ( ; ; ) {
len = clilen;
n = Recvfrom(sockfd, mesg, MAXLINE, , pcliaddr, &len); Sendto(sockfd, mesg, n, , pcliaddr, len);
}
}

UDP回射客户程序

main函数

 #include    "unp.h"

 int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr; if (argc != )
err_quit("usage: udpcli <IPaddress>"); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[], &servaddr.sin_addr); sockfd = Socket(AF_INET, SOCK_DGRAM, ); dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr)); exit();
}

dg_cli函数

 #include    "unp.h"

 void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + ]; while (Fgets(sendline, MAXLINE, fp) != NULL) { Sendto(sockfd, sendline, strlen(sendline), , pservaddr, servlen); n = Recvfrom(sockfd, recvline, MAXLINE, , NULL, NULL); recvline[n] = ; /* null terminate */
Fputs(recvline, stdout);
}
}

验证接收到的相应

知道客户临时端口的任何进程都可往客户发送数据报,而且这些数据报会与正常的服务器应答混杂。

我们应该修改recvfrom调用以返回数据报的发送者的IP地址和端口号,保留来自数据报所发往服务器的应答,而忽略任何其他数据报。

下面是验证返回套接字地址的dg_cli函数版本

 #include    "unp.h"

 void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + ];
socklen_t len;
struct sockaddr *preply_addr; preply_addr = Malloc(servlen); while (Fgets(sendline, MAXLINE, fp) != NULL) { Sendto(sockfd, sendline, strlen(sendline), , pservaddr, servlen); len = servlen;
n = Recvfrom(sockfd, recvline, MAXLINE, , preply_addr, &len);
if (len != servlen || memcmp(pservaddr, preply_addr, len) != ) {
printf("reply from %s (ignored)\n",
Sock_ntop(preply_addr, len));
continue;
} recvline[n] = ; /* null terminate */
Fputs(recvline, stdout);
}
}

UDP的connect函数

UDP套接字可以调用connect,但是跟TCP套接字不一样,它不会有三次握手过程。

对于已连接的套接字,与默认的未连接套接字相比,发生了三个变化:

1.我们再也不能给输出操作指定目的IP地址和端口号。也就是说,我们不使用sendto而改用write或send。

2.我们并不必使用recvfrom以获悉数据报的发送者,而改用read、recv或recvmsg。

3.由已连接UDP套接字引发的异步错误会返回给它们所在的进程,而未连接UDP套接字不接受任何异步错误。

一个已连接的UDP套接字可以再次调用connect以用于:

1.指定新的IP地址和端口号

2.断开套接字

dg_cli函数(修订版)

把上面dg_cli函数重写成调用connect的新函数

 #include    "unp.h"

 void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + ]; Connect(sockfd, (SA *) pservaddr, servlen); while (Fgets(sendline, MAXLINE, fp) != NULL) { Write(sockfd, sendline, strlen(sendline)); n = Read(sockfd, recvline, MAXLINE); recvline[n] = ; /* null terminate */
Fputs(recvline, stdout);
}
}

使用select的TCP和UDP回射服务器程序

 /* include udpservselect01 */
#include "unp.h" int
main(int argc, char **argv)
{
int listenfd, connfd, udpfd, nready, maxfdp1;
char mesg[MAXLINE];
pid_t childpid;
fd_set rset;
ssize_t n;
socklen_t len;
const int on = ;
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int); /* 4create listening TCP socket */
listenfd = Socket(AF_INET, SOCK_STREAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); /* 4create UDP socket */
udpfd = Socket(AF_INET, SOCK_DGRAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); Bind(udpfd, (SA *) &servaddr, sizeof(servaddr));
/* end udpservselect01 */ /* include udpservselect02 */
Signal(SIGCHLD, sig_chld); /* must call waitpid() */ FD_ZERO(&rset);
maxfdp1 = max(listenfd, udpfd) + ;
for ( ; ; ) {
FD_SET(listenfd, &rset);
FD_SET(udpfd, &rset);
if ( (nready = select(maxfdp1, &rset, NULL, NULL, NULL)) < ) {
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("select error");
} if (FD_ISSET(listenfd, &rset)) {
len = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &len); if ( (childpid = Fork()) == ) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
exit();
}
Close(connfd); /* parent closes connected socket */
} if (FD_ISSET(udpfd, &rset)) {
len = sizeof(cliaddr);
n = Recvfrom(udpfd, mesg, MAXLINE, , (SA *) &cliaddr, &len); Sendto(udpfd, mesg, n, , (SA *) &cliaddr, len);
}
}
}
/* end udpservselect02 */

sig_chld信号处理函数

 Signal(SIGCHLD,sig_chld);

 #include    "unp.h"

 void
sig_chld(int signo)
{
pid_t pid;
int stat; pid = wait(&stat);
printf("child %d terminated\n", pid);
return;
}

UNP学习笔记(第八章 基本UDP套接字编程)的更多相关文章

  1. UNP学习笔记(第七章 套接字选项)

    有多种方法获取和设置影响套接字的选项: 1.getsockopt和setsockopt函数 2.fcntl函数 3.ioctl函数 getsockopt和setsockopt函数 这两个函数仅用于套接 ...

  2. 《Unix 网络编程》08:基本UDP套接字编程

    基本UDP套接字编程 系列文章导航:<Unix 网络编程>笔记 UDP 概述 流程图 recvfrom 和 sendto #include <sys/socket.h> ssi ...

  3. 【Python网络编程】利用Python进行TCP、UDP套接字编程

    之前实现了Java版本的TCP和UDP套接字编程的例子,于是决定结合Python的学习做一个Python版本的套接字编程实验. 流程如下: 1.一台客户机从其标准输入(键盘)读入一行字符,并通过其套接 ...

  4. 探索UDP套接字编程

    UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务, ...

  5. 【转】 探索UDP套接字编程

    UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务, ...

  6. JavaTCP和UDP套接字编程

    在我们刚开始入门Java后端的时候可能你会觉得有点复杂,包含了很多杂七杂八的知识,例如文件上传下载,监听器,JDBC,请求重定向,请求转发等等(当然也没有很多),但是我们自己真正的去开发一个小型网站( ...

  7. 计算机网络实验 UDP套接字编程

    这是个傻瓜式操作教程 西科大计算机网络实验 UDP套接字编程 我用自己的Ubuntu16.04来举例,实验室的是虚拟机,差不多 只针对第三个题目,修改服务器来通过响应客户端发送的GetTime并发送给 ...

  8. UDP套接字编程 返回系统时间

    计算机网络实验 简单UDP套接字编程 这是学校老师自己改进了一点的题目.我预习了好久才搞明白,同学来问的时候,一大堆简单问题实在是不想回答...所以,这时候我觉得博客是个好东西! 我的任务是做客户端和 ...

  9. 【Unix网络编程】chapter8基本UDP套接字编程

    chapter8基本UDP套接字编程 8.1 概述 典型的UDP客户端/服务端的函数调用 8.2 recvfrom和sendto函数 #include <sys/socket.h> ssi ...

随机推荐

  1. Log4j官方文档翻译(五、日志输出的方法)

    日志类提供了很多方法用于处理日志活动,它不允许我们自己实例化一个logger,但是提供给我们两种静态方法获得logger对象: public static Logger getRootLogger() ...

  2. 【Luogu】P2403所驼门王的宝藏(强连通分量)

    题目链接 想到缩点后DP这题就迷之好做 横天门就点向该行连一条边 纵门就点向该列连一条边 ziyou门直接枚举……map搞搞……话说ziyou门为啥是违规内容不让我发布? 然后缩点,DP,1A 不过写 ...

  3. linux下编译libmysqlclient, 安装mysql-server mysql-client

    cmake . -DCMAKE_INSTALL_PREFIX=/home/zhangyawei/server/depends make make install 安装 mysql-server mys ...

  4. linux系统初始化——启动脚本是如何工作的

    启动脚本是如何工作的 Linux 使用的是基于 运行级(run-levels) 概念的称为 SysVinit 的专用启动工具.它在不同的系统上可能是完全不一样的,所以不能认为一个脚本在某个 Linux ...

  5. box-sizing重置

    html { /*-webkit-box-sizing: border-box; -moz-box-sizing: border-box;*/ box-sizing: border-box; } *, ...

  6. windows api 程序

    #include "StdAfx.h" #include<windows.h> #include<mmsystem.h> LRESULT CALLBACK ...

  7. vue 中父子组件传值:props和$emit

    更新----------- 1 父组件向子组件传值:通过props数组: 在vue-cli Login.vue父组件中有AcceptAndRefuse.vue子组件,首先import进子组件hello ...

  8. react-router 4.0版本学习笔记

    Router 所有路由组件的底层接口,一般情况都不使用,而是使用更加高级的路由. 最常用的有两种<BrowserRouter>.<HashRouter> <Browser ...

  9. 【CF1016D】Vasya And The Matrix(构造)

    题意: 思路:构造方式见代码…… #include<cstdio> #include<cstring> #include<iostream> #include< ...

  10. AC日记——[Ahoi2009]Seq 维护序列seq bzoj 1798

    1798 思路: 维护两个标记: 乘:m  和  加:a 先下放乘,再下放加: 下放乘的时候要把子节点的加一块乘了: 开long long: 来,上代码: #include <cstdio> ...