关于用户数据报文协议(UDP):
UDP是一个简单的面向数据报的运输层协议:进程的每个输出操作都正好产生一个UDP数据报,并组装成一份待发送的IP数据报。而且UDP提供的是不可靠的非连接型的传输层服务,允许在源站点和目的站点之间传送数据,而不必在传送数据之间建立连接,进而开销比较低,主要用于那些不要求TCP协议的非连接型应用程序。例如:网络管理,视频点播、网络会议...
简单的数据报通信的框图
常用的API:(不完善)
函数名称 socket()
功能,创建一个套接字
原型:int socket(int domain, int type, int protocol)
所属头文件:<sys/socket.h>
成功:返回文件套接字描述符 失败:返回-1
参数说明:
domain:网络协议域 , 如AF_INET-- IPv4域 AF_INET6--IPv6域
type : 套接字类型
如SOCK_DGRAM:固定长度的,无连接的,不可靠的报文传递,默认为UDP
SOCK_STREAM:有序的,可靠的,双向的,面向连接的字节流,默认为TCP
SOCK_RAW:IP协议的数据报接口
protocol: 通常为0 ,表示为给定的域和套接字类型选择一个特定的协议
函数名称bind
功能:关联地址和套接字
原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
所属头文件:<sys/socket.h>
成功:返回0 失败:返回-1
参数说明:
sockfd : 绑定的套接字
addr: 绑定的地址,对于因特网域,若指定地址内的IP为INADDR_ANY,则套接字断点可以被绑定到所有的系统网络接口上,这意味着可以接受这个系统所安装的任何一个网卡的数据包
len : 绑定地址的大小。
函数名称:sendto()
功能 : 发送数字,面对无连接的套接字
原型:ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t destlen)
所属头文件:<sys/socket.h>
成功:返回发送的字节数 失败:返回-1
参数说明:
sockfd : 套接字
buf :需发送的数据
nbytes: 需发送的数据长度
flags:一般为0
destaddr:目的地址(务必转换类型)
destlen:地址长度
函数名称:recvfrom()
功能 : 接收数据,可以获取数据发送者的源地址
原型 : ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict addrlen)
所属头文件:<sys/socket.h>
成功:返回接受的字节数 ,无可用数据则返回0 失败:返回-1
参数说明:
sockfd : 套接字
buf :接收的数据
nbytes: 接收的数据长度
flags:一般为0
addr :发送数据者的套接字端点地址
addrlen:包含了addr所指向的套接字自缓冲区的字节长度
函数名称colse()
原型: int close(int fd)
功能 : 关闭一个文件,关闭一个套接字
头文件:<unistd.h>
成功:0 失败:-1
函数名称:shutdown()
原型:int shutdown(int sockfd, int how)
功能 : 禁止一个套接字的IO
头文件:<sys/socket.h>
成功:0 失败:-1
参数说明:
sockfd : 套接字
how :
SHUT_RD : 将无法从套接字中读取数据
SHUT_WR:关闭写端,将无法使用套接字发送数据
SHUT_RDWR:关闭读写端,既无法读,又无法发送
服务器收发基本流程:
1. 创建套接字
2. 初始化套接字(把相应字段转换成网络字节序后填入)
3. 绑定套接字到一个具体的地址端口
4. 收发处理
4.1 循环收发数据报
4.2 打印收发的信息
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#define SERVER_PORT 8880
#define BUFFER 1024
void udp_respon(int sockfd);
int main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in addr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&addr, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);//注意字节序的转换
addr.sin_addr.s_addr = htonl(INADDR_ANY);//设置IP为INADDR_ANY,这可以绑定套接字端口到任意的本地地址上
if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))<0)
{
printf("Bind Error!\n");
exit(1);
}
udp_respon(sockfd);
close(sockfd);
return 0;
}
void udp_respon(int sockfd)
{
struct sockaddr_in addr;//用于接收源端口信息,即客户端的信息
unsigned int addrlen = 16, n;
char msg[BUFFER]; //用于接收数据的缓冲区
char echo[] = "[server ehco]";
while(1)
{
if(n = recvfrom(sockfd, msg, BUFFER, 0, (struct sockaddr*)&addr, &addrlen) < 0)
{
printf("Recive Error!");
exit(1);
}
fprintf(stdout, "I have received: %s", msg);
strcat(msg, echo);
printf("msg = %s\n", msg);
printf("client port: %d\n", ntohs(addr.sin_port));
sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&addr, addrlen);
}
}
客户端收发基本流程:
1. 判断使用客户端是否合理,及用法
2. 创建服务端套接字(从客户端中提取IP和端口号)和客户端套接字(由客户端代码指定),注意格式的转换
3. 绑定客户端套接字
4.接收处理
4.1 提示输入信息,打印发送数据
4.2 接收服务器回传的数据
代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define BUFFER 1024
void udp_respon(int sockfd, const struct sockaddr_in *addr, int len)
{
char buffer[BUFFER];
int n;
while(1)
{
fgets(buffer, BUFFER, stdin);
sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr*)addr, len);
bzero(buffer, BUFFER);
n = recvfrom(sockfd, buffer, BUFFER, 0, NULL, NULL);
buffer[n] = 0;
fprintf(stdout, "I have received: %s", buffer);
}
}
int main(int argc, char *argv[])
{
int sockfd, port;
struct sockaddr_in s_addr;//服务端地址结构
struct sockaddr_in c_addr;//客户端地址结构
if(argc != 3)
{
fprintf(stderr, "usage: %s IP Port", argv[0]);
exit(1);
}
if((port = atoi(argv[2])) < 0) //注意此处的优先级
{
printf("The port is error\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd<0)
{
fprintf(stderr,"Socket Error:%s\n",strerror(errno));
exit(1);
}
bzero(&s_addr, sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(port);
if(inet_pton(AF_INET, argv[1], &s_addr.sin_addr) < 0 )
{
fprintf(stderr, "Ip errpr %s\n", strerror(errno));
exit(1);
}
/*if(inet_aton(argv[1], &s_addr.sin_addr) < 0)
{
fprintf(stderr, "Ip error:%s\n", strerror(errno));
exit(1);
}*/
bzero(&c_addr, sizeof(struct sockaddr_in));
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(8848);
c_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) < 0)
{
fprintf(stderr, "Bind Error: %s\n", strerror(errno));
exit(1);
}
udp_respon(sockfd, &s_addr, sizeof(struct sockaddr_in));
close(sockfd);
}
通信演示:
服务端:
客户端:
注:注意赋值符号的优先级比较低。
如:
if(a = fuc(b) < c)... 函数结果先与c作比较,在赋值给a.
if( ( a = fuc(b) ) <c )..与上述表达式有很大不同