Reputation: 2787
I want to implement a pipe() kernel call for a single process, I don't have socketpair() either, but I can use non-blocking sockets. Here's what I have now:
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <arpa/inet.h>
int main(void)
{
// Wannabe server
struct sockaddr_in addr;
int fd;
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd == -1)
{
printf("Error opening socket\n");
return -1;
}
addr.sin_port = htons(0);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
if(bind(fd, (struct sockaddr *)&addr,sizeof(struct sockaddr_in) ) == -1)
{
printf("Error binding socket\n");
return -1;
}
if (fcntl(fd, F_SETFL, O_NONBLOCK))
{
printf("Unable to make socket non-blocking");
return -1;
}
socklen_t socklen = sizeof(addr);
getsockname(fd, &addr, &socklen);
const uint16_t port = ntohs(addr.sin_port);
printf("Successfully bound to port %u\n", ntohs(addr.sin_port));
// Accept the data packet from client and verification
struct sockaddr_in cli;
size_t cli_size = sizeof(cli);
accept(fd, &cli, &cli_size);
// Wannabe client
int sockfd;
struct sockaddr_in servaddr;
// socket create and varification
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
printf("socket creation failed...\n");
return -1;
}
else
printf("Socket successfully created..\n");
memset(&servaddr, 0, sizeof(servaddr));
// assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_port = addr.sin_port;
// connect the client socket to server socket
if (connect(sockfd, &servaddr, sizeof(servaddr)) != 0)
printf("connection with the server failed...\n");
else
printf("connected to the server..\n");
return 0;
}
The program says:
Successfully bound to port 44581
Socket successfully created..
connection with the server failed...
I must be wrong with the client socket, or something may be wrong with the accept call. The operating system is weird, but it's a FreeBSD fork, which does not support process calls at all. Any help will be appreciated!
Upvotes: 2
Views: 267
Reputation: 1537
This may help you make a little progress...I've managed the socket/listen/accept+socket/connect stuff in nonblocking mode. I've got data flowing in one direction (server to client) but apparently not client to server (I would expect FD 5 to become readable, but it apparently doesn't). More work needed.
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <errno.h>
#include <unistd.h>
int
main( int argc, char **argv )
{
// Wannabe server
struct sockaddr_in addr;
int fd;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf( stderr, "Error opening socket 1: %s\n", strerror( errno ) );
return errno;
}
addr.sin_port = htons(0);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
if (bind(fd, (struct sockaddr *)&addr,sizeof(struct sockaddr_in) ) == -1) {
fprintf( stderr, "Error binding socket 1: %s\n", strerror( errno ) );
return errno;
}
if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
fprintf( stderr, "Error setting NONBLOCK socket 1: %s\n", strerror( errno ) );
return errno;
}
if (listen( fd, 5 )) {
fprintf( stderr, "Error listening socket 1: %s\n", strerror( errno ) );
return errno;
}
socklen_t socklen = sizeof(addr);
getsockname(fd, (struct sockaddr *)&addr, &socklen);
const uint16_t port = ntohs(addr.sin_port);
printf( "Successfully bound to port %u\n", ntohs(addr.sin_port));
struct sockaddr_in cli;
socklen_t cli_size = sizeof(cli);
errno = 0;
int result;
int srv2cli = accept( fd, (struct sockaddr *)&cli, &cli_size );
printf( "Non-blocking accept() returns %d: %s\n", srv2cli, strerror( errno ) );
// Wannabe client
int sockfd;
struct sockaddr_in servaddr;
// socket create and varification
if ((sockfd = socket( AF_INET, SOCK_STREAM, 0 )) == -1) {
fprintf( stderr, "Error opening socket 2: %s\n", strerror( errno ) );
return errno;
}
memset(&servaddr, 0, sizeof(servaddr));
// assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_port = addr.sin_port;
if (fcntl( sockfd, F_SETFL, O_NONBLOCK)) {
fprintf( stderr, "Error setting NONBLOCK socket 2: %s\n", strerror( errno ) );
return errno;
}
errno = 0;
result = connect( sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr) );
printf( "Non-blocking connect() returns %d: %s\n", result, strerror( errno ) );
for ( ; ; ) {
fd_set readfds;
fd_set writefds;
fd_set exceptfds;
struct timeval tv;
int maxfd = fd;
if (maxfd < sockfd) maxfd = sockfd;
if (maxfd < srv2cli) maxfd = srv2cli;
FD_ZERO( &readfds );
FD_ZERO( &writefds );
FD_ZERO( &exceptfds );
FD_SET( fd, &readfds );
FD_SET( fd, &writefds );
FD_SET( fd, &exceptfds );
FD_SET( sockfd, &readfds );
FD_SET( sockfd, &writefds );
FD_SET( sockfd, &exceptfds );
if (srv2cli >= 0) {
FD_SET( srv2cli, &readfds );
FD_SET( srv2cli, &writefds );
FD_SET( srv2cli, &exceptfds );
}
tv.tv_sec = 5;
tv.tv_usec = 0;
errno = 0;
result = select( maxfd + 1, &readfds, &writefds, &exceptfds, &tv );
printf( "select returns %d : %s\n", result, strerror( errno ) );
if (FD_ISSET( fd, &readfds )) {
printf( "fd (%d) is ready to read (execute accept())\n", fd );
srv2cli = accept( fd, (struct sockaddr *)&cli, &cli_size );
printf( "\"Server\" accepted connection on fd %d (%s)\n", srv2cli, strerror( errno ) );
if (fcntl( srv2cli, F_SETFL, O_NONBLOCK)) {
fprintf( stderr, "Error setting NONBLOCK srv2cli socket : %s\n", strerror( errno ) );
return errno;
}
result = write( srv2cli, "Hello, world", 12 );
printf( "Transmitted %d bytes to client\n", result );
}
if (FD_ISSET( fd, &writefds )) printf( "fd (%d) is ready to write\n", fd );
if (FD_ISSET( fd, &exceptfds )) printf( "fd (%d) is ready to except\n", fd );
if (FD_ISSET( sockfd, &readfds )) {
char buf[32];
result = read( sockfd, buf, sizeof(buf) );
printf( "sockfd(%d) is ready to read...got %d bytes\n", sockfd, result );
}
if (FD_ISSET( sockfd, &writefds )) {
result = write( sockfd, "Foobar", 6 );
printf( "sockfd (%d) is ready to write...sent %d bytes\n", sockfd, result );
}
if (FD_ISSET( sockfd, &exceptfds )) printf( "sockfd (%d) is ready to except\n", sockfd );
if (FD_ISSET( srv2cli, &readfds )) printf( "srv2cli (%d) is ready to read\n", srv2cli );
if (FD_ISSET( srv2cli, &writefds )) printf( "srv2cli (%d) is ready to write\n", srv2cli );
if (FD_ISSET( srv2cli, &exceptfds )) printf( "srv2cli (%d) is ready to except\n", srv2cli );
}
return 0;
}
This produces:
dhm@rubidium-debian:~/code/nettest$ ./a.out 2>&1 | head -20
Successfully bound to port 60219
Non-blocking accept() returns -1: Resource temporarily unavailable
Non-blocking connect() returns -1: Operation now in progress
select returns 1 : Success
fd (3) is ready to read (execute accept())
"Server" accepted connection on fd 5 (Success)
Transmitted 12 bytes to client
select returns 2 : Success
sockfd(4) is ready to read...got 12 bytes
sockfd (4) is ready to write...sent 6 bytes
select returns 1 : Success
sockfd (4) is ready to write...sent 6 bytes
select returns 1 : Success
sockfd (4) is ready to write...sent 6 bytes
select returns 1 : Success
sockfd (4) is ready to write...sent 6 bytes
select returns 1 : Success
sockfd (4) is ready to write...sent 6 bytes
select returns 1 : Success
sockfd (4) is ready to write...sent 6 bytes
(there's no stop condition, so you need to kill it!)
Upvotes: 2
Reputation: 16540
The OPs posted code does not cleanly compile!
When compiling, always enable the warnings, then fix those warnings.
Here is the output from the compiler:
gcc -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled.c"
untitled.c: In function ‘main’:
untitled.c:38:21: warning: passing argument 2 of ‘getsockname’ from incompatible pointer type [-Wincompatible-pointer-types]
getsockname(fd, &addr, &socklen);
^
In file included from /usr/include/netinet/in.h:23:0,
from /usr/include/arpa/inet.h:22,
from untitled.c:5:
/usr/include/x86_64-linux-gnu/sys/socket.h:116:12: note: expected ‘struct sockaddr * restrict’ but argument is of type ‘struct sockaddr_in *’
extern int getsockname (int __fd, __SOCKADDR_ARG __addr,
^~~~~~~~~~~
untitled.c:47:16: warning: passing argument 2 of ‘accept’ from incompatible pointer type [-Wincompatible-pointer-types]
accept(fd, &cli, &cli_size);
^
In file included from /usr/include/netinet/in.h:23:0,
from /usr/include/arpa/inet.h:22,
from untitled.c:5:
/usr/include/x86_64-linux-gnu/sys/socket.h:232:12: note: expected ‘struct sockaddr * restrict’ but argument is of type ‘struct sockaddr_in *’
extern int accept (int __fd, __SOCKADDR_ARG __addr,
^~~~~~
untitled.c:47:22: warning: passing argument 3 of ‘accept’ from incompatible pointer type [-Wincompatible-pointer-types]
accept(fd, &cli, &cli_size);
^
In file included from /usr/include/netinet/in.h:23:0,
from /usr/include/arpa/inet.h:22,
from untitled.c:5:
/usr/include/x86_64-linux-gnu/sys/socket.h:232:12: note: expected ‘socklen_t * restrict {aka unsigned int * restrict}’ but argument is of type ‘size_t * {aka long unsigned int *}’
extern int accept (int __fd, __SOCKADDR_ARG __addr,
^~~~~~
untitled.c:71:25: warning: passing argument 2 of ‘connect’ from incompatible pointer type [-Wincompatible-pointer-types]
if (connect(sockfd, &servaddr, sizeof(servaddr)) != 0)
^
In file included from /usr/include/netinet/in.h:23:0,
from /usr/include/arpa/inet.h:22,
from untitled.c:5:
/usr/include/x86_64-linux-gnu/sys/socket.h:126:12: note: expected ‘const struct sockaddr *’ but argument is of type ‘struct sockaddr_in *’
extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);
^~~~~~~
untitled.c:40:20: warning: unused variable ‘port’ [-Wunused-variable]
const uint16_t port = ntohs(addr.sin_port);
^~~~
Compilation finished successfully.
While the last compiler message says "compilation finished successfully" The reality is the compiler output some 'work around' for each of the warnings. That does NOT mean the compiler produced the correct code.
Please fix all the warnings, then post an EDIT to your question
Suggest reading the MAN pages for connect()
, accept()
, and getsockname()
Upvotes: 0