5.getsockname与getpeername
getsockname与getpeername是返回套接口关联的本地协议地址和远程协议地址。
int getsockname(int sockfd, struct sockaddr * localaddr, socken_t * addrlen);
int getpeername(int sockfd, struct sockaddr * peeraddr, socken_t * addrlen);
返回0表示成功,返回1表示出错
?
参数sockfd表示你要获取的套接口的描述字。
localaddr返回本地协议地址描述结构, peeraddr返回远程协议地址描述结构,addrlen分别是上述2个结构的长度。这个2个函数不难,下面是使用样例
#include "/programe/net/head.h"
#include "stdio.h"
#include "stdlib.h"
int main(int argc, char ** argu) {
??????? int listenfd;
??????? struct sockaddr_in servaddr;
??????? pid_t pid;
??????? char temp[100];
??????? int n = 0;
??????? listenfd = socket(AF_INET, SOCK_STREAM, 0);
??????? bzero(&servaddr, sizeof(servaddr));
??????? servaddr.sin_family = AF_INET;
??????? servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
??????? servaddr.sin_port = htons(atoi(argu[1]));
??????? bind(listenfd, (struct socket_addr *)&servaddr, sizeof(servaddr));
??????? listen(listenfd, n);
??????? for(;;) {
??????????????? struct sockaddr_in local;
??????????????? int connfd = accept(listenfd, (struct sockaddr *)NULL, NULL);
??????????????? if((pid = fork()) == 0) {
??????????????????????? struct sockaddr_in serv, guest;
??????????????????????? char serv_ip[20];
??????????????????????? char guest_ip[20];
??????????????????????? int serv_len = sizeof(serv);
??????????????????????? int guest_len = sizeof(guest);
??????????????????????? getsockname(connfd, (struct sockaddr *)&serv, &serv_len);
??????????????????????? getpeername(connfd, (struct sockaddr *)&guest, &guest_len);
??????????????????????? inet_ntop(AF_INET, &serv.sin_addr, serv_ip, sizeof(serv_ip));
??????????????????????? inet_ntop(AF_INET, &guest.sin_addr, guest_ip, sizeof(guest_ip));
??????????????????????? printf("host %s:%d guest %s:%d\n", serv_ip, ntohs(serv.sin_port), guest_ip, ntohs(guest.sin_port));
??????????????????????? char buf[] = "hello world";
??????????????????????? write(connfd, buf, strlen(buf));
??????????????????????? close(connfd);
??????????????????????? exit(0);
??????????????? }
??????????????? close(connfd);
??????? }
}
?
?
[root@liumengli net]# cat client1.c
#include "/programe/net/head.h"
#include "stdio.h"
#include "stdlib.h"
int main(int argc, char ** argv) {
??????? int sockfd, n;
??????? int my;
??????? char buf[100];
??????? struct sockaddr_in servaddr;
??????? if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
??????????????? printf("create socket error\n");
??????????????? exit(1);
??????? }
??????? bzero(&servaddr, sizeof(struct sockaddr_in));
??????? servaddr.sin_family = AF_INET;
??????? servaddr.sin_port = htons(atoi(argv[2]));
??????? if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0) {
??????????????? printf("inet_pton error\n");
??????????????? exit(1);
??????? }
??????? if((my = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) < 0) {
??????????????? printf("connect error\n");
??????????????? exit(1);
??????? }
??????? struct sockaddr_in serv, guest;
??????? int serv_len = sizeof(serv);
??????? int guest_len = sizeof(guest);
??????? char serv_ip[20], guest_ip[20];
??????? getsockname(sockfd, (struct sockaddr *)&guest, &guest_len);
??????? getpeername(sockfd, (struct sockaddr *)&serv, &serv_len);
??????? inet_ntop(AF_INET, &guest.sin_addr, guest_ip, sizeof(guest_ip));
??????? inet_ntop(AF_INET, &serv.sin_addr, serv_ip, sizeof(serv_ip));
??????? printf("host? %s:%d, guest? %s:%d\n", serv_ip, ntohs(serv.sin_port), guest_ip, ntohs(guest.sin_port));
??????? n = read(sockfd, buf, 100);
??????? buf[n] = '\0';
??????? printf("%s\n", buf);
??????? getchar();
??????? close(sockfd);
??????? exit(0);
}
?
补充:getsockname和getpeername调度时机很重要,如果调用时机不对,则无法正确获得地址和端口。
?
TCP
对于服务器来说,在bind以后就可以调用getsockname来获取本地地址和端口,虽然这没有什么太多的意义。getpeername只有在链接建立以后才调用,否则不能正确获得对方地址和端口,所以他的参数描述字一般是链接描述字而非监听套接口描述字。
?
对于客户端来说,在调用socket时候内核还不会分配IP和端口,此时调用getsockname不会获得正确的端口和地址(当然链接没建立更不可能调用getpeername),当然如果调用了bind 以后可以使用getsockname。想要正确的到对方地址(一般客户端不需要这个功能),则必须在链接建立以后,同样链接建立以后,此时客户端地址和端口就已经被指定,此时是调用getsockname的时机。
?
UDP
UDP分为链接和没有链接2种(这个到UDP与connect可以找到相关内容)
?
没有链接的UDP不能调用getpeername,但是可以调用getsockname,和TCP一样,他的地址和端口不是在调用socket就指定了,而是在第一次调用sendto函数以后
?
已经链接的UDP,在调用connect以后,这2个函数都是可以用的(同样,getpeername也没太大意义。如果你不知道对方的地址和端口,不可能会调用connect)。