#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int port = 3008;
int listenfd;
extern void makelistener();
int main(int argc, char **argv)
{
makelistener();
int clientfd, nready;
socklen_t len;
struct sockaddr_in q;
int i;
// initialize allset and add listenfd to the
// set of file descriptors passed into select
fd_set allset;
fd_set rset;
int maxfd;
FD_ZERO(&allset);
FD_SET(listenfd, &allset); // set of file descriptors
maxfd = listenfd;
int ret;
while (1)
{
// make a copy of the set before we pass it into select
rset = allset;
/*select will wait until an exceptional event occurs when tv is NULL*/
nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
if (nready == 0) {
continue;
}
if (nready == -1) {
perror("select");
continue;
}
//FD_ISSET returns 1 when a new connection is attempted
if(FD_ISSET(listenfd, &rset)){
//printf("a new client is connecting\n");
len = sizeof(q); //accept connection of listenfd stream socket
if ((clientfd = accept(listenfd, (struct sockaddr *)&q, &len)) < 0) {
perror("accept");
exit(1);
}
FD_SET(clientfd, &allset);
if (clientfd > maxfd) {
maxfd = clientfd;
}
static char msg[] = "What is your name?\r\n";
write(clientfd, msg, sizeof msg - 1);
printf("connection from %s\n", inet_ntoa(q.sin_addr));
char buf[256];
ret = read(clientfd, buf, sizeof(buf));
buf[ret] = '\0';
printf("%s", buf);
}
}
}
void makelistener()
{
struct sockaddr_in r;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(1);
}
memset(&r, '\0', sizeof r);
r.sin_family = AF_INET;
r.sin_addr.s_addr = INADDR_ANY;
r.sin_port = htons(port);
if (bind(listenfd, (struct sockaddr *)&r, sizeof r)) {
perror("bind");
exit(1);
};
if (listen(listenfd, 5)) {
perror("listen");
exit(1);
}
}
上面的代码是针对服务器的,它就是这样做的
$ ./above.c
(does nothing but runs forever)
如何以客户身份连接:
$ nc 127.0.0.1 3000
What is your name?
(waiting for my input) so if I put bob, it would output it to the server
按预期工作。但是我希望它与多个客户端同时工作。
例如:
服务器
$ ./above.c
(does nothing but runs forever)
客户端1
$ nc 127.0.0.1 3000
What is your name?
客户2
$ nc 127.0.0.1 3000
What is your name? (Currently client2 wont show up until client1 is answered which is what I'm trying to fix)
如何使客户端可以同时运行而无需等待第一个客户端完成?为了解释一下代码,监听器只是绑定并监听连接。在while(1)里面是select和calls的地方。
答案 0 :(得分:0)
如何使客户端可以同时运行而无需等待第一个客户端完成?
注意哪些套接字select()
向您报告。您要求select()
监视多个套接字是否可读,但您只是检查侦听套接字是否可读,而不是检查客户端套接字。您需要跟踪已连接的客户端,以便在需要时对其进行枚举。
尝试这样的事情:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int port = 3008;
#define MAX_CLIENTS (FD_SETSIZE - 1)
int listenfd = -1;
extern void makelistener();
int main(int argc, char **argv)
{
int clientfd, nready;
socklen_t len;
struct sockaddr_in q;
int i, j, ret;
fd_set allset;
fd_set rset;
int clients[MAX_CLIENTS];
int num_clients = 0;
int maxfd;
char buf[256];
makelistener();
// initialize allset and add listenfd to the
// set of file descriptors passed into select
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
maxfd = listenfd;
while (1)
{
// make a copy of the set before we pass it into select
FD_COPY(&allset, &rset);
// select will wait until an exceptional event occurs when tv is NULL
nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
if (nready < 0) {
perror("select");
continue;
}
if (nready == 0) { // should never happen since no timeout was requested
continue;
}
//FD_ISSET returns 1 when a socket is readable
if (FD_ISSET(listenfd, &rset)) {
//printf("a new client is connecting\n");
len = sizeof(q); //accept connection of listenfd stream socket
if ((clientfd = accept(listenfd, (struct sockaddr *)&q, &len)) < 0) {
perror("accept");
exit(1);
}
printf("Client %d connected from %s\n", clientfd, inet_ntoa(q.sin_addr));
if (num_clients == MAX_CLIENTS) {
static char msg[] = "Max number of clients are already connected\r\n";
write(clientfd, msg, sizeof(msg)-1);
close(clientfd);
}
else {
static char msg[] = "What is your name?\r\n";
if (write(clientfd, msg, sizeof(msg)-1) < 0) {
close(clientfd);
}
else {
clients[num_clients++] = clientfd;
FD_SET(clientfd, &allset);
if (clientfd > maxfd) {
maxfd = clientfd;
}
}
}
}
for (i = 0; i < num_clients; ++i) {
clientfd = clients[i];
if (!FD_ISSET(clientfd, &rset)) {
continue;
}
ret = read(clientfd, buf, sizeof(buf));
if (ret <= 0) {
//printf("a client has disconnected\n");
close(clientfd);
FD_CLR(clientfd, &allset);
for (j = i + 1; j < num_clients; ++j) {
clients[j-1] = clients[j];
}
--num_clients;
if (clientfd == maxfd) {
maxfd = listenfd;
for (j = 0; j < num_clients; ++j) {
if (clients[j] > maxfd) {
maxfd = clients[j];
}
}
}
--i;
continue;
}
printf("Client %d: %.*s", clientfd, ret, buf);
}
}
return 0;
}
请注意poll()
或epoll()
通常比select()
更适合使用。
答案 1 :(得分:0)
如何使客户端可以同时运行而无需等待第一个客户端完成?
每次调用accept()
时,都会启动一个线程pthread_create()
来处理与客户端的实际通信。
注意:创建/销毁线程非常耗时,因此建议了解thread pools
以及如何使用它们。
使用线程时,没有调用select()
(也不是poll()
)而是调用accept()
时主函数被阻塞,当该函数返回时,传递相关的套接字到要处理的线程
有很多关于服务器应如何与stackoverflow.com上的多个客户端通信的示例代码