这是关于多客户端套接字编程的问题。
当我在考虑如何制作单个客户端和服务器程序时
对于多个客户,我遇到了如何实现这一点。
但是,即使我正在搜索所有内容,仍然存在某种困惑。
但是我有很多全局变量不可以共享,所以我没有考虑使用线程。
由于我在客户端以及服务器程序中使用顺序recv()和send()
当它是单个客户端和服务器时,它确实可以很好地工作,但是
我不知道如何针对多语言更改它。
客户端是否也必须取消阻止?
select()的所有要求是什么?
我在服务器程序上做的多客户端操作
1)我使用SO_REUSEADDR将套接字选项设置为重用地址
2)并使用fctl()使用O_NONBLOCK将我的服务器设置为非阻塞模式。
3)并将timeout参数设为零。
并在上面之后正确使用FD_functions。
但是,当我从第二个客户端运行一个或多个客户端程序时,
客户端程序块,未被服务器接受。
我想原因是因为我把服务器程序的主要功能部分放在了
进入“recv为> 0”的情况。
例如我的服务器代码
(我使用temp并读为fd_set,在这种情况下读为master)
int main(void)
{
int conn_sock, listen_sock;
struct sockaddr_in s_addr, c_addr;
int rq, ack;
char path[100];
int pre, change, c;
int conn, page_num, x;
int c_len = sizeof(c_addr);
int fd;
int flags;
int opt = 1;
int nbytes;
fd_set read, temp;
if ((listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
perror("socket error!");
return 1;
}
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
s_addr.sin_port = htons(3500);
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1)
{
perror("Server-setsockopt() error ");
exit(1);
}
flags = fcntl(listen_sock, F_GETFL, 0);
fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK);
//fcntl(listen_sock, F_SETOWN, getpid());
bind(listen_sock, (struct sockaddr*) &s_addr, sizeof(s_addr));
listen(listen_sock, 8);
FD_ZERO(&read);
FD_ZERO(&temp);
FD_SET(listen_sock, &read);
while (1)
{
temp = read;
if (select(FD_SETSIZE, &temp, (fd_set *) 0, (fd_set *) 0,
(struct timeval *) 0) < 1)
{
perror("select error:");
exit(1);
}
for (fd = 0; fd < FD_SETSIZE; fd++)
{
//CHECK all file descriptors
if (FD_ISSET(fd, &temp))
{
if (fd == listen_sock)
{
conn_sock = accept(listen_sock, (struct sockaddr *) &c_addr, &c_len);
FD_SET(conn_sock, &read);
printf("new client got session: %d\n", conn_sock);
}
else
{
nbytes = recv(fd, &conn, 4, 0);
if (nbytes <= 0)
{
close(fd);
FD_CLR(fd, &read);
}
else
{
if (conn == Session_Rq)
{
ack = Session_Ack;
send(fd, &ack, sizeof(ack), 0);
root_setting();
c = 0;
while (1)
{
c++;
printf("in while loop\n");
recv(fd, &page_num, 4, 0);
if (c > 1)
{
change = compare_with_pre_page(pre, page_num);
if (change == 1)
{
page_stack[stack_count] = page_num;
stack_count++;
}
else
{
printf("same as before page\n");
}
} //end of if
else if (c == 1)
{
page_stack[stack_count] = page_num;
stack_count++;
}
printf("stack count:%d\n", stack_count);
printf("in page stack: <");
for (x = 0; x < stack_count; x++)
{
printf(" %d ", page_stack[x]);
}
printf(">\n");
rq_handler(fd);
if (logged_in == 1)
{
printf("You are logged in state now, user: %s\n",
curr_user.ID);
}
else
{
printf("not logged in.\n");
c = 0;
}
pre = page_num;
} //end of while
} //end of if
}
} //end of else
} //end of fd_isset
} //end of for loop
} //end of outermost while
}
如果需要进行代码说明:我将要使用此代码,
制作某种网页来为服务器实现“浏览器”。
我想让每个客户端都获得服务器的 session 以获取登录页面左右。
但是执行的结果是,正如我上面所说的。
这是为什么?
与非阻塞服务器程序一起使用以使用select()?
我这样说的原因是,在我对这个问题进行了很多考虑之后,
“select()”似乎仅适用于多客户端聊天程序...那么多
可以在诸如聊天室之类的“派生”或“线程化”客户端中挂接。
你怎么想?...
对于普通的多客户端程序,是否还可以选择select或使用合适的东西?
如果我错过了让多客户端程序正常运行的某些功能,
请给我一些有关您的知识或正确使用select的一些要求。
我之前不知道多客户端通信不是那么容易:)
我也考虑过使用epoll,但我认为我需要首先了解选择的技巧。
谢谢阅读。
最佳答案
除了您要从单客户端转到多客户端的事实外,目前还不清楚是什么在阻止您。
您确定您完全了解select
应该如何工作吗?该手册(在Linux上为man 2 select
)可能会有所帮助,因为它提供了一个简单的示例。您还可以检查Wikipedia。
要回答您的问题:
fork
或select
。两者并没有真正一起使用(或者我不知道怎么用:-))。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。丧ss.。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。使用轻量级线程的模型本质上是asynchronous programming(我是否提到过,它还取决于您所说的“异步”吗?),对于您似乎要做的事情可能有些过大(在C++中,一个很好的例子是Boost.Asio)。 您可能已经知道,与多个客户打交道时的主要问题是I/O操作(例如
read
)正在阻塞,何时有新客户或客户说了些什么就不通知我们。fork
的方法很简单:服务器套接字(接受连接的套接字)在主进程中,并且每次接受新客户端时,它都会派生一个全新的进程来监视此新客户端:此新进程将专注于它。由于每个客户端只有一个进程,因此我们不在乎I/O操作是否处于阻塞状态。select
方法使我们可以在同一过程中监视多个客户端:这是一个多路复用器,告诉我们何时在我们提供给它的套接字上发生什么事情。在服务器端的基本思想是,首先将服务器套接字放置在select的read_fds FD_SET上。每次select
返回时,您都需要对其进行特殊检查:如果服务器套接字在read_fds集中设置(使用FD_ISSET(...)),则意味着您有一个新的客户端正在连接:您可以在之后调用accept
您的服务器套接字来创建连接。然后,您必须将所有客户端套接字放入您提供给
select
的fd_set中,以监视其上的任何更改(例如,传入消息)。我不太确定您对
select
的不了解,这是为了进行充分的解释。但是长话短说,select
是进行单线程同步网络的一种干净整洁的方法,它可以绝对地同时管理多个客户端,而无需使用任何fork
或线程。但是请注意,如果您绝对要使用select
处理非阻塞套接字,则必须处理不会阻塞的额外错误情况(Wikipedia示例很好地说明了这一点,因为他们必须检查errno
是否为' t EWOULDBLOCK
)。但这是另一个故事。编辑:好的,再加上一些代码,更容易知道出了什么问题。
select
的第一个参数应为nfds + 1,即“三个集合中任何一个中编号最高的文件描述符加1”(参见手册),而不是FD_SETSIZE,它是FD_SET的最大大小。通常是拥有最后一个accept
的客户端套接字(或开头的服务器套接字)。 fd
设置为0(例如在Wikipedia示例中),但是由于0是stdin
,1 stdout
和2 stderr
,除非您要监视其中之一,否则可以将其直接设置为服务器套接字的fd(因为可能是在给定的套接字号始终增加的情况下,第一个受监视的套接字会不断增加,并进行迭代直到它等于“nfds”(当前最高的fd)。 select
之前,您应该清除(例如,使用FD_ZERO),然后使用要监视的所有套接字(即服务器套接字和所有客户端套接字)重新填充读取的fd_set。 。再一次,激发自己以Wikipedia为例。 关于c - 在C中使用 'select()'为多客户端进行套接字编程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20308048/