这是我第一次构建一个接受c中多个客户端的服务器,我需要使用select来实现这一点。
我目前能够向服务器发送信息,但是当服务器执行回送时,信息最终会触发服务器端的select语句,而不是实际进入客户端。请帮助:(
客户端:
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd < 0) syserr("can't open socket");
printf("create socket...\n");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr = *((struct in_addr*)server->h_addr);
serv_addr.sin_port = htons(portno);
if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
syserr("can't connect to server");
printf("connect...\n");
// send the server our username
n = (int) send(sockfd, argv[3], strlen(argv[3]), 0) ;
if(n < 0) syserr("Can't send username to server\n");
printf("Username sent to server\n");
// wait for response, n ALWAYS RETURNS 0
n = (int) recv(sockfd, buffer, strlen(buffer), 0);
printf("buffer:\n%s\n",buffer) ;
if(n < 0) syserr("Can't receive from server\n");
else if (n == 0)
{
close(sockfd) ;
syserr("Server disconnected you due to duplicate username.\n") ;
}
else buffer[n] = '\0';
while ( done == 0 )
{
// rest of program loop, not being reached yet
}
close(sockfd);
return 0;
服务器:
portno = atoi(argv[1]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) syserr("can't open socket");
printf("create socket...\n");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
syserr("can't bind");
printf("bind socket to port %d...\n", portno);
listen(sockfd, 5);
FD_ZERO(&fds) ;
FD_SET(sockfd, &fds) ;
for(;;)
{
printf("wait on port %d...\n", portno);
addrlen = sizeof(clt_addr);
//newsockfd = accept(sockfd, (struct sockaddr*)&clt_addr, &addrlen);
//if(newsockfd < 0) syserr("can't accept");
readfds = fds ;
int result = select(FD_SETSIZE, &readfds, NULL, NULL, NULL) ;
// no timeout val, so never 0 return
if(result == -1)
syserr("Error on select call.\n") ;
int i ;
for( i = 0; i < FD_SETSIZE; i++ ) // loop through set of fds
{
if (FD_ISSET(i, &readfds)) // if fd i has data available to be read
{
if (i == sockfd) // if we are reading from server fd
{
if (numClients < maxClients) // if we could fit more clients
{
// place incoming client's fd into array of all clients
clients[numClients].clientfd = accept(sockfd, (struct sockaddr*)&clt_addr, &addrlen);
if(clients[numClients].clientfd < 0) syserr("Error on accept.\n");
FD_SET(clients[numClients].clientfd, &fds) ;
// receive the username sent by user
printf("new incoming connection, block on receive\n");
n = (int) recv(clients[numClients].clientfd, clients[numClients].username, 30, 0);
if(n < 0) syserr("can't receive from client");
clients[numClients].username[n] = '\0';
printf("New user accepted: %s\n", clients[numClients].username) ;
clients[numClients].online = 1 ;
// NOW I NEED TO SEND SOME INFO BACK
/// THIS IS THE PART THAT IS NOT SENDING BACK PROPERLY
/// IT IS ACTUALLY TRIGERRING THE SELECT ON THE SECOND ITERATION
n = (int) send(clients[numClients].clientfd, "Hey\nThis\nis\na\ntest", 18, 0) ;
if(n < 0) syserr("Can't send userlist to client.\n");
printf("n: %d\n", n) ; // My print returns the expected 18
printf("User List sent to client.\n") ; // This also prints
++numClients ; // extend client counter
}
else
{
printf("Sorry, we are full.\n") ;
}
}
else // not server file descriptor
{
/// On my second iteration This is triggered through the ‘select’ because of my send back above
// check if recv returns 0 for disconnect
n = (int) recv(clients[i].clientfd, buffer, sizeof(buffer), 0);
if(n < 0) syserr("can't receive from client"); // I actually get an error for bad fd
if( n == 0 ) // disconnected
{
// This code not reached yet
}
else // A message is being sent
{
// This code not reached yet
}
}
}
}
}
close(sockfd);
return 0;
答案 0 :(得分:0)
在服务器端,您的主要问题是,在您通过选择的第二次迭代中触发的else
中,i
应recv()
来自clients[i].clientfd
,而不是maxfd + 1
{1}}。它们不太可能是相同的数字,但我不能确定,因为你没有发布所有代码。 (请始终 http://www.sscce.org/)
还有其他问题。我会提到我立即注意到的那些
要选择的第一个参数应该是int maxfd = MYMAX(maxfd, new_fd_from_accept/listen)
,其中#define MYMAX(a,b) (((a)>(b))?(a):(b))
和maxfd
。将sockfd
初始化为man select
即可。循环到FD_SETSIZE是一种浪费。
我无法看到你是否正确初始化变量,所以请确保你这样做。如果您使用的是Linux,请阅读man select_tut
和recv()
。
在你刚刚接受的套接字上使用select()
,而不是从select()
指示这样做,似乎可以正常工作,因为你进行了阻塞读取,但我仍然建议将接受的fd放入fd_set并在{{1}}表示可以这样做时从中读取。
编辑:请参阅Serge Ballestas回答客户方问题。
答案 1 :(得分:0)
我无法看到你如何在客户端声明buffer
,但我可以看到你这样使用它:
n = (int) recv(sockfd, buffer, strlen(buffer), 0);
如果buffer
中的第一个字符为0,strlen(buffer)
为0
,n
将始终为0.
如果您有char buffer[...];
,请使用:
n = (int) recv(sockfd, buffer, sizeof(buffer), 0);
如果您在堆栈上使用动态分配:buffer = (char *) malloc(size);
,请执行:
n = (int) recv(sockfd, buffer, size, 0);