网络编程-2、TCP&UDP编程 udp网络编程的一般步骤
yuyutoo 2024-12-22 21:48 12 浏览 0 评论
1、UDP编程
1.1、UDP编程-创建套接字
#include <sys/socket.h>
int socket(int family,int type,int protocol);
功能
创建一个用于网络通信的socket套接字(描述符)
参数
family:协议族(AF_INET、AF_INET6、PF_PACKET等)
type:套接字类(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等)
protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等
返回值:
套接字
特点
创建套接字时,系统不会分配端口
创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的
1.2、UDP编程-发送数据
ssize_t sendto(int sockfd,const void *buf,size_t nbytes,int flags,const struct sockaddr *to,socklen_t addrlen);
功能:
向to结构体指针中指定的ip,发送UDP数据
参数:
sockfd:套接字
buf: 发送数据缓冲区
nbytes: 发送数据缓冲区的大小
flags:一般为0
to: 指向目的主机地址结构体的指针
addrlen:to所指向内容的长度
返回值:
成功:发送数据的字符数
失败: -1
注意:
通过to和addrlen确定目的地址
可以发送0长度的UDP数据包
1.3、UDP编程-绑定端口
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
功能:将本地协议地址与sockfd绑定
参数:
sockfd: socket套接字
myaddr: 指向特定协议的地址结构指针
addrlen:该地址结构的长度
返回值 :
成功:返回0
失败:其他
1.4、UDP编程-接收数据
ssize_t recvfrom(int sockfd, void *buf,size_t nbytes,int flags,struct sockaddr *from,socklen_t *addrlen);
功能 :
接收UDP数据,并将源地址信息保存在from指向的结构中
参数 :
sockfd:套接字
buf: 接收数据缓冲区
nbytes:接收数据缓冲区的大小
flags: 套接字标志(常为0)
from: 源地址结构体指针,用来保存数据的来源
addrlen: from所指内容的长度
返回值:
成功:接收到的字符数
失败: -1
注意:
通过from和addrlen参数存放数据来源信息
from和addrlen可以为NULL, 表示不保存数据来源
1.5、UDP编程-客户端示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_IP "192.168.0.108"
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in dest_addr;
bzero(&dest_addr, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, SERVER_IP, &dest_addr.sin_addr);
printf("UDP server %s:%d\n", SERVER_IP, SERVER_PORT);
while(1)
{
char send_buf[512] = "";
fgets(send_buf, sizeof(send_buf), stdin);//获取输入
send_buf[strlen(send_buf)-1] = '\0';
sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));//发送数据
}
close(sockfd);
return 0;
}
1.6、UDP编程-服务端示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_IP "192.168.0.104"
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(SERVER_PORT);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
printf("server bind port: %d\n", SERVER_PORT);
int ret = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if(ret != 0)
{
perror("bind");
close(sockfd);
exit(-1);
}
printf("receive data:\n");
while(1)
{
int recv_len;
char recv_buf[512] = "";
struct sockaddr_in client_addr;
char client_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16
socklen_t cliaddr_len = sizeof(client_addr);
recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
//char *client_ip_pr=inet_ntoa(client_addr.sin_addr);
printf("client_ip:%s ,client_port:%d\n",client_ip, ntohs(client_addr.sin_port));
printf("data(%d):%s\n",recv_len,recv_buf);
}
close(sockfd);
return 0;
}
1.7、UDP广播
广播:由一台主机向该主机所在局域网内的所有主机发送数据的方式 ,广播只能用UDP或原始IP实现,不能用TCP
广播的用途:
单个服务器与多个客户主机通信时减少分组流通
地址解析协议(ARP)
动态主机配置协议(DHCP)
网络时间协议(NTP)
广播的特点:
处于同一子网的所有主机都必须处理数据
UDP数据包会沿协议栈向上一直到UDP层
运行音视频等较高速率工作的应用,会带来大负
局限于局域网内使用
UDP广播地址
{网络ID,主机ID}
网络ID表示由子网掩码中1覆盖的连续位
主机ID表示由子网掩码中0覆盖的连续位
定向广播地址:主机ID全1
例:对于192.168.220.0/24,其定向广播地址为 192.168.220.255
通常路由器不会转发该广播
受限广播地址:255.255.255.255
路由器从不转发该广播
套接字选项
int setsockopt(int sockfd, int level,int optname,const void *optval,socklen_t optlen);
功能:
设置套接字选项值
参数:
sockfd:套接字
level: 被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET
optname:准备设置的选项,option_name可以有哪些取值,这取决于level
optval:类型
optlen:
返回值:
成功:0
失败: -1
示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_IP "192.168.0.104"
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{
int sockfd=0;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
printf("server bind port: %d\n", SERVER_PORT);
int opt=1;
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(opt));
char send_buf[1024]={0};
strcpy(send_buf,"this is broadbast msg\n");
int len = sendto(sockfd,send_buf,strlen(send_buf),0,(struct sockaddr *)&server_addr,sizeof(server_addr));
if(len<0)
{
printf("send error\n");
close(sockfd);
return -1;
}
printf("send success\n");
close(sockfd);
return 0;
}
1.8、UDP多播(组播)
多播: 数据的收发仅仅在同一分组中进行多播的特点:
多播地址标示一组接口
多播可以用于广域网使用
在IPv4中,多播是可选的
UDP多播地址
IPv4的D类地址是多播地址
十进制:224.0.0.1 - 239.255.255.254
十六进制:E0.00.00.01 - EF.FF.FF.FE 特殊的IP地址多播地址:224.0.0.1:是所有主机组,子网上所有具有多播能力的节点(主机、路由器、打印机)必须在所有具有多播能力的接口上加入该组。224.0.0.2:是所有路由器组,子网上所有多播路由器必须在所有具有多播能力的接口上加入该组。
多播地址分类:
224.0.0.0 -- 224.0.255 链路局部的多播地址,是低级拓扑和维护协议保留的,多播路由器不转发以这些地址为目的地址的数据报
224.0.1.0 -- 224.0.1.255: 为用户可用的组播地址(临时组地址),可以用于 Internet 上的。224.0.2.0 -- 238.255.255.255: 用户可用的组播地址(临时组地址),全网范围内有效239.0.0.0 -- 239.255.255.255: 为本地管理组播地址,仅在特定的本地范围内有效
- 在IPv4因特网域(AF_INET)中,多播地址结构体用如下结构体ip_mreq表示
struct in_addr{
in_addr_t s_addr;
};
struct ip_mreq{
struct in_addr imr_multiaddr;//多播组ip
struct in_addr imr_interface;//将要添加到的多播组ip
};
套接口选项
int setsockopt(int sockfd, int level,int optname,const void *optval, socklen_t optlen);
功能:
设置套接字选项值
参数:
sockfd:套接字
level: 被设置的选项的级别,IPPROTO_IP
optname: IP_ADD_MEMBERSHIP :加入多播组 ,IP_DROP_MEMBERSHIP :离开多播组
optval:类型 ip_mreq{}
optlen:
返回值:
成功:0
失败: -1
为什么要用组播:
单播和组播是寻址方案的两个极端(要么单个、要么全部),多播则是两者之间的一种折中的方案,多播数据报只应该由对它感兴趣的接口接收。另外,广播一般局限于局域网内使用,多播则既可用于局域网,也可跨广域网使用。
2、TCP编程
2.1、TCP介绍
- 作为客户端需要具备的条件
(1) 知道服务器的ip、port
(2) 主动连接 服务器
- 需要用到的函数
socket : 创建TCP套接字(主动)
connect:连接服务器
send:发送数据到服务器
recv: 接受服务器的响应
close:关闭连接
2.2、TCP客户端相关函数
2.2.1 创建TCP套接字函数
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建通信端点:套接字
2.2.2 连接服务器函数
int connect(int sockfd,const struct sockaddr *addr,socklen_t len);
功能:主动跟服务器建立链接
参数:
sockfd:socket套接字
addr: 连接的服务器地址结构
len: 地址结构体长度
返回值:
成功:0 失败:其他
注意:
? connect建立连接之后不会产生新的套接字
? 连接成功后才可以开始传输TCP数据
2.2.3 TCP发送数据
#include <sys/socket.h>
ssize_t send(int sockfd, const void* buf,size_t nbytes, int flags);
功能:用于发送数据
参数:
sockfd: 已建立连接的套接字
buf: 发送数据的地址
nbytes: 发送缓数据的大小(以字节为单位)
flags: 套接字标志(常为0)
注意:不能用TCP协议发送0长度的数据包
2.2.4 TCP接收数据
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf,size_t nbytes, int flags);
功能:用于接收网络数据
参数:
? sockfd:套接字
? buf: 接收网络数据的缓冲区的地址
? nbytes:接收缓冲区的大小(以字节为单位)
? flags: 套接字标志(常为0)
返回值:成功接收到字节数
2.2.5 客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_IP "192.168.0.107"
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建通信端点:套接字
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
int err_log = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 主动连接服务器
if(err_log != 0)
{
perror("connect");
close(sockfd);
exit(-1);
}
char send_buf[1024] = "";
printf("send data to [%s:%d]\n",SERVER_IP,SERVER_PORT);
while(1)
{
printf("send:");
fgets(send_buf,sizeof(send_buf),stdin);
send_buf[strlen(send_buf)-1]='\0';
send(sockfd, send_buf, strlen(send_buf), 0); // 向服务器发送信息
}
close(sockfd);
return 0;
}
2.3、TCP服务端相关函数
做为TCP服务器需要具备的条件
? 具备一个可以确知的地址
? 让操作系统知道是一个服务器,而不是客户端
? 等待连接的到来
对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始
2.3.1、bind函数
bind用法和UDP服务端使用一样,
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if( err_log != 0)
{
perror("binding");
close(sockfd);
exit(-1);
}
2.3.2、listen函数
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:
? 将套接字由主动修改为被动
? 使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接
参数:
? sockfd: socket监听套接字
? backlog:连接队列的长度
返回值:
? 成功:返回0
? 失败:其他
2.3.3、accept 函数
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
功能:从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待(阻塞)
参数:
? sockfd: socket监听套接字
? cliaddr: 用于存放客户端套接字地址结构
? addrlen:套接字地址结构体长度的地址
返回值:已连接套接字
注意:返回的是一个已连接套接字,这个套接字代表当前这个连接
2.3.4、服务端的代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{ //创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(SERVER_PORT);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if( err_log != 0)
{
perror("binding");
close(sockfd);
exit(-1);
}
err_log = listen(sockfd, 10);
if(err_log != 0)
{
perror("listen");
close(sockfd);
exit(-1);
}
printf("listen client port=%d...\n",SERVER_PORT);
while(1)
{
struct sockaddr_in client_addr;
char cli_ip[INET_ADDRSTRLEN] = "";
socklen_t cliaddr_len = sizeof(client_addr);
int connfd;
connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);
if(connfd < 0)
{
perror("accept");
continue;
}
inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
printf("----------------------------------------------\n");
printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));
char recv_buf[2048] = "";
while( recv(connfd, recv_buf, sizeof(recv_buf), 0) > 0 )
{
printf("\nrecv data:\n");
printf("%s\n",recv_buf);
}
close(connfd); //关闭已连接套接字
printf("client closed!\n");
}
close(sockfd); //关闭监听套接字
return 0;
}
相关推荐
- 电脑 CMD 命令大全:简单粗暴收藏版
-
电脑CMD命令大全包括了许多常用的命令,这些命令可以帮助用户进行各种系统管理和操作任务。以下是一些常用的CMD命令及其功能:1、系统信息和管理...
- 电脑维修高手必备!8个神奇DOS命令,自己动手不求人
-
我相信搞电脑维修或者维护的基本都会些DOS的命令。就算Windows操作系统是可视化的界面,但很多维护检查是离不开DOS命令的。掌握好这些命令,你不仅能快速诊断问题,还能解决90%的常见电脑故障。下...
- 一个互联网产品总监的设计技巧总结 - 技术篇
-
古语:工欲善其事必先利其器。往往在利其器后我们才能事半功倍。从这个角度出发成为一个合格的产品经理你需要的是“利其器”,这样你才能产品的设计过程中如鱼得水,得心应手。有些产品经理刚入职,什么都感觉自己欠...
- 超详解析Flutter渲染引擎|业务想创新,不了解底层原理怎么行?
-
作者|万红波(远湖)出品|阿里巴巴新零售淘系技术部前言Flutter作为一个跨平台的应用框架,诞生之后,就被高度关注。它通过自绘UI,解决了之前RN和weex方案难以解决的多端一致性...
- 瑞芯微RK3568|SDK开发之环境安装及编译操作
-
1.SDK简介一个通用LinuxSDK工程目录包含有buildroot、app、kernel、device、docs、external等目录。其中一些特性芯片如RK3308/RV1108/R...
- 且看L-MEM ECC如何守护i.MXRT1170从核CM4
-
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M4内核的L-MEMECC功能。本篇是《简析i.MXRT1170Cortex-M7F...
- ECC给i.MXRT1170 FlexRAM带来了哪些变化?
-
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M7内核的FlexRAMECC功能。ECC是“ErrorCorrectingCode”...
- PHP防火墙代码,防火墙,网站防火墙,WAF防火墙,PHP防火墙大全
-
PHP防火墙代码,防火墙,网站防火墙,WAF防火墙,PHP防火墙大全资源宝整理分享:https://www.htple.net...
- 从零开始移植最新版本(2023.10)主线Uboot到Orange Pi 3(全志H6)
-
本文将从零开始通过一步一步操作来实现将主线U-Boot最新代码移植到OrangePi3(全志H6)开发板上并正常运行起来。本文从通用移植思路的角度,展现是思考的过程,通过这种方式希望能让读者一通百...
- 可视化编程工具Blockly——定制工具箱
-
1概述本文重点讲解如何定制Blocklytoolbox上,主要包含如下几点目标:如何为toolbox不同类别添加背景色如何改变选中的类别的外观如何为toolbox类别添加定制化的css如何改变类别...
- 用户界面干货盘点(用户界面的基本操作方法)
-
DevExpressDevExpressWPF的DXSplashScreen控件在应用加载的时候显示一个启动界面。添加DXSplashScreen后,会默认生成一个XAML文件,当然,你也可...
- Vue3+Bootstrap5整合:企业级后台管理系统实战
-
简洁而不简单,优雅而不失强大在当今快速发展的企业数字化进程中,高效、美观的后台管理系统已成为企业运营的核心支撑。作为前端开发者,我们如何选择技术栈,才能既保证开发效率,又能打造出专业级的用户体验?答案...
- 什么?这三款i.MXRT型号也开放了IAP API?
-
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT1050/1020/1015系列ROM中的FlexSPI驱动API使用。今天痞子衡去4S店给爱车做保养了,...
- OneCode基础组件介绍——表格组件(Grid)
-
在企业级应用开发中,表格组件是数据展示与交互的核心载体。OneCode平台自研的Grid表格组件,以模型驱动设计...
- 开源无线LoRa传感器(光照温湿度甲醛Tvoc)
-
本开源项目基于ShineBlinkC2M低代码单片机实现,无需复杂单片机C语言开发。即使新手也可很容易用FlexLua零门槛开发各种功能丰富稳定可靠的IoT硬件,更多学习教程可参考Flex...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)