对于已连接的UDP套接口,发生了三个变化:
1、再也不能给输出操作指定目的IP地址和端口,也就是说,不能再调用 sendto,而使用send或者write。写到已连接UDP套接口上的任何东西都自动发送到由connect指定的协议地址。
2、不用recvfrom,而用recv或者read。在已连接的UDP套接口上由内核为输入操作返回的唯一数据报是来自connect所指定协议地址的数据报。
3、异步错误由已连接UDP套接口返回给进程 。未连接UDP套接口不接收任何异步错误。
给一个UDP套接口多次调用 connect
对于已连接UDP套接口,进程可给那个套接口再次调用 connect达到下面两个目的:
1、指定新的IP地址和端口号
2、断开套接口
第一种情况即给已连接UDP套接口指定新的对方,与TCP套接口中connect的使用有所不同,只可给TCP套接口调用 一次connect
第二种情况,调用 connect,但是设置 套接口地址结构的地址族为AF_UNSPEC。这可能返回一个EAFNOSPPORT错误,但没有关系,使得套接口断开连接的是在已连接UDP套接口上调用 connect的进程。
性能
在未连接UDP套接口上调用 sendto时,内核作的工作是:暂时连接套接口,发送数据报,然后断开套接口连接。在未连接UDP套接口上给两个数据报调用 函数sendto导致内枋执行下六步:
1、连接套接口
2、输出第一个数据报
3、断开套接口连接
4、连接套接口
5、输出第二个数据报
6、断开套接口连接
对于已连接UDP套接口,其步骤为:
1、连接套接口
2、输出第一个数据报
3、输出第二个数据报
服务器端:
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define SERV_PORT 9999
#define BUF_LEN 128
void dg_echo(int sockfd, struct sockaddr_in *pcliaddr, socklen_t clilen)
{
char buf[BUF_LEN];
socklen_t len;
int n;
for (;;) {
len = clilen;
if ((n = recvfrom(sockfd, buf, BUF_LEN, 0, (struct sockaddr*)pcliaddr, &len)) < 0) {
printf("recvfrom error:%s\n", strerror(errno));
continue;
}
sendto(sockfd, buf, n, 0, (struct sockaddr*)pcliaddr, len);
}
}
int main(int argc, char **argv)
{
struct sockaddr_in servaddr, clientaddr;
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
printf("socket error:%s\n", strerror(errno));
return -1;
}
memset(&servaddr, 0x00, 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) {
printf("bind error:%s\n", strerror(errno));
close(sockfd);
return -1;
}
dg_echo(sockfd, &clientaddr, sizeof(clientaddr));
return 0;
}
客户端:
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERV_PORT 9999
#define BUF_LEN 128
char *sock_ntop(const struct sockaddr* sockaddr, socklen_t len)
{
static char str[BUF_LEN];
char portstr[7];
struct sockaddr_in *sin;
switch (sockaddr->sa_family) {
case AF_INET:
sin = (struct sockaddr_in*)sockaddr;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == (const char *)NULL) {
printf("inet_ntop error:%s\n", strerror(errno));
return NULL;
}
if (ntohs(sin->sin_port) != 0) {
snprintf(portstr, sizeof(portstr), ":%d", ntohs(sin->sin_port));
strcat(str, portstr);
}
return str;
}
return NULL;
}
void dg_cli(FILE *fp, int sockfd, const struct sockaddr_in * pservaddr, socklen_t servlen)
{
int n;
char sendline[BUF_LEN], recvline[BUF_LEN];
struct sockaddr reply_addr;
socklen_t len;
if (connect(sockfd, (struct sockaddr*)pservaddr, servlen) < 0) {
printf("connect error:%s\n", strerror(errno));
return ;
}
while (fgets(sendline, BUF_LEN, fp) != NULL) {
//sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr*)pservaddr, servlen);
write(sockfd, sendline, strlen(sendline);
len = servlen;
//n = recvfrom(sockfd, recvline, BUF_LEN, 0, &reply_addr, &len);
n = read(sockfd, recvline, BUF_LEN);
if (len != servlen || memcmp(&reply_addr, pservaddr, len) != 0) {
printf("receive (%s) ignored\n", sock_ntop(&reply_addr, len));
continue;
}
recvline[n] = 0;
fputs(recvline, stdout);
}
}
int main(int argc, char **argv)
{
struct sockaddr_in servaddr;
int sock;
memset(&servaddr, 0x00, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0) {
printf("inet_pton error:%s\n", strerror(errno));
return -1;
}
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
printf("socket error:%s\n", strerror(errno));
return -1;
}
dg_cli(stdin, sock, &servaddr, sizeof(servaddr));
return 0;
}