0

linux x86_64、gcc、ipv4。

这就是我如何理解我的 IPv4 客户端是谁。首先,我从客户端句柄开始,它不是指针而是整数(嗯?)。然后,我需要为目标 sockaddr 结构分配空间,并为 unsigned int 分配空间以保持其长度(作为参考传递)。然后我用整数客户端句柄和我的目标 sockaddress 和 sockaddress 长度调用 getsockname()。(在内部,可能是 getsockname() 将内核的 IP 表示和其他信息复制到我自己的 sockaddr 中。)然后我需要使用 sockaddr 结构调用 ntoa() 来获取一个字符串。最后,我需要将此字符串转换为整数。(还有 getpeername 和 getsockname。)

这些是我看到的例子。对于延迟很重要的服务器来说,这似乎是可移植的但效率低下。但是假设我想留在 x86_64 架构和 IPv4 上的 linux 中。我猜内核内部已经在某处存储了一个 IP 号作为 int,因此这里的大多数往返调用都不是必需的,因此可以直接查找 IPv4,并且在没有中间存储的情况下更快地查找。这样的功能存在吗?我找了很久,但没有找到。

如果 getsockname() 函数有一个替代方案,它只是将 IP 表示形式返回为单个整数,那将很有意义。这也将节省制作副本的时间和开销。这存在吗?(PS:顺便说一句,如果它确实存在,那么有一个类似ntoa()的调用将这个unique-per-IP int映射到标准的xx.xx.xx.xx字符串是合乎逻辑的,即不需要中间 sockaddress。)

如果不存在这样的函数,至少,我应该能够避免 ntoa 到字符串并返回到整数转换。这意味着我必须知道 sockaddr 结构中的哪些(甚至是否)字节代表 IP,所以我可以将这些字节转换为 4 字节的 C 整数。我认为这是字节 2-6。正确的?

/iaw

4

3 回答 3

1

I'm trying to parse your question - I might be getting some of the details wrong.

"client handle, that is not a pointer but an integer"

"allocate space for a destination sockaddr structure, and allocate space for an unsigned int to hold its length"

  • Yes, these do need to be allocated, but it doesn't matter where from. Allocating from the stack would certainly be faster than allocating from the heap (malloc() and friends)

"I call getsockname() with the integer client handle and my destination sockaddress and sockaddress length"

I'm not sure if you're after the local or the remote address for a given socket:

Regardless, I don't see any need for any ntoa()-like call if all you need is the int representation of the IP address - the struct sockaddr_in filled in by the get[peer|sock]name() call already contains that in the sin_addr member.

So assuming you already have the client FD (returned from accept()) and you want the remote address for that socket then all you need to do is something like this:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

...

    struct sockaddr_in peer_sa;                         // Stack allocation
    socklen_t peer_sa_len = sizeof(struct sockaddr_in); // Stack allocation
    int fd;
    int status;

...

    // fd is the client file descriptor, as returned by accept()
    status = getpeername(fd, (struct sockaddr *)&peer_sa, &peer_sa_len);
    // check your return codes!

    printf("peer address %s as int is %d\n",
       inet_ntoa(peer_sa.sin_addr), peer_sa.sin_addr.s_addr);

Note inet_ntoa(peer_sa.sin_addr) is just used above for illustrative purposes.

Also note this is very IPv4 specific. If you expect IPv6 clients too, then you'll need to use struct sockaddr_storage instead (which should be sized big enough to fit any address family). Also 128bit IPv6 addresses obviuosly don't fit into 32bit integers. Also you would have to use inet_ntop() to give a text representation of the address.

于 2013-10-15T00:26:00.120 回答
0

IPv4 连接sockaddr_in填充的结构getsockname()套接字地址的内部二进制表示。getpeername()

如果您有一个指向 a 的指针struct sockaddr_in *sinsockaddr_insin->sin_addr.s_addrauint32_t表示网络字节顺序中的 IPv4 地址。

请注意,getsockname()检索连接的本地端和getpeername()远程端的地址。

于 2013-10-15T00:42:39.557 回答
0

(警告:我是这个东西的初学者。)

