这是一个带有select()`的服务器实现,以及一个简单的客户端。它不是主应用程序,而是一个可能无法运行的示例。重要的是来自服务器的无限循环。
此处出现问题:客户端发送(正确)消息,从服务器接收(正确)答案,然后,当他发送另一条消息时,它收到错误:write():错误的文件描述符。
我在服务器上查了回来,他没有关闭它。我怀疑在select()或服务器架构中存在问题。
编辑: 如果相关,我会在127.0.0.1上测试我的应用程序。
服务器
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#define PORT 2728
extern int errno;
/* converting client address int char* */
char * conv_addr (struct sockaddr_in address)
{
static char str[25];
char port[7];
strcpy (str, inet_ntoa (address.sin_addr));
bzero (port, 7);
sprintf (port, ":%d", ntohs (address.sin_port));
strcat (str, port);
return (str);
}
int main ()
{
struct sockaddr_in server;
struct sockaddr_in from;
fd_set readfds; //clients which are ready to write
fd_set actfds; //active clients
struct timeval tv;
int sd, client;
int optval=1;
int fd;
int nfds; //max number of descriptors
int len;
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("[server] Error:socket().\n");
return errno;
}
/*SO_REUSEADDR option*/
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,&optval,sizeof(optval));
bzero (&server, sizeof (server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl (INADDR_ANY);
server.sin_port = htons (PORT);
if (bind (sd, (struct sockaddr *) &server, sizeof (struct sockaddr)) == -1)
{
perror ("[server] Error:bind().\n");
return errno;
}
if (listen (sd, 5) == -1)
{
perror ("[server] Error:listen().\n");
return errno;
}
FD_ZERO (&actfds);
FD_SET (sd, &actfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
nfds = sd;
printf ("[server] Waiting at port %d...\n", PORT);
fflush (stdout);
/* serving clients... */
while (1)
{
bcopy ((char *) &actfds, (char *) &readfds, sizeof (readfds));
if (select (nfds+1, &readfds, NULL, NULL, &tv) < 0)
{
perror ("[server] Error:select().\n");
return errno;
}
if (FD_ISSET (sd, &readfds))
{
/* preparing client structure */
len = sizeof (from);
bzero (&from, sizeof (from));
client = accept (sd, (struct sockaddr *) &from, &len);
if (client < 0)
{
perror ("[server] Error:accept()\n");
continue;
}
if (nfds < client) /* adjusting max value */
nfds = client;
FD_SET (client, &actfds);
printf("[server] Client with %d descriptor and %s address connected.\n",client, conv_addr (from));
fflush (stdout);
}
/* checking if any client is ready so 'speak' */
for (fd = 0; fd <= nfds; fd++)
{
if (fd != sd && FD_ISSET (fd, &readfds))
{
//receive message from client
//do something
//sent back result
}
}
/* for */
} /* while */
} /* main */
客户端
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>
extern int errno;
int port;
int main (int argc, char *argv[])
{
int sd;
struct sockaddr_in server;
char msg[100];
if (argc != 3)
{
printf ("[client] Sintax: %s <server_address> <port>\n", argv[0]);
return -1;
}
port = atoi (argv[2]);
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("[client] Error:socket().\n");
return errno;
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(argv[1]);
server.sin_port = htons (port);
if (connect (sd, (struct sockaddr *) &server,sizeof (struct sockaddr)) == -1)
{
perror ("[client]Error: connect().\n");
return errno;
}
while(1)/*meaning untill user doesn't close the client*/)
{
//here are the functions used to communicate with server. Same in server.
if (write (sd, msg, 100) <= 0)
{
perror ("[client]Error:write().\n");
return errno;
}
if (read (sd, msg, 100) < 0)
{
perror ("[client]Error:read().\n");
return errno;
}
}//while
close (sd);
}//main
答案 0 :(得分:1)
while(/*user doesn't close client*/)
{
//here are the functions used to communicate with server. Same in server.
if (write (sd, msg, 100) <= 0)
{
perror ("[client]Error:write().\n");
return errno;
}
if (read (sd, msg, 100) < 0)
{
perror ("[client]Error:read().\n");
return errno;
}
}
close (
如果在使用EBADF
的第二次写入中此循环失败,显然这不是真正的代码,并且显然闭合是在循环内的一行,如其缩进所示。
答案 1 :(得分:-1)
服务器和客户端的代码似乎都缺少设置'SO_KEEPALIVE'选项。 建议使用类似的东西:
int keepAlive = 1;
if( 0 > setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE,
&keepAlive, (socklen_t)sizeof(keepAlive)) )
{ // then, setsockopt failed
// process error
}
当前代码通过以下方式创建套接字:
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{ // then socket failed
// process error
}
建议使用:
if ((sd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{ // then socket failed
// process error
}