赞
踩
UDP协议:面向无连接的用户数据报协议,在传输数据前不需要先建立连接;目地主机的运输层收到UDP报文后,不需要给出任何确认
UDP特点
1、相比TCP速度稍快些
2、简单的请求/应答应用程序可以使用UDP
3、对于海量数据传输不应该使用UDP
4、广播和多播应用必须使用UDP
UDP应用: DNS(域名解析)、NFS(网络文件系统)、RTP(流媒体)等
网络通信需要解决3大问题(应用层)
1.协议
2.端口(port)
3.IP地址
20世纪80年代初,加州大学Berkeley分校在BSD(一个UNIX OS版本)系统内实现了TCP/IP协议;其网络程序编程开发接口为socket。
随着UNIX以及类UNIX操作系统的广泛应用, socket成为最流行的网络程序开发接口
socket作用:提供不同主机上的进程之间的通信
特点
1、socket也称“套接字”
2、是一种文件描述符,代表了一个通信管道的一个端点
3、类似对文件的操作一样,可以使用read、write、close等函数对socket套接字进行网络数据的收取和发送等操作
4、得到socket套接字(描述符)的方法调用socket()
- 服务端要绑定确定的端口号
- 客户端不能使用read、write,因为需要发送给指定的服务端地址。
创建一个用于网络通信的socket套接字(描述符)
//头文件:
#include <sys/socket.h>
int socket(int family,int type,int protocol);
/*
参数:
family:协议族(AF_INET4、AF_INET6、PF_PACKET等)
||流式套接字 用于TCP通信 ||报式套接字 用于UDP通信 ||原始套接字
type:套接字类( SOCK_STREAM、 SOCK_DGRAM、 SOCK_RAW等)
||一般放0 自动指定协议
protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等)
返回值:
>0 通信的文件描述符(套接字)
<0 创建失败
*/
特点:
1.创建套接字时,系统不会分配端口
2.创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器
3.时,往往需要修改为被动的
#include <stdio.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //struct sockaddr_in
#include <string.h> //memser
#include <arpa/inet.h> //htons
int main()
{
//创建通信的UDP的套接字(没有port、ip)
int sockfd = socket(AF_INET, SOCK_DGRAM,0);
printf("UDP套接字sockfd=%d\n",sockfd);
//udp客户端 发送消息 给服务器
//定义一个IPv4地址结构 存放服务器的地址信息(目的主机)
struct sockaddr_in ser_addr;
memset(&ser_add, 0, sizeof(ser_add));
ser_addr.sin_family = AF_INET; //IPv4
ser_addr.sin_port = htons(8000); //服务器的端口
inet_pton(AF_INET,"10.9.21.211", &ser_addr.sin_addr.s_addr); //服务器的IP地址由字符串转换为整型数据存放到ser_addr
//发送数据
sento(sockfd, "hello net", strlen("hello net"), 0, \
(struct sockaddr *)&ser_addr,sizeof(ser_addr));
close(sockfd);
return 0;
}
#include <stdio.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //struct sockaddr_in
#include <string.h> //memset
#include <arpa/inet.h> //htos
#include <unistd.h> //close
int main()
{
//创建通信的UDP的套接字(没有port、ip)
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
printf("UDP套接字sockfd=%d\n", sockfd);
//定义IPv4地址结构,存放本机信息
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9000);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//给udp套接字 bind绑定一个固定的地址信息
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
//接收udp消息
while(1)
{
//定义一个IPv4地址结构,存放发送者的信息
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
unsigned char buf[1500] = "";
int len = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&from_addr, &from_len);
//from_addr存放的就是发送者的消息
char ip[16] = "";
//转换为点分十进制ip
inet_ntop(AF_INET, &from_addr.sin_addr.s_addr, ip, 16);
printf("消息来自%s %hu--->", ip, ntohs(from_addr.sin_port));
printf("len:%d msg:%s\n", len, buf);
}
//关闭套接字
close(sockfd);
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h> //线程头文件
#include <arpa/inet.h>
void *send_function(void *arg)
{
//获取套接字
int sockfd = *(int *)arg;
//定义地址目的结构
struct sockaddr_in dst_addr;
bzero(&dst_addr,sizeof(dst_addr));
dst_addr.sin_family = AF_INET;
while(1)
{
//获取键盘输入
fgets(buf,sizeof(buf),stdin);
buf(strlen(buf)-1)=0;
//判断是否是IP port
//sayto IP port
if(strncmp(buf,"sayto",5) == 0)
{
char ip[16] = "";
unsigned short port = 0;
//sayto 10.9.21.211 8000
sscanf(buf,"sayto %s %hu", ip, &port);
dst_addr.sin_port = htons(port);
inet_pton(AF_INET, ip, &dst_addr.sin_addr.s_addr);
continue;
}
else
{
sendto(socked, buf, strlen(buf), 0,
\(struct sockaddr *)&dst_addr, sizeof(dst_addr));
if(strcmp(buf,"bye")==0)
break;
}
}
return NULL;
}
void *recv_function(void *arg)
{
int sockfd = *(int *)arg;
while(1)
{
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
unsigned char buf[1500]="";
char ip[16]="";
int len = recvfrom(sockfd, buf,sizeof(buf), 0,\
(struct sockaddr *)&from_addr, &from_len);
printf("%s %hu:%s\n",inet_ntop(AF_INET,&from_addr.sin_addr.s_addr,ip,16),\
ntohs(from_addr.sin_port),buf);
if(strcmp(buf,"bye")==0)
break;
}
return NULL;
}
int main(int argc, char const *argv[])
{
// 判断参数 ./a.out 8000\n");
if(arfc != 2)
{
printf("./a.out 8000\n");
return 0;
}
//创建udp套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//bind绑定固定的端口, IP
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[1]); ///atoi 将字符串转为数字
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
//创建发送线程
pthread_d send_tid;
pthread_create(&send_tid, NULL, send_function, (void *)&sockfd);
//创建接收线程
pthread_d recv_tid;
pthread_create(&recv_tid, NULL, recv_function, (void *)&sockfd);
pthread_join(send_tid, NULL);
pthread_join(recv_tid, NULL);
//关闭套接字
close(sockfd);
return 0;
}
7、8、9、10章节
将16位主机字节序数据转换成网络字节序数据
将主机字节序的端口转换成网络字节序
//头文件:
#include <arpa/inet.h>
uint16_t htons(uint16_t hostint16);
/*
参数:
uint16_t:unsigned short int
hostint16:待转换的16位主机字节序数据
返回值:
成功:返回网络字节序的值
*/
将32位网络字节序数据转换成主机字节序数据
//头文件:
#include <arpa/inet.h>
uint32_t ntohl(uint32_t netint32);
/*
参数:
uint32_t: unsigned int
netint32:待转换的32位网络字节序数据
返回值:
成功:返回主机字节序的值
*/
例
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int num = 0x01020304;//
int sum = htonl(num);
printf("%x\n",ntohl(sum));
return 0;
}
将16位网络字节序数据转换成主机字节序数据
//头文件:
#include <arpa/inet.h>
uint16_t ntohs(uint16_t netint16);
/*
参数:
uint16_t: unsigned short int
netint16:待转换的16位网络字节序数据
返回值:
成功:返回主机字节序的值
*/
例
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
short a = 0x0102;
short b = htons(a);
printf("%x\n",ntohs(b));
return 0;
}
字符串ip地址转整型数据
将点分十进制数串转换成32位无符号整数
//头文件:
#include <arpa/inet.h>
int inet_pton(int af,const char *stc, void *dst);
/*
参数:
af: 协议族 选IPV4对应的宏AF_INET ,选IPv6对应的宏AF_INET6
stc:点分十进制数串的首元素地址
dst:转换为32位无符号整数的地址
返回值:
成功返回1 、 失败返回其它
*/
例
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc,char *argv[])
{
char ip_str[] = "10.0.13.100";
unsigned int ip_uint = 0;
unsigned char * ip_p =NULL;//可以用char吗?
inet_pton(AF_INET,ip_str,&ip_uint);
printf("ip_uint = %d\n",ip_uint);
ip_p = (unsigned char *) &ip_uint;
printf("ip_uint = %d.%d.%d.%d\n",*ip_p,*(ip_p+1),*(ip_p+2),*(ip_p+3));
return 0;
}
整型数据转字符串格式ip地址
将32位无符号整型数据(默认大端)转成 点分十进制数组
//头文件:
#include <arpa/inet.h>
//len的宏定义
#define INET_ADDRSTRLEN 16 //for ipv4
#define INET6_ADDRSTRLEN 46 //for ipv6
const char *inet_ntop(int family, const void *addrptr,char *strptr, size_t len);
/*
参数:
family 协议族 AF_INET:IPv4 AF_INET6:IPv6
addrptr 32位无符号整数数据的地址
strptr 点分十进制数串的首元素地址
len 点分十进制数串的最大长度
返回值:
成功:则返回字符串的首地址
失败:返回NULL
*/
例
#include<stdio.h>
#include<arpa/inet.h>
int main()
{
unsigned char ip[]={10,0,13,252};
char ip_str[16];
inet_ntop(AF_INET,(unsigned int *)ip,ip_str,16);
printf("ip_str = %s\n",ip_str);
return 0;
}
存放IP地址(32位无符号整数)
//头文件:
#include <netinet/in.h>
struct in_addr
{
in_addr_t s_addr;//4字节
};
存放IPv4协议通信的所址信息
//头文件:
#include <netinet/in.h>
struct in_addr
{
in_addr_t s_addr;//4字节
};
struct sockaddr_in
{
sa_family_t sin_family; //2字节 协议AF_INE4 AF_INET6
in_port_t sin_port; //2字节 端口
struct in_addr sin_addr;//4字节 IP地址(32位无符号整数)
char sin_zero[8] //8字节 全写0
};
为了使不同格式地址能被传入套接字函数,地址须要强制转换成通用套接字地址结构
//头文件:
#include <netinet/in.h>
struct sockaddr
{
sa_family_t sa_family; // 2字节
char sa_data[14] //14字节
};
例
struct sockaddr_in //IPv4地址结构(存放客户端、服务器的地址信息(协议,port,IP))
struct sockaddr //通用地址结构,不是存放数据 socket API 类型转换
//在定义源地址和目的地址结构的时候,选用struct sockaddr_in;
struct sockaddr_in my_addr;
//当调用编程接口函数,且该函数需要传入地址结构时需要用struct sockaddr进行强制转换
bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));
向to结构体指针中指定的ip,发送UDP数据
#include <sys/socket.h>
ssize_t sendto(int sockfd,
const void *message,
size_t length,
int flags,
const struct sockaddr *dest_addr,
socklen_t dest_len);
/*
参数:
sockfd:从那个套接字发出
message:需要发送的消息的首元素地址
length: 消息的实际长度
flags:0 网络默认方式通信
dest_addr:指向目主机的IPv4地址信息(协议、port、IP地址)
dest_len:地址结构体的长度
返回值:
成功:发送数据的字符数
失败: -1
注意:
通过dest_addr和dest_len确定目的地址
可以发送0长度的UDP数据包
*/
接收UDP数据,并将源地址信息保存在from指向的结构中(默认没消息,阻塞)
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf,size_t nbytes,int flags,
struct sockaddr *from,socklen_t *addrlen);
/*
参数:
sockfd: udp套接字
buf: 用来存放接收消息的空间起始地址
nbytes: 能接收消息的最大字节数
flags: 套接字标志(常为0)
from: 存放发送者的IPv4地址信息(不关心发送者信息,可为NULL)
addrlen: 地址结构长度
返回值:
成功:接收到的实际字节数
失败: -1
注意:
通过from和addrlen参数存放数据来源信息
from和addrlen可以为NULL, 表示不保存数据来源
*/
bind给udp套接字绑定固定的port、IP信息
服务器收到客户端的信息,客户端的port是随机的。如果udp套接字不使用bind函数绑定固定端口,那么在第一次调用sendto系统会自动给套接字分配一个随机端口。后续sendto调用继续使用前一次的端口。
将本地协议地址与sockfd绑定
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr *address,socklen_t address_len);
/*
参数:
sockfd: socket套接字
sockaddr: 指向特定协议的地址结构指针
address_len:该地址结构的长度
返回值:
成功:返回0
失败:其他
注:只能绑定本地主机的
*/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。