UDP是无连接不可靠的数据报协议,非常不同于TCP提供的面向连接的可靠字节流。然而相比于TCP,有些场合更适合使用UDP,使用UDP的一些常见应用程序有:
DNS,NFS和SNMP。借用linux一站式编程里的图:
一、基本套接字函数
udp所用函数:
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags,
struct sockaddr *from, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,
const struct sockaddr *to, socklen_t addrlen);
参数说明:
1. 需要说明的是recvfrom的struct sockaddr参数,这是一个值-结果传递,返回时填入收到的udp数据包的来源,并不是只接受某个主机的数据包
其他的参数很容易理解。
简单的udp服务器程序如下:
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#define SERV_PORT 8000 /* TCP and UDP */
#define MAXLINE 4096 /* max text line length */
int main(void)
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int sockfd;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int i, n;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
if (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
perror("bind error: ");
exit(0);
}
printf("Accepting connections ...\n");
while (1) {
cliaddr_len = sizeof(cliaddr);
n = recvfrom(sockfd, buf, MAXLINE, 0, (struct sockaddr *) &cliaddr,
&cliaddr_len);
if (n == -1) {
perror("recvfrom error");
exit(0);
};
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
for (i = 0; i < n; i++) {
buf[i] = toupper(buf[i]);
}
n = sendto(sockfd, buf, n, 0, (struct sockaddr *) &cliaddr,
sizeof(cliaddr));
if (n == -1) {
perror("sendto error");
exit(0);
}
bzero(buf,sizeof(buf));
}
}
上面程序使用基本的udp的socket函数,来接收客户端发送的字符,然后转换为大写,回送回去。。。
客户端程序:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#define SERV_PORT 8000 /* TCP and UDP */
#define MAXLINE 4096 /* max text line length */
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
socklen_t servaddr_len;
char buf[MAXLINE];
// char recvline[MAXLINE];
int sockfd, n;
if (argc != 2) {
perror("usage : tcpcli <ipaddress>");
exit(0);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
while(fgets(buf, MAXLINE, stdin) != NULL){
n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(n == -1){
perror("client sendto error: ");
exit(0);
}
n = recvfrom(sockfd, buf, MAXLINE, 0, NULL, NULL);
if(n == -1){
perror("client recive error: ");
exit(0);
}
buf[n] = 0;
fputs(buf,stdout);
}
}
客户端程序很容易理解,只是从标准输入读入数据然后发送到udp套接字。
运行结果:
zal@zal:~/NetBeansProjects/6-17$ ./udpcli 127.0.0.1
asd
ASD
asd
ASD
fd
FD
gfd
GFD
gf
GF
二、UDP的conect函数
udp程序使用conect函数,用来与确定的唯一对端进行通信,只是指定对端的地址信息,并不像tcp那样连接,不会到协议栈底层去,调用conect的通常是UDP客户端程序。与普通UDP区别:
1。不能使用sentto和resvfrom指定目的地址和端口号,改用write、sent、read、recv、recvmsg
2。由已经连接的(假连接)UDP套接字引发的异步错误返回给进程,而未连接的UDP不接受任何异步错误。
3。每次调用sendto都需要将目的地址拷贝到内核,对于已经连接的udp,只需要拷贝一次。效率高。。。