petalinux 創建UDP服務端時,select函數不起作用故障解決一例

    這幾天在搞賽靈思公司的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);

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章