参考文章1:C++根据SOCKET获取套接字IP、Port等信息的代码
第一次获取的客户端的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; }
}