linux C++ socket 如何获取客户端ip地址?(第一次accept无法获取客户端ip原因,第一次获取ip为0.0.0.0,需要赋初始size)

Posted Dontla

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux C++ socket 如何获取客户端ip地址?(第一次accept无法获取客户端ip原因,第一次获取ip为0.0.0.0,需要赋初始size)相关的知识,希望对你有一定的参考价值。

参考文章1:C++根据SOCKET获取套接字IP、Port等信息的代码

参考文章2:获取socket客户端的IP

参考文章3:第一次accept 无法获取客户端 ip

第一次获取的客户端的ip老是0.0.0.0,不知道是怎么回事,后面连接进来的客户端ip就是正确的

原因是没有给下面代码中的client_len参数赋初始size

 if ( (m_clientfd=accept(m_listenfd, (struct sockaddr*)&client_address, &client_len)) <= 0)

The addrlen argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.
addrlen 参数是一个值结果参数:调用者必须将其初始化为包含 addr 指向的结构的大小(以字节为单位); 返回时它将包含对等地址的实际大小。
注:这个参数既是输入,也是输出,但第一次没有输出,所以我们需要给它赋值,,,虽然如果不赋值,除第一次外也行。。。

附socket服务端代码

socket_server_4_docker_test.cpp

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdlib.h>

#include <mutex>
#include <sys/time.h>

#define KY_AI_API_MAX_BUF_LEN 1024
struct sockaddr_in client_address;	  //arnold add 20220530
unsigned int client_len;	  //arnold add 20220530
char client_ip[256];
static  std::mutex s_ky_ai_mutex;


#define KY_AI_RECV_DATA_FILE "./test_log/ky_ai_recv_data_"


