1. UDP编程模型

(1)UDP客户端服务器模型

  ①客户端可以不调用bind()而直接与服务器通讯。

  ②UDP是无连接的,因此服务端不需要调用accept和listen客户端也无需调用connect函数

(2)数据传输

  ①发送数据

头文件

#include <sys/socket.h>

函数

ssize_t send(int sockfd, const void* buf, size_t nbytes, int flag);

ssize_t send(int sockfd, const void* buf, size_t nbytes, int flag,

const struct sockaddr* destaddr, socklen_t destlen);

ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flag);

参数

struct msghdr{
void* msg_name; //optional address
socklen_t msg_namelen; //address size in bytes
struct iovec* msg_iov; //array of I/O buffers
int msg_iovlen; //number of elements in array
void* msg_control; //ancillary data
socklen_t msg_controllen; //number of ancillary bytes;
int msg_flags; //flags for received message
};

功能

发送数据

返回值

返回:成功返回发送字节数,出错返回-1。

  ②接收数据

头文件

#include <sys/socket.h>

函数

ssize_t recv(int sockfd, const void* buf, size_t nbytes, int flag);

ssize_t recvfrom(int sockfd, const void* buf, size_t nbytes, int flag, const struct sockaddr* addr, socklen_t addrlen);

ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flag);

参数

与send*函数类似

功能

接收数据

返回值

返回消息的字节数,无消息返回0,出错返回-1。

【编程实验】利用UDP获取服务器的当前时间

//time_udp_server.c

