在新终端执行子进程
我想为unix创建一个简单的聊天应用程序。 我创建了一个支持多个客户端的服务器。当新的客户端连接到服务器时,使用fork命令创建一个新的进程。现在问题是所有子进程在服务器上共享同一个stdin,导致这个消息发送到第二个客户端第一个子客户端必须终止。为了解决这个问题,我想在新的终端中运行每个子进程。 这可以通过将子进程代码的代码写入一个新文件并像xterm -e sh -c一样执行(我还没有尝试过)。在新终端执行子进程
我真正想要的不是有两个文件只是为了启动一个新的终端并运行代码的其余部分。
int say(int socket)
{
char *s;
fscanf(stdin,"%79s",s);
int result=send(socket,s,strlen(s),0);
return result;
}
int main()
{
int listener_d;
struct sockaddr_in name;
listener_d=socket(PF_INET,SOCK_STREAM,0);
name.sin_family=PF_INET;
name.sin_port=(in_port_t)htons(30000);
name.sin_addr.s_addr=htonl(INADDR_ANY);
int c = bind(listener_d,(struct sockaddr *)&name,sizeof(name)); //Bind
if(c== -1)
{
printf("\nCan't bind to socket\n");
}
if(listen(listener_d,10) == -1) // Listen
{
printf("\nCan't listen\n");
}
puts("\nWait for connection\n");
while(1)
{
struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
int connect_d = accept(listener_d,
(struct sockaddr*)&client_addr,&address_size); //Accept
if(connect_d== -1)
{
printf("\nCan't open secondary socket\n");
}
if(!fork())
{
close(listener_d);
char *msg = "welcome Sweetone\n";
if(send(connect_d,msg,strlen(msg),0))
{
printf("send");
}
int k=0;
while(k<5)
{
say(connect_d);
++k;
}
close(connect_d);
exit(0);
}
close(connect_d);
}
close(listener_d);
return 0;
}
fscanf(stdin,"%79s",s);
为什么?是tcp聊天吗?你为每个客户都有一些套接字,如果同意要“说”某种东西,那么你必须使用客户端。这是真正的logick。
服务器通常只发送服务消息。这也是真正的logick。
但是,如果你想要新的终端,那么你可以尝试使用unistd.h中的exec系列。
嗨,我替换了if(!fork())块内的代码,并将它放在新的filw(也称为函数)中。当我使用系统(./ subserver listener_d connection_d)时,子程序并不像预期的那样工作。该客户不会收到我从服务器发送的任何消息。 – Mustafa 2013-02-20 05:23:34
如果我离开关闭(listener_d)它在哪里,并将其余的代码放在if(!fork())块中的其他文件服务器去beserk。我想它分叉了很多进程,因为在那之后我无法使用我的机器,因为它太忙了。 – Mustafa 2013-02-20 05:26:18
我试过类似system的命令(“xterm -e .../subServer arg1 arg2”); xterm成为我的主服务器的子进程,但不是我的子服务器。功能并不像预期的那样。 – Mustafa 2013-02-20 05:30:13
我认为您的客户端和服务器之间的消息发送有点不同寻常。在这个简单的“只测试它如何工作”的场景中,让客户端发送消息到服务器更为常见。作为一个例子,我可以提到一个简单的echo服务,它反映了客户端发送的所有内容,并返回给客户端。这种设计是否受到一些要求的限制?
除了批评,我有两个单独的更改,可以使您目前的设计工作。它们都涉及改变子服务器中输入的读数。
替代方案1: 而不是从标准输入读取,请创建一个命名管道(请参阅man 3 mkfifo
),fex/tmp/childpipe“pid_of_subserver_here”。您可以在say()
中创建管道并将其打开以供阅读。然后使用echo(man echo
)写入管道回声“我的消息”>/tmp/childpipe“NNNN”。在退出孩子之前,请记得使用unlink()
删除管道替代方法2: 在服务器和每个子服务器之间创建一个未命名的管道。这使得代码更加混乱,但避免了创建命名管道和使用echo。代码示例如下。它没有足够的错误处理(像大多数示例代码),并且不能正确处理断开连接的客户端。 (例如:nc localhost 30000)3)通过输入“1Hello client one”写入客户端1 4)(将第二个客户端连接到第二个客户端)第三个窗口等)4)通过键入“2Hello second client”写入第二个客户端
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
enum max_childeren{
MAX_CHILDEREN = 50
};
int say(int socket)
{
char buf[513] = {0};
fgets(buf, sizeof(buf), stdin);
int result=send(socket, buf, strlen(buf),0);
return result;
}
int main()
{
int listener_d;
struct sockaddr_in name;
listener_d=socket(PF_INET,SOCK_STREAM,0);
name.sin_family=PF_INET;
name.sin_port=(in_port_t)htons(30000);
name.sin_addr.s_addr=htonl(INADDR_ANY);
int on = 1;
if (setsockopt(listener_d, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){
perror("setsockopt()");
}
int c = bind(listener_d,(struct sockaddr *)&name,sizeof(name)); //Bind
if(c== -1)
{
printf("\nCan't bind to socket\n");
}
if(listen(listener_d,10) == -1) // Listen
{
printf("\nCan't listen\n");
}
// Edited here
int number_of_childeren = 0;
int pipes[2] = {0};
int child_pipe_write_ends[MAX_CHILDEREN] = {0};
fd_set select_fds;
FD_ZERO(&select_fds);
puts("\nWait for connection\n");
while(1)
{
struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
// Edited here, to multiplex IO
FD_SET(listener_d, &select_fds);
FD_SET(STDIN_FILENO, &select_fds);
int maxfd = listener_d + 1;
int create_new_child = 0;
int connect_d = -1; // moved here
select(maxfd, &select_fds, NULL, NULL, NULL);
if (FD_ISSET(listener_d, &select_fds)){
connect_d = accept(listener_d,
(struct sockaddr*)&client_addr,&address_size); //Accept
if(connect_d== -1)
{
printf("\nCan't open secondary socket\n");
exit(EXIT_FAILURE);
}
create_new_child = 1;
}
char buf[512] ={0};
char *endptr = NULL;
if (FD_ISSET(STDIN_FILENO, &select_fds)){
fgets(buf, sizeof(buf), stdin);
long int child_num = strtol(buf, &endptr, 10);
if (child_num > 0 && child_num <= number_of_childeren) {
write(child_pipe_write_ends[child_num - 1], endptr, strnlen(buf, sizeof(buf)) - (endptr - buf));
}
else {
printf("Skipping invalid input: %s\n", buf);
}
}
if (create_new_child != 1)
continue;
number_of_childeren++; // Edited here
int error = pipe(pipes);
if (error != 0){
//handle errors
perror("pipe():");
exit(EXIT_FAILURE);
}
child_pipe_write_ends[number_of_childeren - 1] = pipes[1];
if(!fork())
{
error = dup2(pipes[0], STDIN_FILENO);
if (error < 0){ // could also test != STDIN_FILENO but thats confusing
//handle errors
perror("dup2");
exit(EXIT_FAILURE);
}
close(pipes[0]);
close(listener_d);
char *msg = "welcome Sweetone\n";
if(send(connect_d,msg,strlen(msg),0))
{
printf("send\n");
}
int k=0;
while(k<5)
{
say(connect_d);
++k;
}
close(connect_d);
exit(0);
}
close(connect_d);
close(pipes[0]);
}
close(listener_d);
return 0;
}
该代码需要重构为函数。它太长了。我试图尽可能少地做出改变,所以我将重组留作练习。
感谢您的惊人答复。一旦我分析和理解它,肯定会从代码中学到很多东西。我刚刚读了一些关于套接字编程的内容,旨在创建我自己的终端库p2p聊天应用程序(Havent研究了如何制作一个std p2p聊天应用程序)。这就是为什么我想在客户端连接时打开一个新终端的原因,就像我为每个客户端都会有不同的窗口,我可以通过这些窗口与他们聊天。让我知道我还能做些什么来进一步达到预期的方向。谢谢。 – Mustafa 2013-02-20 17:01:58
由于许多在线资源青睐[Beejs guide](http://beej.us/guide/bgnet/output/html/multipage/index.html)。一本能够告诉你几乎所有需要的东西,然后是[UNIX网络编程](http://www.kohala.com/start/unpv12e.html)的书。一个经典的,不仅是网络编程,而是[APUE](http://www.apuebook.com/)和它现代的Linux仅等同于[Linux编程接口](http://man7.org/tlpi/)。首先,我认为Beejs Guide和阅读任何不熟悉的图书馆和系统调用的手册将会很好。 – thuovila 2013-02-20 17:44:20
从'stdin'读取时,是否应将该输入发送到所有连接的客户端?你有没有想过线程而不是进程? – 2013-02-15 10:05:55
是的这是一个示例程序。我肯定会转向线程。就stdin的读取而言,会发生什么情况是stdin的数据会转到第一个连接一次的客户端,只有当你输入任何内容到达客户端时才终止。 – Mustafa 2013-02-15 20:29:47