好的,我想我现在已经清楚地编写了代码。84 行。该代码不可移植。如果服务器是 linux x86_64,它只应该与 IPv4 一起使用。它应该使用 gcc 编译并开箱即用地运行。

下面启动一个接受单个客户端的 TCP 服务器。服务器有它的整数句柄 servSock,客户端有它的整数句柄 clntSock。该代码包含仅将这些整数句柄作为输入的函数,并以唯一整数形式(例如 iwhoareyou)或字符串形式(例如 swhoareyou)返回服务器和客户端 IP 地址。

我相信这段代码表现得很好,包括执行所有适当的清理代码。(它不会超时,但这并不重要,因为它会在客户端出现后立即关闭连接。没有接收或发送。)有时,它返回 0.0.0.0,有时它返回 127.0.0.1,但我相信这是正常行为.

如果仍然存在错误,即使是轻微的错误,请告诉我。在处理网络和许多进程时由于偏执狂......

#include <stdio.h>      // printf, fprintf
#include <sys/socket.h> // socket, bind, connect
#include <arpa/inet.h>  // sockaddr_in, inet_ntoa
#include <stdlib.h>     // exit
#include <string.h>     // strcpy, bzero
#include <unistd.h>     // close

/****************************************************************/
#define IPNAMELEN 32
typedef char IPNAME[IPNAMELEN];  // must fit 4*4 + terminator

/****************************************************************/

static inline unsigned long iwho( const int clntSocket,
              int (*function)(const int, struct sockaddr *, unsigned int *) ) {
  struct sockaddr_in ipsock_in;
  unsigned int cisl= sizeof(struct sockaddr_in);
  if (getpeername( clntSocket, (struct sockaddr *)&ipsock_in, &cisl )) return 0;
  return  *((unsigned long *) (&ipsock_in.sin_addr));
}

static inline unsigned long iwhoareyou( const int clntSocket ) {
    return iwho(clntSocket, getpeername ); }

static inline char *swhoareyou( const int clntSocket, IPNAME ipbuf ) {
  unsigned long il= iwhoareyou(clntSocket);
  return strcpy(ipbuf, inet_ntoa( *((struct in_addr *) (&il)) ));
}


static inline unsigned long iwhoami( const int servSocket ) {
     return iwho(servSocket, getsockname ); }

static inline char *swhoami( const int servSocket, IPNAME ipbuf ) {
  unsigned long il= iwhoareyou(servSocket);
  return strcpy(ipbuf, inet_ntoa( *((struct in_addr *) (&il)) ));
}


/****************************************************************/
int main() {
  const unsigned short ServPort=8081;

  void die(char *s) { fputs(s, stderr); fputc('\n', stderr); exit(1); }

  int servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (servSock<=0) die("cannot socket()");

  struct sockaddr_in ServAddr;
  bzero( &ServAddr, sizeof(ServAddr));
  ServAddr.sin_family = AF_INET;
  ServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  ServAddr.sin_port = htons(ServPort);

  {
    int yes=1;    // tell the OS that we want to use it again
    if (setsockopt(servSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) die("cannot setsockopt");
  }

  if (bind(servSock, (struct sockaddr *) &ServAddr, sizeof(ServAddr)) < 0) die("cannot bind");

  if (listen(servSock, 2) < 0) die("cannot listen");

  struct sockaddr_in ClntAddr;
  unsigned int clntLen = sizeof(ClntAddr);
  int clntSock = accept(servSock, (struct sockaddr *) &ClntAddr, &clntLen);
  if (clntSock < 0) die("cannot accept()");

  printf("Server= %s.\n", inet_ntoa(ServAddr.sin_addr));
  printf("Client= %s.\n", inet_ntoa(ClntAddr.sin_addr));

  IPNAME buf;
  printf("Via Function on clntSock: '%lu' and '%s'\n", iwhoareyou(clntSock), swhoareyou(clntSock, buf));
  printf("Via Function on servSock: '%lu' and '%s'\n", iwhoami(servSock), swhoami(servSock, buf));

  if (close(clntSock)) die("cannot close clnt");
  if (close(servSock)) die("cannot close serv");

  printf("Happy Ending\n");
  return 0;
}
于 2013-10-15T18:06:03.763 回答