這幾天在搞賽靈思公司的ZYNQ-7000芯片,使用了ALINX的開發板,想在此平臺上跑petalinux,在此基礎上進行linux開發。以前都是使用QT之類的封裝庫,在windows上開發,首次在linux環境下開發,然後想使用select來實現多路複用,不用每次接收socke數據就阻塞。 在此過程中發現select函數時靈時不靈。現象爲如果客戶端的發送週期小於我的select的timeout時間時可以接收到數據,但是timeout時間小於客戶端的發送週期時,服務端程序就無法正確接收數據。
百度,bing上面查詢問題,沒有找到原因。後來在https://blog.csdn.net/FeeLang/article/details/4983317 文章裏發現了我的程序問題。就是select之前沒有FD_ZERO重新初始化fd_set結構體的值。但是原來沒有每次初始化也能接收到數據是什麼原因不知道。
示例代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <fcntl.h>
#include <time.h>
static fd_set s_udpFd;
/**
* initilize global variables
*/
void init_socket_var()
{
FD_ZERO(&s_udpFd);
}
int createSocket(in_addr_t addr,int port,struct sockaddr_in* sockAddr)
{
if( sockAddr == NULL)
{
return -1;
}
int sockFd =socket(AF_INET,SOCK_DGRAM,0);
if(sockFd < 0)
{
perror("Failed to create socket.");
return -1 ;
}
memset(sockAddr, 0, sizeof(struct sockaddr_in));
// Filling server information
sockAddr->sin_family = AF_INET; // IPv4
sockAddr->sin_addr.s_addr = addr;
sockAddr->sin_port = htons(port);
return sockFd;
}
int createUdpServer(void)
{
struct sockaddr_in servaddr;
int sockFd =createSocket(INADDR_ANY,38282,&servaddr);
if(sockFd < 0)
{
printf("Failed to create server socket.");
return -1 ;
}
if (bind(sockFd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1)
{
perror("Bind failed.");
close(sockFd);
sockFd = -1;
return -1;
}
return sockFd;
}
void testSocket()
{
char *hello = "Hello from petalinux client";
struct sockaddr_in servaddr;
struct timeval timeout;
struct sockaddr clntAddr;
int result;
socklen_t len;
int maxFd;
int srvFd;
int rcvSize;
char recvbuf[1024];
fd_set exceptFds;
int cnt= 0;
int useSelect = 1;
printf("test udp send\n");
init_socket_var();
timeout.tv_sec = 1;
timeout.tv_usec = 0;
srvFd = createUdpServer();
if(srvFd < 0)
{
return;
}
printf("start setting mode\n");
if(useSelect ==1)
{
// set non block mode
int flags;
flags = fcntl(srvFd,F_GETFL,0);
if(flags<0)
{
perror("fcntl");
return;
}
printf("current flags:%d\n",flags);
flags |= O_NONBLOCK;
if( fcntl(srvFd,F_SETFL,flags) < 0)
{
perror("failed to set nonblock mode\n");
return;
}else{
printf("set nonblock mode success.\n");
}
}
maxFd = srvFd;
int sockFd = createSocket(inet_addr("10.30.130.182"),38080,&servaddr);
if(maxFd <= sockFd)
{
maxFd = sockFd +1;
}
if(sockFd < 0)
{
return;
}
while(1)
{
if( useSelect ==1){
// 需要每次都初始化fd_set的值,是否可以FD_CLR方式還沒有試驗。問題原因
FD_ZERO(&s_udpFd);
FD_SET(srvFd,&s_udpFd);
//timeout 應該不需要每次設置,原來的排錯代碼
timeout.tv_usec = 0;
timeout.tv_sec = 1;
result = select(maxFd,&s_udpFd,NULL,NULL,&timeout);
if(result < 0)
{
// 失敗就退出。
perror("select failed.");
break;
}
else if(result ==0)
{
continue;
}
cnt++;
if(cnt %10 == 1){
printf("data ready %d\n",result);
}
}
//for(int i = 0; i < 2; i++ )
{
if ( (useSelect==0) || FD_ISSET(srvFd,&s_udpFd) )
{
memset( recvbuf, 0, sizeof(char) * 1024 );
//rcvSize = recv( srvFd, recvbuf, 1024, 0);
len = sizeof(clntAddr);
rcvSize = recvfrom(srvFd,recvbuf, 1024, 0,&clntAddr,&len);
if ( rcvSize < 0 )
{
perror("recv failed.\n");
}
else
{
if(rcvSize > 0)
{
struct sockaddr_in* p =(struct sockaddr_in*)(&clntAddr);
printf("from %s recv %d bytes. <= %s\n",
inet_ntoa(p->sin_addr),rcvSize,recvbuf);
sendto(sockFd,(const char *)hello, strlen(hello),
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
}
}
}
}
}
close(srvFd);
close(sockFd);
}