TCP/IP网络编程学习笔记(四)基于UDP的服务器端/客户端
时间:2019-04-15
本文章向大家介绍TCP/IP网络编程学习笔记(四)基于UDP的服务器端/客户端,主要包括TCP/IP网络编程学习笔记(四)基于UDP的服务器端/客户端使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
一、概念
1.UDP套接字的特点
- 不可靠的数据传输
- 没有流控制
2.UDP的内部工作原理
- 根据端口号将传到主机的数据包交付给最终的 UDP 套接字
3.UDP的使用场合
- 实时传输视频或音频
二、相关函数
- sendto 函数
#include<sys/socket.h>
// 功能:发送数据
// 参数:
// sock--用于传输数据的 UDP 套接字文件描述符
// buff--保存待传输数据的缓冲地址值
// nbytes--待传输的数据长度
// flags--可选参数,默认传递 0
// to--存有目标地址信息的 sockaddr 结构体变量的地址值
// addrlen--传递给参数 to 的地址值结构体变量长度
// 返回值:成功时返回传输的字节数,失败时返回 -1
ssize_t sendto(int sock,void* buff,size_t bytes,int flags,struct sockaddr* to,socklen_t addrlen);
- recvfrom 函数
#include<sys/socket.h>
// 功能:接收数据
// 参数:
// sock--用于接收数据的 UDP 套接字文件描述符
// buff--保存接收数据的缓冲地址值
// nbytes--可接收的最大字节数,故无法超过 buff 所指的缓冲大小
// flags--可选参数,默认为 0
// from--存有发送端地址信息的 sockaddr 结构体变量的地址值
// addrlen--保存参数 from 的结构体变量长度的变量地址值
// 返回值:成功时返回接收的字节数,失败时返回 -1
ssize_t recvfrom(int sock,void* buff,size_t nbytes,int flags,struct sockaddr* from,socklen_t* addrlen);
三、基于UDP的回声服务器端/客户端
- 服务器端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int serv_sock;
struct sockaddr_in serv_addr, clnt_addr;
char message[BUF_SIZE];
if (argc != 2) {
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (serv_sock == -1)
error_handling("UDP socket creation error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
error_handling("bind() error");
while (1) {
socklen_t clnt_addr_size = sizeof(clnt_addr);
int str_len = recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
sendto(serv_sock, message, str_len, 0, (struct sockaddr*)&clnt_addr, clnt_addr_size);
}
close(serv_sock);
return 0;
}
- 客户端
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int sock;
char message[BUF_SIZE];
struct sockaddr_in serv_addr, from_addr;
if (argc != 3) {
printf("Usage: %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
while (1) {
fputs("Insert message(Q to quit): ", stdout);
fgets(message, sizeof(message), stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
// 调用 sendto 函数时自动分配IP地址和端口号
sendto(sock, message, strlen(message), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
socklen_t addr_size = sizeof(from_addr);
int str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&from_addr, &addr_size);
message[str_len] = 0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
- 运行结果:
四、UDP套接字存在数据边界
- bound_host1.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int sock;
struct sockaddr_in my_addr, your_addr;
char message[BUF_SIZE];
if (argc != 2) {
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1)
error_handling("socket() error");
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_port = htons(atoi(argv[1]));
if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1)
error_handling("bind() error");
for (int i = 0; i < 3; i++) {
sleep(3);
socklen_t addr_size = sizeof(your_addr);
int str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&your_addr, &addr_size);
printf("Message %d: %s \n", i + 1, message);
}
close(sock);
return 0;
}
- bound_host2.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int sock;
char msg1[] = "Hi!";
char msg2[] = "I am another UDP host!";
char msg3[] = "Nice to meet you!";
struct sockaddr_in your_addr;
socklen_t your_addr_size;
if (argc != 3) {
printf("Usage: %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1)
error_handling("socket() error");
memset(&your_addr, 0, sizeof(your_addr));
your_addr.sin_family = AF_INET;
your_addr.sin_addr.s_addr = inet_addr(argv[1]);
your_addr.sin_port = htons(atoi(argv[2]));
sendto(sock, msg1, sizeof(msg1), 0, (struct sockaddr*)&your_addr, sizeof(your_addr));
sendto(sock, msg2, sizeof(msg2), 0, (struct sockaddr*)&your_addr, sizeof(your_addr));
sendto(sock, msg3, sizeof(msg3), 0, (struct sockaddr*)&your_addr, sizeof(your_addr));
close(sock);
return 0;
}
- 运行结果:
五、已连接UDP套接字
- 要与同一主机进行长时间通信,将UDP套接字转换为已连接套接字会提高效率,即调用 connect 函数注册IP地址和端口号,sendto,recvfrom 函数改用 write,read 函数
- uecho_con_client.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char* message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[]) {
int sock;
struct sockaddr_in serv_addr;
char message[BUF_SIZE];
if (argc != 3) {
printf("Usage: %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
while (1) {
fputs("Insert message(Q to quit): ", stdout);
fgets(message, sizeof(message), stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
write(sock, message, strlen(message));
int str_len = read(sock, message, sizeof(message) - 1);
message[str_len] = 0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
- 运行结果:
参考书籍:《TCP/IP网络编程》尹圣雨 著,金果哲 译
- ES6 Features系列:Template Strings & Tagged Template Strings
- 基于Tcp协议的简单Socket通信实例(JAVA)
- spring集成kafka
- Java常用类(二)String类详解
- 用树莓派玩转蓝牙
- CSS魔法堂:你真的理解z-index吗?
- HashSet
- 树莓派的GPIO编程
- Java集合源码分析(三)Vevtor和Stack
- JS魔法堂:再识instanceof
- Web开发之CSS
- Linux重启命令与如何重启网络?
- spring boot + embed tomcat + standalone jar的内存泄露问题
- 树莓派:设置与软件安装
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- linux安装图形化界面的操作方法
- Apache FlinkCEP 实现超时状态监控的步骤详解
- 解决Centos7下crontab+shell脚本定期自动删除文件问题
- 详解在Ubuntu上的Apache配置SSL(https证书)的正确姿势
- 如何在 Linux 中查找一个命令或进程的执行时间
- Ubuntu 18.04 LTS中配置IP地址的完整步骤
- Linux系统下Nginx支持ipv6配置的方法
- 微信研发体系下的分布式配置系统设计概要
- Linux双网卡绑定脚本的方法示例
- Serverless 有一百种玩法,比好玩更好玩
- 如何在容器服务中获取客户端真实源IP
- Linux服务器间文件实时同步的实现
- centos7 设置grub密码及单用户登录实例代码
- Linux命令行快速技巧之定位一个文件的方法
- Linux低电量自动关机的实现方法