【问题标题】:getpeername() won't return a correct port but it returns a correct address of remote host socket language Cgetpeername() 不会返回正确的端口,但会返回远程主机套接字语言 C 的正确地址
【发布时间】:2014-04-13 02:39:36
【问题描述】:

我想问一下 getpeername() 函数,因为它返回标题状态的数据。我试图直接从 accept() 函数中获取值,结果也一样。即使地址的值是正确的,端口的值似乎也是随机出现的(地址是 127.0.0.1,因为我在一台机器上运行多进程)。 getpeername() 的返回码为 0(状态 = 0)。我正在使用 gcc 4.8.1 版。我编写了一个没有服务器的对等 2 对等聊天应用程序。以下是我的代码:

struct sockaddr_in addr;
socklen_t addr_len;
int tempPort, serverSockfd;
char test[100];

// Get serverSockfd successfully....
serverSockFd = initializeSock(PORT) // In this function I initialize socket(), bind() and listen(), serverSockFd is returned by the value of socket() 


addr_len = sizeof addr;
newSock = accept(serverSockfd, (struct sockaddr *)&addr, &addr_len);
tempPort = ntohs(addr.sin_port);
inet_ntop(AF_INET, &(addr.sin_addr), test, sizeof test);

printf("tempPort\t%d\n", tempPort);
printf("test\t%s\n", test);

addr_len = sizeof addr; 
if ((status = getpeername(newSock, (struct sockaddr *) &addr, &addr_len)) != 0){  
  printf("getpeername() error!\n");
}

tempPort = ntohs(addr.sin_port);
inet_ntop(AF_INET, &(addr.sin_addr), test, sizeof test);

printf("tempPort\t%d\n", tempPort);
printf("test\t%s\n", test);    

非常感谢您的任何评论。这是initializeSock()中的部分代码:

sd = socket(AF_INET, SOCK_STREAM, 0);

if(sd < 0)
{
  perror("SocketInit(): socket() error!\n");
  exit(1);
}

ret_val = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char*) &flag, sizeof(flag));
if(ret_val == -1)
{
  perror("SocketInit(): setsockopt(SO_REUSEADDR) error!\n");
  exit(1);
}


gethostname(hostname,100);
host_entry = gethostbyname(hostname);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)*host_entry->h_addr_list));

ret_val = bind(sd, (struct sockaddr*) &addr, sizeof(addr));

if(ret_val == -1)
{
  perror("SocketInit(): bind() error!\n");
  printf("For port:%d\n",port);
  exit(1);
}
....
return sd;

这是连接到对等点的服务器部分的代码。 ConnectSock(portOfPeerA):

sd = socket(AF_INET, SOCK_STREAM, 0);  

if(sd < 0)
{
  perror("ConnectToServer(): socket() error!\n");
  exit(1);
}

if (port != 0) {

addr.sin_family = AF_INET;
addr.sin_port = htons(portOfPeerA);
addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)*host_entry->h_addr_list));

// Do I need to bind() the port of peer B when it would like to connect to peer A? 

ret_val = connect(sd, (struct sockaddr*)&addr, sizeof(addr)); 
if(ret_val == -1)
{
  printf("Error connect());
  exit(1);
}
...

【问题讨论】:

  • 这里有问题吗?代码看起来都正确,并将打印客户端正确绑定的端口。你看到别的了吗?

标签: c sockets tcp p2p


【解决方案1】:

我不知道您从对等方接受哪个端口,但如果对等方正在连接到您的服务器(例如,一个调用接受),它将从(或多或少)随机端口连接,这就是 TCP 的工作方式。仅当对等方在连接之前显式绑定到该端口时,它才会从固定端口连接。

这意味着,发起端口的对等点不是在服务器端(您的代码片段的来源)定义的,而是在客户端(调用连接的一侧,您只进行连接但不绑定)。

但是,请注意,如果客户端和服务器都使用固定的 IP 和端口,重复连接可能会出现问题,因为这样您将在 TCP 中获得相同的 4 元组,它定义了重复连接的连接,因此进入所有这些问题都与各种 TIME_WAIT 状态有关。所以最好让客户端只选择一个可用的端口,而不是强迫它使用特定的端口。

【讨论】:

  • 这或多或少是正确的。如果端口未绑定,TCP 堆栈 API 将从临时端口范围中为其本​​地端口查找可用端口。在 Linux 上,这通常是 32768 到 61000,其他堆栈可能使用 49152 到 65535 的范围。
  • 嗨,Steffen,非常感谢您的评论。它是一个没有服务器的对等 2 对等聊天应用程序。假设我有 2 个进程 A 和 B。首先我将 A 作为“./p2p groupName serverPortOfA”运行,然后我像“./p2p groupName serverPortOfB serverPortOfA”一样运行 B。 A#Server 被初始化返回 A#ServerSockFd。然后初始化B#Client,连接到A#Server的端口,返回B#ClientSockFD。在while循环中,select()在进程A中返回A#ServerSockFd,newSock被创建。实际上,当我尝试在进程A中调用getpeername()时,B的端口值是不正确的。
  • 我无法关注您的评论,但就像我说的 - 如果您希望 TCP 连接源自特定端口,您必须将套接字显式绑定到该端口,否则系统将选择一个端口靠自己。
  • 我已经将套接字绑定到端口,因为我在我的问题中添加了更多代码。那么这里的绑定是什么意思?这是否意味着我应该调用 bind() 到端口,是吗?
  • 问题不在于 getpeername 报告他正确的端口。如果您想报告另一个端口,您需要在客户端绑定到它 - 所以您最好发布调用 connect 的部分,而不是调用 accept 的部分。
【解决方案2】:

getpeername()(和accept())报告远程方在其端本地绑定的IP和端口。如果远程方是在调用connect() 之前没有调用bind() 的客户端,则connect() 执行对随机可用端口的隐式绑定。这就是您所看到的,并且是典型的用法。 大多数客户不需要在connect() 之前调用bind(),但有些用例需要这样做,所以不排除。

【讨论】:

  • 我明白了。非常感谢您的评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-18
  • 1970-01-01
  • 1970-01-01
  • 2011-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多