歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> Unix基礎知識 >> UNIX網絡編程:getsockname和getpeername函數

UNIX網絡編程:getsockname和getpeername函數

日期:2017/3/3 14:55:43   编辑:Unix基礎知識

這兩個函數或者返回與某個套接字關聯的本地協議地址(getsockname),或者返回與某個套接字關聯的外地協議地址即得到對方的地址(getpeername)。

#include <sys/socket.h>  
int getsockname(int sockfd,struct sockaddr* localaddr,socklen_t *addrlen);  
int getpeername(int sockfd,struct sockaddr*  peeraddr,socklen_t *addrlen);  
均返回:若成功則為0,失敗則為-1

getpeername只有在連接建立以後才調用,否則不能正確獲得對方地址和端口,所以它的參數描述字一般是已連接描述字而非監聽套接口描述字。

沒有連接的UDP不能調用getpeername,但是可以調用getsockname和TCP一樣,它的地址和端口不是在調用socket就指定了,而是在第一次調用sendto函數以後。

已經連接的UDP,在調用connect以後,這2個函數(getsockname,getpeername)都是可以用的。但是這時意義不大,因為已經連接(connect)的UDP已經知道對方的地址。

需要這兩個函數的理由如下:

在一個沒有調用bind的TCP客戶上,connect成功返回後,getsockname用於返回由內核賦予該連接的本地IP地址和本地端口號。

在以端口號為0調用bind(告知內核去選擇本地臨時端口號)後,getsockname用於返回由內核賦予的本地端口號。

在一個以通配IP地址調用bind的TCP服務器上,與某個客戶的連接一旦建立(accept成功返回),getsockname就可以用於返回由內核賦予該連接的本地IP地址。在這樣的調用中,套接字描述符參數必須是已連接套接字的描述符,而不是監聽套接字的描述符。

當一個服務器的是由調用過accept的某個進程通過調用exec執行程序時,它能夠獲取客戶身份的唯一途徑便是調用getpeername。

例如下面的,inetd調用accept(左上方的方框)返回兩個值:已連接套接字描述符connfd,這是函數的返回值;客戶的IP地址及端口號,如圖中標有“對端地址”的小方框所示(代表一個網際網套接字地址結構)。inetd隨後調用fork,派生出inetd的一個子進程。這樣父進程的那個套接字地址結構在子進程也可用,那個已連接套接字描述符也是如此。然而當子進程調用exec執行真正的服務器程序(譬如說Telent服務器程序)時,子進程的內存映像被替換成新的Telnet服務器的程序文件(也就是說包含對端地址的那個套接字地址結構就此丟棄),不過那個已連接套接字描述符跨exec繼續保持開放。Telnet服務器首先調用的函數之一便getpeername
,用於獲取客戶的IP地址和端口號。

顯然,最後一個例子中的Telnet服務器必須在啟動之後獲取connfd的值。獲取該值有兩個常用方法:

調用exec的進程可以把這個描述符格式化成一個字符串,再把它作為一個命令行參數傳遞給新程序。

約定在調用exec之前,總是把某個特定描述符置為所接受的已連接套接字的描述符。

inetd采用的是第二種方法,它總是把描述符0、1、2置為所接受的已連接套接字的描述符(即將已連接套接字描述符dup到描述符0、1、2,然後close原連接套接字)。

查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/

服務器的代碼:

#include    "unp.h"  
      
int
main(int argc, char ** argv)  
{  
    int         listenfd,connfd;  
    struct      sockaddr_in servaddr;  
    pid_t       pid;  
    char        temp[20];  
      
    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(10010);  
    Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));  
    Listen(listenfd, LISTENQ);  
    for( ; ; )  
    {  
        struct sockaddr_in local;  
        connfd = Accept(listenfd, (SA *)NULL, NULL);  
        if((pid = fork()) == 0)  
        {  
            Close(listenfd);struct sockaddr_in serv, guest;  
            char serv_ip[20];  
            char guest_ip[20];  
            socklen_t serv_len = sizeof(serv);  
            socklen_t 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);  
    }  
}

客戶端的代碼:

#include "unp.h"  
#define DEST_IP "127.0.0.1"  
      
int
main(int argc, char ** argv)  
{  
    int         sockfd, n;  
    char        buf[100];  
    char        serv_ip[20], guest_ip[20];  
    struct      sockaddr_in servaddr;  
      
    sockfd = Socket(AF_INET, SOCK_STREAM, 0);  
    bzero(&servaddr, sizeof(struct sockaddr_in));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_port = htons(10010);  
      
    Inet_pton(AF_INET, DEST_IP, &servaddr.sin_addr);  
    Connect(sockfd, (SA *)&servaddr, sizeof(servaddr));  
      
    struct sockaddr_in serv, guest;  
    socklen_t serv_len = sizeof(serv);  
    socklen_t guest_len = sizeof(guest);  
      

    getsockname(sockfd, (SA *)&guest, &guest_len);  
    getpeername(sockfd, (SA *)&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);  
    Close(sockfd);  
    exit(0);  
}

TCP

對於服務器來說,在bind以後就可以調用getsockname來獲取本地地址和端口,雖然這沒有什麼太多的意義。getpeername只有在鏈接建立以後才調用,否則不能正確獲得對方地址和端口,所以他的參數描述字一般是鏈接描述字而非監聽套接口描述字。

對於客戶端來說,在調用socket時候內核還不會分配IP和端口,此時調用getsockname不會獲得正確的端口和地址(當然鏈接沒建立更不可能調用getpeername),當然如果調用了bind 以後可以使用getsockname。想要正確的到對方地址(一般客戶端不需要這個功能),則必須在鏈接建立以後,同樣鏈接建立以後,此時客戶端地址和端口就已經被指定,此時是調用getsockname的時機。

查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/

Copyright © Linux教程網 All Rights Reserved