#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <memory.h> int sockfd; void sig_handler(int signo)
{
if(signo == SIGINT){
printf("server close\n");
close(sockfd);
exit();
}
} //输出客户端的信息
void out_addr(struct sockaddr_in* addr)
{
char ip[];
int port = ;
memset(ip, , sizeof(ip));
inet_ntop(AF_INET, &addr->sin_addr.s_addr, ip, sizeof(ip));
port = ntohs(addr->sin_port); printf("client: %s(%d)\n", ip, port);
} //与客户端进行通信
void do_service()
{
struct sockaddr_in cliAddr;
socklen_t len = sizeof(cliAddr);
char buff[];
memset(buff, , sizeof(buff)); //接受客户端的数据报文
//使用recvfrom而不使用recv函数的主要目的是为了获取客户端信息
if(recvfrom(sockfd, buff, sizeof(buff), ,
(struct sockaddr*)&cliAddr, &len) < ){
perror("recvfrom error");
}else{
out_addr(&cliAddr);
printf("client send info: %s\n", buff); //向客户端发送数据报文
long int t = time();
char* ptr = ctime(&t);
size_t size = strlen(ptr) * sizeof(char);
if(sendto(sockfd, ptr, size, ,(struct sockaddr*)&cliAddr, len) < ){
perror("sendto error"); //cliAddr己经从recvfrom那里得到了客户端的信息
}
}
} int main(int argc, char* argv[])
{
if(argc < ){
printf("usage: %s port\n", argv[]);
exit();
} //注册ctrl-c信号处理函数
if(signal(SIGINT, sig_handler) == SIG_ERR){
perror("signal sigint error");
exit();
} /*步骤1: 创建socket*/
sockfd = socket(AF_INET, SOCK_DGRAM, ); //SOCK_DGRAM为UDP协议
if(sockfd < ){
perror("socket error");
exit();
} //设置socket的相关选项
int ret;
int opt = ;//1表示启动该选项
//设置为可重新使用端口,每次启动该端口时,原来对该端口使用将失效
if((ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < ){
perror("setsockopt error");
exit();
} /*步骤2: 调用bind函数将socket和地址进行绑定*/
struct sockaddr_in servAddr;
memset(&servAddr, , sizeof(servAddr));
servAddr.sin_family = AF_INET; //IPv4
servAddr.sin_port = htons(atoi(argv[])); //port
servAddr.sin_addr.s_addr = INADDR_ANY; //ip
if(bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < ){
perror("bind error");
exit();
} /*步骤3:与客户端进行双向的数据通信*/
while(){
do_service();
} return ;
}
/* 输出结果
* [root@localhost 14.udp]# gcc -o bin/time_udp_client src/time_udp_client.c
* [root@localhost 14.udp]# bin/time_udp_server 8888
* client: 127.0.0.1(48929)
* client send info: hello world!
* client: 127.0.0.1(32953)
* client send info: hello world!
* ^Cserver close
* [root@localhost 14.udp]#
*/

//time_udp_client.c

#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h> int main(int argc, char* argv[])
{
if(argc < ){
printf("usage: %s ip port\n", argv[]);
exit();
} /*步骤1:创建socket*/
int sockfd = socket(AF_INET, SOCK_DGRAM, );
if(sockfd < ){
perror("socket error");
exit();
} /*步骤2: 调用recvfrom和sendto等函数和服务端双向通信*/
struct sockaddr_in servAddr; //封装服务器的地址信息
memset(&servAddr, , sizeof(servAddr));
servAddr.sin_family = AF_INET; //IPv4
servAddr.sin_port = htons(atoi(argv[]));//端口
inet_pton(AF_INET, argv[], &servAddr.sin_addr.s_addr); //在UDP能否调用connect来与服务端连接?
//TCP中调用connect会经过三次握手,建立起双方的连接。
//但UDP中调用connect并没有建立真正的连接,而是在内核中记录了通讯双方的地址信息(如IP、por)
//当UDP中调用了connect后,以后可以直接使用send而不必使用sendto来发送消息给对方。
//此外,还有一个好处就是因为sock记录了客户端的IP,使用该sockfd可以只接收指定服务器发来的消息,而不会
//接收除服务器以外其他地方发来的消息。
if(connect(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < ){
perror("connect error");
exit();
} char buff[] = "hello world!";
//向服务器发送数据报文
if((sendto(sockfd, buff, sizeof(buff), , (struct sockaddr*)&servAddr, sizeof(servAddr))) < ){
perror("sendto error");
exit();
}else{
//接受服务器端的数据报文
memset(buff, , sizeof(buff));
size_t size; //1.为什么recv里没有指定服务器地址,却可以发送成功?
//因为如果之前的sendto发送成功,则sockfd(是个结构体)里将保存通讯双方的信息,这里就可以直接使用这个sockfd来通讯
//2.为什么不需要判断recv的返回值为0?(0表示对方己关闭连接)
//因为UDP是无连接的通信,通信双方是没有建立连接的,数据被传到链路层以后发送方就可以关闭,因此这里不需判断是否为0.
if((size = recv(sockfd, buff, sizeof(buff), )) < ){
perror("recv error");
exit();
}else{
printf("%s", buff);
}
} close(sockfd); return ;
}
/*输出结果
* [root@localhost 14.udp]# bin/time_udp_client 127.0.0.1 8888
* Sat Mar 18 10:01:09 2017
* [root@localhost 14.udp]# bin/time_udp_client 127.0.0.1 8888
* Sat Mar 18 10:01:12 2017
* [root@localhost 14.udp]#
*/

第14章 UDP编程(1)_UDP客户端服务器模型的更多相关文章

  1. 第14章 UDP编程(3)_利用UDP实现广播功能

    3. 广播的介绍 (1)广播 ①广播实现一对多的通信,如QQ群 ②它通过向广播地址发送数据报文实现的 (2)SO_BROADCAST选项 ①SO_BROADCAST选项控制着UDP套接字是否能发送广播 ...

  2. 第14章 UDP编程(2)_端口绑定和域名解析

    2. 端口绑定和域名解析 2.1 端口绑定:SO_REUSEADDR选项 ;//1表示启用该选项 //设置为可重新使用端口,每次启动该端口时,会重新绑定端口.相当于端口被复位并被重新. //绑定.因此 ...

  3. CSAPP:第十一章 网络编程

    CSAPP:第十一章 网络编程 11.1 客户端服务器模型11.2 全球IP因特网11.3 套接字接口 11.1 客户端服务器模型   每个网络应用都是基于客户端-服务器模型.采用这个模型,一个应用是 ...

  4. 【RL-TCPnet网络教程】第14章 RL-TCPnet之TCP客户端

    第14章      RL-TCPnet之TCP客户端 本章节为大家讲解RL-TCPnet的TCP客户端实现,学习本章节前,务必要优先学习第12章TCP传输控制协议基础知识.有了这些基础知识之后,再搞本 ...

  5. 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化

    第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...

  6. 老李推荐:第14章1节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-面向控件编程VS面向坐标编程

    老李推荐:第14章1节<MonkeyRunner源码剖析> HierarchyViewer实现原理-面向控件编程VS面向坐标编程   poptest是国内唯一一家培养测试开发工程师的培训机 ...

  7. [Real World Haskell翻译]第22章 扩展示例:Web客户端编程

    第22章 扩展示例:Web客户端编程 至此,您已经看到了如何与数据库交互,解析一些数据,以及处理错误.现在让我们更进了一步,引入Web客户端库的组合. 在本章,我们将开发一个真正的应用程序:一个播客下 ...

  8. 牛客网Java刷题知识点之TCP、UDP、TCP和UDP的区别、socket、TCP编程的客户端一般步骤、TCP编程的服务器端一般步骤、UDP编程的客户端一般步骤、UDP编程的服务器端一般步骤

    福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑         Java全栈大联盟   ...

  9. TCP/IP网络编程之基于UDP的服务端/客户端

    理解UDP 在之前学习TCP的过程中,我们还了解了TCP/IP协议栈.在四层TCP/IP模型中,传输层分为TCP和UDP这两种.数据交换过程可以分为通过TCP套接字完成的TCP方式和通过UDP套接字完 ...

随机推荐

  1. JAVA中static什么作用?

    是静态修饰符,什么叫静态修饰符呢?大家都知道,在程序中任何变量或者代码都是在编译时由系统自动分配内存来存储的,而所谓静态就是指在编译后所分配的内存会一直存在,直到程序退出内存才会释放这个空间,也就是只 ...

  2. PowerShell添加和部署WSP

    SharePoint PowerShell在SharePoint Product列表里边,然后以管理员权限启动. 1. 添加Solution 到 SharePoint Farm. Add-SPSolu ...

  3. 对象Date的方法

    Date()对象是js提供给我们的日期对象,可以利用它获取关于时间的量. var myDate = new Date() //首先构造一个时间对象,然后用对象的方法获取时间: 1.获取年份: var ...

  4. 百度地图API 绘制轨迹历史

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  5. C语言指针和操作系统的逻辑地址

    你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址,不和绝对物理地址相干.只有在Intel实模式下,逻辑地址才和物理地址相等 ...

  6. oracle 与sql serve 获取随机行数的数据

    Oracle 随机获取N条数据    当我们获取数据时,可能会有这样的需求,即每次从表中获取数据时,是随机获取一定的记录,而不是每次都获取一样的数据,这时我们可以采取Oracle内部一些函数,来达到这 ...

  7. js 验证代码部分的简单实现

    接上面的文章. 我们已经简单的设计了关于如何进行处理了,但是如何进行校验呢,代码也是比较简单的因为我们使用的是asp.net 简单并且功能强大. 我们同样使用的是HttpResponse,简单的模拟代 ...

  8. 调用飞信HTTP接口给自己发短信

    注: 1.下文中所有HTTP请求所指的Host都是f.10086.cn 2.目前只有中国移动用户可以使用 1.打开登录页面:GET /huc/user/space/login.do?m=submit& ...

  9. 升级CentOS 7.4内核版本的三种方案

    https://blog.csdn.net/breeze915/article/details/79243673 在实验环境下,已安装了最新的CentOS 7.4操作系统,现在需要升级内核版本. 实验 ...

  10. 顶级域名和子级域名之间的cookie共享和相互修改、删除

    举例: js 设置 cookie: domain=cag.com 和 domain=.cag.com 是一样的,在浏览器cookie中,Domain都显示为 .cag.com. 就是说:以下2个语句是 ...