我在Linux下遇到服务器套接字问题。由于某些原因我不知道服务器套接字消失了,我在select调用中遇到Bad file descriptor
错误,等待进入连接。当我在另一个线程中关闭不相关的套接字连接时,总会发生此问题。这种情况发生在2.6.36内核的嵌入式Linux上。
有谁知道为什么会这样?服务器套接字是否可以简单地消失导致Bad file descriptor
?
修改
另一个套接字代码实现了VNC服务器并在完全不同的线程中运行。其他代码中唯一特别的是使用setjmp/longjmp
,但这应该不是问题。
创建服务器套接字的代码如下:
int server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(1234);
const int optionval = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &optionval, sizeof(optionval));
if (bind(server_socket, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
perror("bind");
return 0;
}
if (listen(server_socket, 1) < 0) {
perror("listen");
return 0;
}
我等待使用以下代码进行连接:
static int WaitForConnection(int server_socket, struct timeval *timeout)
{
fd_set read_fds;
FD_ZERO(&read_fds);
int max_sd = server_socket;
FD_SET(server_socket, &read_fds);
// This select will result in 'EBADFD' in the error case.
// Even though the server socket was not closed with 'close'.
int res = select(max_sd + 1, &read_fds, NULL, NULL, timeout);
if (res > 0) {
struct sockaddr_in caddr;
socklen_t clen = sizeof(caddr);
return accept(server_socket, (struct sockaddr *) &caddr, &clen);
}
return -1;
}
修改 当问题出现时我目前只是重新启动服务器,但我不明白为什么服务器套接字ID应该突然变成无效的文件描述符:
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (server_socket, SOL_SOCKET, SO_ERROR, &error, &len );
if (retval < 0) {
close(server_socket);
goto server_start;
}
答案 0 :(得分:4)
套接字(文件描述符)通常会遇到与C
中的原始指针相同的管理问题。无论何时关闭套接字,都不要忘记将-1
分配给保留描述符值的变量:
close(socket);
socket = -1;
正如您对C
指针
free(buffer);
buffer = NULL;
如果你忘记这样做,你可以稍后关闭套接字两次,因为如果是指针,你会free()
内存两次。
另一个问题可能与人们通常忘记的事实有关:UNIX环境中的文件描述符从0
开始。如果代码中有某处
struct FooData {
int foo;
int socket;
...
}
// Either
FooData my_data_1 = {0};
// Or
FooData my_data_2;
memset(&my_data_2, 0, sizeof(my_data_2));
在这两种情况下,my_data_1
和my_data_2
都有一个有效的描述符(socket
)值。后来,负责释放FooData
结构的一些代码可能会盲目地close()
这个描述符,恰好是你服务器的监听套接字(0
)。
答案 1 :(得分:1)
您无法区分代码中的两个错误情况,两者都可能会失败select
或accept
。我的猜测是你只有一个时间,而选择返回0
。
retval
分支errno
和else
accept
的返回值errno
重置为0
答案 2 :(得分:0)
1-关闭你的插座:
public static void fillmyList(ListView myListView, string query)
{
myListView.Items.Clear();
myListView.Columns.Clear();
string server = "localhost";
string database = "mydatabaseName";
string uid = "myUser";
string password = "MyPassword";
string connectionString;
connectionString = "SERVER=" + server + ";" + "DATABASE=" + database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";";
connection = new MySqlConnection(connectionString);
try
{
//Open connection
if (OpenConnection() == true)
{
//Create Command
MySqlCommand cmd = new MySqlCommand(query, connection);
//Create a data reader and Execute the command
MySqlDataReader dataReader = cmd.ExecuteReader();
//Read the data and store them in the listview
if (dataReader.FieldCount > 0)
{
for (int i = 0; i < dataReader.FieldCount; i++)
{
if (i == 0)
{
myListView.Columns.Add(dataReader.GetName(0), 0, HorizontalAlignment.Left);
}
else
{
myListView.Columns.Add(dataReader.GetName(i).ToString().Replace("_", " "), 80, HorizontalAlignment.Left);
}
}
ListViewItem lv = new ListViewItem();
//
while (dataReader.Read())
{
lv = myListView.Items.Add(dataReader[dataReader.GetName(0)].ToString().Replace('_', ' '));
for (int h = 1; h < dataReader.FieldCount; h++)
{
lv.SubItems.Add(dataReader[dataReader.GetName(h)].ToString());
}
}
}
for (int i = 1; i < myListView.Columns.Count; i++)
myListView.Columns[i].Width = -2;
//close Data Reader
dataReader.Close();
//close Connection
CloseConnection();
}
}
catch (Exception)
{
// MessageBox.Show(ex.Message);
}
}
//open connection to database
private static bool OpenConnection()
{
try
{
connection.Open();
return true;
}
catch (MySqlException ex)
{
//When handling errors, you can your application's response based on the error number.
//The two most common error numbers when connecting are as follows:
//0: Cannot connect to server.
//1045: Invalid user name and/or password.
switch (ex.Number)
{
case 0:
MessageBox.Show("Cannot connect to server. Contact administrator");
break;
case 1045:
MessageBox.Show("Invalid username/password, please try again");
break;
}
return false;
}
}
//Close connection
private static bool CloseConnection()
{
try
{
connection.Close();
return true;
}
catch (MySqlException ex)
{
MessageBox.Show(ex.Message);
return false;
}
}
2-从select set中清除套接字文件描述符:
close(sockfd);
答案 3 :(得分:-3)
在Linux中创建连接并关闭后,您必须等待一段时间才能建立新连接。 与在Linux中一样,socket不会释放端口号。一关闭套接字。
或强>
你重用套接字,然后想要来坏文件描述符。