#if 1
#define KY_AI_RECV_DATA(format, ...)                                                \\
    do    /*1.check file size*/                                                    \\
           s_ky_ai_mutex.lock();                                                    \\
          /*2.generate a time string and connect it to the log string tail.*/       \\
          char str[KY_AI_API_MAX_BUF_LEN] = 0;                                    \\
	      if(format[0] != '\\n' && strlen(format) > 1)                           \\
                                                                                   \\
              struct timeval tv;                                                    \\
              struct tm* t;                                                         \\
              gettimeofday(&tv, NULL);                                              \\
              t = localtime(&tv.tv_sec);                                            \\
              sprintf(str,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld] ",                 \\
                             1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday,          \\
                             t->tm_hour, t->tm_min, t->tm_sec, tv.tv_usec / 1000);  \\
              strcat(str,format);                                                   \\
                                                                                   \\
           else                                                                     \\
                                                                                   \\
              strcpy(str,format);                                                   \\
	                                                                	    \\
          /*3. write the log to the cfg file*/                                      \\
	  std::string local_ip(client_ip);					    \\
          std::string filename = KY_AI_RECV_DATA_FILE + local_ip + ".txt";          \\
          FILE* logFile = fopen(filename.c_str(), "a");                             \\
          fprintf(logFile, str, ##__VA_ARGS__);                                     \\
          fclose(logFile);                                                          \\
          s_ky_ai_mutex.unlock();                                                   \\
        while (0)
#endif










class CTcpServer

public:
  int m_listenfd;   // 服务端用于监听的socket
  int m_clientfd;   // 客户端连上来的socket
 
  CTcpServer();
 
  bool InitServer(int port);  // 初始化服务端
 
  bool Accept();  // 等待客户端的连接
 
  // 向对端发送报文
  int  Send(const void *buf,const int buflen);
  // 接收对端的报文
  int  Recv(void *buf,const int buflen);
 
  void CloseClient();    // 关闭客户端的socket
  void CloseListen();    // 关闭用于监听的socket
 
 ~CTcpServer();
;
 
CTcpServer TcpServer;
 

static void demo_signal_call_back(int sig)	//arnold modified 20220420
 
  printf("\\n");
  printf("received signal: [%d]\\n\\n", sig);

  exit(0);



int main()

  signal(SIGCHLD,SIG_IGN);  // 忽略子进程退出的信号,避免产生僵尸进程

  //signal acquisition[must must must add]	//信号(可在摄像头里用kill -l查看信号表)
  signal(SIGINT, demo_signal_call_back);//interrupt	//ctrl+c	//kill -2
  signal(SIGTERM, demo_signal_call_back);//abort	//kill或kill all命令	//kill -15	
  signal(SIGFPE, demo_signal_call_back);//math processing	//浮点异常	//kill -8
  signal(SIGKILL, demo_signal_call_back);//memory crossover	//kill -9
  signal(SIGSEGV,demo_signal_call_back);	//无效的内存引用
  signal(SIGPIPE,SIG_IGN);	//管道破裂:比如没有读端口的管道
 
  //if (TcpServer.InitServer(5051)==false)
	if (TcpServer.InitServer(6969)==false)
   printf("服务端初始化失败,程序退出。\\n"); return -1; else printf("服务端初始化成功。\\n");
 
  while (1)
  
    if (TcpServer.Accept() == false) continue;
 
    if (fork()>0)  TcpServer.CloseClient(); continue;   // 父进程回到while,继续Accept。


 
    //signal(SIGINT,SIG_IGN);  //测试给子进程屏蔽ctrl+c会怎么样,父进程死后,孤儿进程还会在吗? //还在的,而且子进程用ctrl+c杀不死了




    // 子进程负责与客户端进行通信,直到客户端断开连接。
    TcpServer.CloseListen();
 
    printf("客户端已连接。\\n");
 
    // 与客户端通信,接收客户端发过来的报文后,回复ok。
    char strbuffer[1024];
 
    while (1)
    
      memset(strbuffer,0,sizeof(strbuffer));
      if (TcpServer.Recv(strbuffer,sizeof(strbuffer))<=0) break;
	printf("#################### receive start ####################\\n");
     

	//memset(client_ip, 0, sizeof(client_ip));
	//sprintf(client_ip, "%u.%u.%u.%u", NIPQUAD(client_address.sin_addr.s_addr));

	printf("接收:%s\\n",strbuffer);
      //printf("接收:[%s] %s\\n",inet_ntoa(client_address.sin_addr), strbuffer);
	KY_AI_RECV_DATA("%s\\n\\n", strbuffer);













	printf("#################### receive end ####################\\n\\n");
#if 0
      strcpy(strbuffer,"ok");
      if (TcpServer.Send(strbuffer,strlen(strbuffer))<=0) break;
      printf("发送:%s\\n",strbuffer);
#endif
    
 
    printf("客户端已断开连接。\\n");
 
    return 0;  // 或者exit(0),子进程退出。
  

 
CTcpServer::CTcpServer()	//在对象创建的时候会自动调用?

  // 构造函数初始化socket
  m_listenfd=m_clientfd=0;

 
CTcpServer::~CTcpServer()	//析构函数:在对象销毁的时候自动调用?

  if (m_listenfd!=0) 
  
    close(m_listenfd);  // 关闭m_listenfd
    printf("m_listenfd has been closed!\\n\\n");
    
  if (m_clientfd!=0)
  
    close(m_clientfd);  // 关闭m_clientfd
    printf("m_clientfd has been closed!\\n\\n");
    


 
// 初始化服务端的socket,port为通信端口
bool CTcpServer::InitServer(int port)

  if (m_listenfd!=0)  close(m_listenfd); m_listenfd=0; 
 
  m_listenfd = socket(AF_INET,SOCK_STREAM,0);  // 创建服务端的socket
 
  // 把服务端用于通信的地址和端口绑定到socket上
  struct sockaddr_in servaddr;    // 服务端地址信息的数据结构
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;  // 协议族,在socket编程中只能是AF_INET
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  // 本主机的任意ip地址
  servaddr.sin_port = htons(port);  // 绑定通信端口
  if (bind(m_listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
   close(m_listenfd); m_listenfd=0; printf("bind error\\n"); return false; 
 
  // 把socket设置为监听模式
  if (listen(m_listenfd,5) != 0 )  close(m_listenfd); m_listenfd=0; return false; 
 
  return true;

 
bool CTcpServer::Accept()

  //if ( (m_clientfd=accept(m_listenfd,0,0)) <= 0) return false;




	memset(&client_address,0,sizeof(client_address));
	client_len = sizeof(client_address);
  if ( (m_clientfd=accept(m_listenfd, (struct sockaddr*)&client_address, &client_len)) <= 0)
	
		printf("accept failed!\\n");
		return false;
	 		




	//printf("IP:[%s]\\n",inet_ntoa(client_address.sin_addr));
	memcpy(client_ip, inet_ntoa(client_address.sin_addr), strlen(inet_ntoa(client_address.sin_addr)));
	printf("IP:[%s]\\n",client_ip);



 
  	return true;

 
int CTcpServer::Send(const void *buf,const int buflen)

  return send(m_clientfd,buf,buflen,0);

 
int CTcpServer::Recv(void *buf,const int buflen)

  return recv(m_clientfd,buf,buflen,0);

 
void CTcpServer::CloseClient()    // 关闭客户端的socket

  if (m_clientfd!=0)  close(m_clientfd); m_clientfd=0; 

 
void CTcpServer::CloseListen()    // 关闭用于监听的socket

  if (m_listenfd!=0)  close(m_listenfd); m_listenfd=0; 


以上是关于linux C++ socket 如何获取客户端ip地址?(第一次accept无法获取客户端ip原因,第一次获取ip为0.0.0.0,需要赋初始size)的主要内容,如果未能解决你的问题,请参考以下文章

Linux之C++ socket通信编程

linux socket通讯如何获取本地的源端口号

手把手写C++服务器(38):面试必背!Linux网络socket编程必会十问!

事件上的 C++ 侦听器未使用套接字 io C++ 客户端获取事件

linux下C++ socket网络编程——即时通信系统(含源码)

如何获取 boost::asio::ip::tcp::socket 的 IP 地址?