Witam
Próbuję zrozumieć komunikację pomiędzy procesami za pomocą socketów w domenie AF_UNIX, czyli gdy nasz socket przyjmuje nazwę zaczynającą się od slasha (a nie IP::port jak w domenie AF_INET) i używa się read/write zamiast send/recv.
Poniżej przykład jaki sobie napisałem.
Rzeczy których nie rozumiem:
1.) Zanim robię write to po stronie klienta dałem sleepa 10[s] żeby zobaczyć co się stanie po stronie serwera - okazuje się że serwer czeka na funkcji read te 10 [s] aż klient wywoła write'a. Nie rozumiem czemu tak jest, skąd serwer wie że ma czekać? Spodziewałem się, że gdy nie będzie danych to read zwróci 0.
2.) Jak wywalę sleepa z pętli klienta to jest tak, że po stronie serwera otrzymuję z 1 reada wszystko co klient wysłał. Jak nie wyrzucę sleepa to nie wiem jak mam poraz drugi połączyć się z serwerem. Gdy wstawię w pętlę for klienta connect to zwraca się ERRNO że "Transport endpoint is already connected". Więc nie wiem jak tutaj nawiązać ponownie połączenie. Czy jedyne wyjście to zamknąć socket po stronie klienta, jeszcze raz go utworzyć (socket(AF_UNIX...)) i wywołać connect? Czy może nie trzeba tego wszystkiego robić na nowo?
const char* SERVER_PATH = "/tmp/mySocket";
void freeSockets(int sockfd, int sockfdClient=-1)
{
close(sockfd);
close(sockfdClient);
unlink(SERVER_PATH);
}
void sockStreamAfUnixExample()
{
pid_t pid = fork();
if (pid == 0) //Server
{
sockaddr_un serveraddr, clientaddr;
socklen_t len;
int sockfdClient = -1;
int sockfd = -1;
//create endpoint of communication, socket descriptor
sockfd = socket(AF_UNIX, SOCK_STREAM, 0); //AF_UNIX also know as AS_LOCAL), SOCK_DGRAM/SOCK_DGRAM
if (sockfd == -1)
{
perror("Cannot create server socket");
exit(EXIT_FAILURE);
}
//bind socket on unique name
unlink(SERVER_PATH); //sot that bind never fail
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
strncpy(serveraddr.sun_path, SERVER_PATH, sizeof(serveraddr.sun_path));
//serveraddr = {AF_UNIX, SERVER_PATH};
if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(sockaddr_un)) == -1)
{
perror("Server bind failed");
freeSockets(sockfd, sockfdClient);
exit(EXIT_FAILURE);
}
//The listen() function basically sets a flag in the internal socket structure marking the socket as a passive
//listening socket, one that you can call accept on. It opens the bound port so the socket can then start receiving connections
// from clients.
//The accept() function asks a listening socket to accept the next incoming connection and return a socket descriptor for
// that connection.
if (listen(sockfd, 128) == -1)//128 queue size (max number of client connections in pending state on acceptance)
{
perror("Server listen failed");
freeSockets(sockfd, sockfdClient);
exit(EXIT_FAILURE);
}
while (1)
{
cout << "Server ready to accept next connection" << endl;
//accept client connection and remove from queue
len = sizeof(sockaddr_un);
sockfdClient = accept(sockfd, (struct sockaddr *)&clientaddr, &len);
if (sockfdClient == -1)
{
perror("Cannot accept socket");
freeSockets(sockfd, sockfdClient);
exit(EXIT_FAILURE);
}
cout << "Connection accepted from client with addr " << clientaddr.sun_path << endl;
//Receive data from client
string buff(1000, '\0'); //goto cannot cross initialized variables
int bytes = read(sockfdClient, &buff[0], buff.size());
if (bytes == -1)
{
perror("recv failed");
freeSockets(sockfd, sockfdClient);
exit(EXIT_FAILURE);
}
cout << "Server receives " << bytes << " bytes: " << &buff[0] << endl;
}
freeSockets(sockfd, sockfdClient);
exit(0);
}
else //Client
{
sockaddr_un serveraddr;
int sockfd = -1;
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1)
{
perror("Cannot create client socket");
exit(EXIT_FAILURE);
}
this_thread::sleep_for(chrono::seconds(1));
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
strncpy(serveraddr.sun_path, SERVER_PATH, sizeof(serveraddr.sun_path));
if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(sockaddr_un)) == -1)
{
perror("Connect failed");
freeSockets(sockfd);
exit(0);
}
std::array<string, 10> table =
{
"Msg priority 0",
"Msg priority 1",
"Msg priority 2",
"Msg priority 3",
"Msg priority 4",
"Msg priority 5",
"Msg priority 6",
"Msg priority 7",
"Msg priority 8",
"Msg priority 9"
};
for (int i = 0; i<10; ++i) //the message might be delivered as one without boundaries if You remove sleep_for and will have a luck
{
this_thread::sleep_for(chrono::seconds(10)); //each time connect is required
int bytes = write(sockfd, table.at(i).c_str(), table.at(i).size());
if (bytes == -1)
{
perror("Client write error");
freeSockets(sockfd);
exit(0);
}
cout << "Client sent " << bytes << " bytes: " << table.at(i).c_str() << endl;
}
int status = 0;
wait(&status);
freeSockets(sockfd);
}
}