在兩台計算機上建立一個網絡連接,需要五個要素:本機地址 本機端口 協議類型 遠端端口 遠端地址.那么如何從一個建立好的連接上獲取這些信息呢.就需要用到
getsockname 和 getpeername 這兩個函數.
但前提是要在建立好的連接上.
作為客戶端,要在正確調用connect()之后,才能使用這兩個函數
作為服務端,要在正確調用accept()之后,才能使用這兩個函數
以下為測試代碼, windows/linux 下均可編譯運行.
#include <stdint.h> #include <stdio.h> #include <memory.h> #ifdef WIN32 #include <windows.h> typedef int32_t socklen_t; #define close(x) closesocket(x) #else #include <errno.h> #include <arpa/inet.h> #include <unistd.h> #define INVALID_SOCKET (-1) typedef int32_t SOCKET; #endif int32_t Errno() { #ifdef WIN32 return WSAGetLastError(); #else return errno; #endif } void test_getname(SOCKET sock, const char *desc) { printf("%s\n", desc); struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); int32_t ret = getsockname(sock, (struct sockaddr *)&addr, &addr_len); if(ret == 0) { printf("getsockname succ:%s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); } else { printf("getsockname failed,error=%d\n", Errno()); } memset(&addr, 0, sizeof(addr)); ret = getpeername(sock, (struct sockaddr *)&addr, &addr_len); if(ret == 0) { printf("getpeername succ:%s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); } else { printf("getpeername failed,error=%d\n", Errno()); } } void test_connect() { sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(5000); SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); test_getname(sock, "before connect"); int32_t ret = connect(sock, (sockaddr*)&addr, sizeof(addr)); if(ret != 0) { printf("connect error, errno: %d\n", Errno()); return; } test_getname(sock, "after connect"); close(sock); } void test_accept() { SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(INVALID_SOCKET == sock) { printf("create socket error,errno=%d\n", Errno()); return; } sockaddr_in srvAddr; memset(&srvAddr, 0, sizeof(srvAddr)); srvAddr.sin_family = AF_INET; srvAddr.sin_addr.s_addr = htonl(INADDR_ANY); srvAddr.sin_port = htons(5000); test_getname(sock, "before bind"); int32_t ret = bind(sock, (sockaddr*)&srvAddr, sizeof(srvAddr)); printf("bind:%s:%d\n", inet_ntoa(srvAddr.sin_addr), ntohs(srvAddr.sin_port)); test_getname(sock, "after bind"); if(ret != 0) { printf("bind listen socket error,errno=%d\n", Errno()); return; } ret = listen(sock, SOMAXCONN); if(ret != 0) { return; } sockaddr_in cliAddr; socklen_t cliAddrLen = sizeof(cliAddr); SOCKET new_sock = accept(sock, (sockaddr*)&cliAddr, &cliAddrLen); printf("accept:%s:%d\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port)); test_getname(new_sock, "after accept"); close(sock); close(new_sock); } int32_t main() { #ifdef WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); #endif //test_connect(); test_accept(); #ifdef WIN32 WSACleanup(); #endif getchar(); return 0; }
測試結果:
connect:
before connect getsockname failed,error=10022 getpeername failed,error=10057 after connect getsockname succ:127.0.0.1:4618 getpeername succ:127.0.0.1:5000
accept:
before bind getsockname failed,error=10022 getpeername failed,error=10057 bind:0.0.0.0:5000 after bind getsockname succ:0.0.0.0:5000 getpeername failed,error=10057 accept:127.0.0.1:4630 after accept getsockname succ:127.0.0.1:5000 getpeername succ:127.0.0.1:4630