网络编程-2、TCP&UDP编程 udp网络编程的一般步骤
yuyutoo 2024-12-22 21:48 4 浏览 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;
}
相关推荐
- 史上最全的浏览器兼容性问题和解决方案
-
微信ID:WEB_wysj(点击关注)◎◎◎◎◎◎◎◎◎一┳═┻︻▄(页底留言开放,欢迎来吐槽)●●●...
-
- 平面设计基础知识_平面设计基础知识实验收获与总结
-
CSS构造颜色,背景与图像1.使用span更好的控制文本中局部区域的文本:文本;2.使用display属性提供区块转变:display:inline(是内联的...
-
2025-02-21 16:01 yuyutoo
- 写作排版简单三步就行-工具篇_作文排版模板
-
和我们工作中日常word排版内部交流不同,这篇教程介绍的写作排版主要是用于“微信公众号、头条号”网络展示。写作展现的是我的思考,排版是让写作在网格上更好地展现。在写作上花费时间是有累积复利优势的,在排...
- 写一个2048的游戏_2048小游戏功能实现
-
1.创建HTML文件1.打开一个文本编辑器,例如Notepad++、SublimeText、VisualStudioCode等。2.将以下HTML代码复制并粘贴到文本编辑器中:html...
- 今天你穿“短袖”了吗?青岛最高23℃!接下来几天气温更刺激……
-
最近的天气暖和得让很多小伙伴们喊“热”!!! 昨天的气温到底升得有多高呢?你家有没有榜上有名?...
- CSS不规则卡片,纯CSS制作优惠券样式,CSS实现锯齿样式
-
之前也有写过CSS优惠券样式《CSS3径向渐变实现优惠券波浪造型》,这次再来温习一遍,并且将更为详细的讲解,从布局到具体样式说明,最后定义CSS变量,自定义主题颜色。布局...
- 你的自我界限够强大吗?_你的自我界限够强大吗英文
-
我的结果:A、该设立新的界限...
- 行内元素与块级元素,以及区别_行内元素和块级元素有什么区别?
-
行内元素与块级元素首先,CSS规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,分别为块级(block)、行内(inline)。块级元素:(以下列举比较常...
-
- 让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华
-
去年的两会期间,习近平总书记在参加人大会议四川代表团审议时,对治蜀兴川提出了明确要求,指明了前行方向,并带来了“祝四川人民的生活越来越安逸”的美好祝福。又是一年...
-
2025-02-21 16:00 yuyutoo
- 今年国家综合性消防救援队伍计划招录消防员15000名
-
记者24日从应急管理部获悉,国家综合性消防救援队伍2023年消防员招录工作已正式启动。今年共计划招录消防员15000名,其中高校应届毕业生5000名、退役士兵5000名、社会青年5000名。本次招录的...
- 一起盘点最新 Chrome v133 的5大主流特性 ?
-
1.CSS的高级attr()方法CSSattr()函数是CSSLevel5中用于检索DOM元素的属性值并将其用于CSS属性值,类似于var()函数替换自定义属性值的方式。...
- 竞走团体世锦赛5月太仓举行 世界冠军杨家玉担任形象大使
-
style="text-align:center;"data-mce-style="text-align:...
- 学物理能做什么?_学物理能做什么 卢昌海
-
作者:曹则贤中国科学院物理研究所原标题:《物理学:ASourceofPowerforMan》在2006年中央电视台《对话》栏目的某期节目中,主持人问过我一个的问题:“学物理的人,如果日后不...
-
- 你不知道的关于这只眯眼兔的6个小秘密
-
在你们忙着给熊本君做表情包的时候,要知道,最先在网络上引起轰动的可是这只脸上只有两条缝的兔子——兔斯基。今年,它更是迎来了自己的10岁生日。①关于德艺双馨“老艺...
-
2025-02-21 16:00 yuyutoo
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 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)