Thread Linux Monitor: Client-Server Rollen (15 answers)
Opened by bloonix at 2006-05-25 16:52

betterworld
 2006-05-30 17:36
#38809 #38809
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
Ich habe mal die oben von mir angesprochene Sache implementiert, um Dateideskriptoren an andere Prozesse zu schicken. Dabei wird unter Linux "sendmsg" gebraucht. In perldoc -f send steht aber leider, dass sendmsg nicht implementiert ist. Daher musste ich zu Inline::C greifen.

Unnoetig zu erwaehnen, dass das Programm wohl nicht auf Windows laeuft. Keine Ahnung, ob es allgemein auf *ix laeuft.

Das Script forkt zwei Kinder. Der Elternprozess lauscht dann auf einem TCP-Port und leitet alle angenommenen Sockets an ein Kind weiter, welches dann mit dem Client kommuniziert.

Man kann es testen, indem man es mit "./dispatcher.pl" startet und dann in mehreren xterms oder Konsolen jeweiels "nc localhost 40001" tippt. Der dritte nc sollte gleich wieder zu Ende sein, weil es eben nur zwei Server-Prozesse gibt.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/perl
# dispatcher.pl
use strict;
use warnings;
use Inline 'C';
use IO::Socket::INET;
use IO::Socket::UNIX;
use Socket;
use IO::Select;


my $dispatcher_pid = $$;
my $debug_prefix = 'dispatcher';

sub debug ($) {
warn "[$debug_prefix] @_\n";
}

socketpair (my $child_end, my $parent_end, AF_UNIX, SOCK_DGRAM, PF_UNSPEC) or die $!;
bless $_, 'IO::Socket::UNIX' for $child_end, $parent_end;

my $listen = IO::Socket::INET->new(
Listen => 2,
Proto => 'tcp',
LocalPort => 40001,
ReuseAddr => 1,
) or die $!;

# Unschoene Methode, um nach unserem Tod sicherzustellen, dass keine Kinder ueberleben
END {
system('fuser', '-k', $listen->sockport . '/tcp') if defined $listen and $$ == $dispatcher_pid;
}

my @children;
my $available_children=0;

for my $child_id (0..1) {
my $pid = fork;
defined $pid or die $!;
unless ($pid) {
$debug_prefix = "child $child_id";
run_child($child_id);
exit;
}
$children[$child_id] = {
pid => $pid,
id => $child_id,
};
}
debug 'forked all children';

run_dispatcher();
exit;

sub run_dispatcher {
my $sel = IO::Select->new;
$sel->add($parent_end);
$sel->add($listen);
debug 'running dispatcher';
while (my @r = $sel->can_read()) {
INCOMING: for (@r) {
if ($_ == $listen) {
my $accept = $listen->accept;
debug 'incoming connection from ' . $accept->peerhost;
if ($available_children) {
debug "$available_children children are available. Dispatching connection";
my $ret = send_fd(fileno($parent_end), fileno($accept));
debug "send_fd returned $ret";
0 == $ret and --$available_children;
next INCOMING;
}
debug 'no-one available. Shutting down the connection.';
$accept->shutdown(SHUT_RDWR);
} elsif ($_ == $parent_end) {
defined $parent_end->recv(my $dgram, 20, 0) or die $!;
debug "got $dgram";
if ($dgram =~ /^\d+ available$/) {
++$available_children;
}
}
}
}
}

sub run_child {
my ($child_id) = @_;
debug "running child (pid $$)";
for (;;) {
$child_end->send("$child_id available") or die $!;
my $fd = get_fd(fileno($child_end));
die $! if -1 == $fd;
debug "get_fd returned $fd";
open my $client, "+<&$fd" or die $!;
serve_client($client);
}
}


sub serve_client {
my ($client) = @_;
local $_;
while (<$client>) {
chomp;
debug "client sent $_";
syswrite ($client, "I already know everything about $_.\n");
}
}


__END__
__C__
#include <sys/types.h>
#include <sys/socket.h>

int get_fd (int sock) {
struct cmsghdr *cmsg = alloca(CMSG_LEN(sizeof(int)));
struct msghdr hdr = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = NULL,
.msg_iovlen = 0,
.msg_control = cmsg,
.msg_controllen = CMSG_LEN(sizeof(int)),
.msg_flags = 0,
};
int ret;
int *fdptr;

ret = recvmsg (sock, &hdr, 0);
if (0 != ret)
return -1;

cmsg = CMSG_FIRSTHDR(&hdr);
if (NULL == cmsg)
return -1;

fdptr = (int*) CMSG_DATA(cmsg);

return *fdptr;
}

int send_fd (int sock, int fd) {
char buf [CMSG_SPACE(sizeof(fd))];
struct msghdr hdr = {
.msg_control = buf,
.msg_controllen = sizeof(buf),
};
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
int *fdptr;

cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
fdptr = (int*) CMSG_DATA(cmsg);
*fdptr = fd;

return sendmsg(sock, &hdr, 0);
}

(Edit: Habe einen Bug gefixt, der dafuer sorgen kann, dass der Dispatcher eine falsche Vorstellung davon hat, welche Kinder available sind.)\n\n

<!--EDIT|betterworld|1149006267-->

View full thread Linux Monitor: Client-Server Rollen