仅使用套接字实现单进程管道
Implement a single-process pipe using only sockets
我也想实现一个 pipe() kernel call for a single process, I don't have socketpair(),但我可以使用非阻塞套接字。这是我现在拥有的:
#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;
}
程序说:
Successfully bound to port 44581
Socket successfully created..
connection with the server failed...
肯定是客户端socket有问题,或者accept调用有问题。操作系统很奇怪,但它是一个 FreeBSD 分支,根本不支持进程调用。任何帮助将不胜感激!
OP posted 代码编译不干净!
编译时,始终启用警告,然后修复这些警告。
这是编译器的输出:
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.
虽然最后一条编译器消息说 "compilation finished successfully" 实际上编译器会为每个警告输出一些 'work around'。这并不意味着编译器生成了正确的代码。
请修复所有警告,然后post编辑您的问题
建议阅读 connect()
、accept()
和 getsockname()
的手册页
这可能会帮助您取得一些进步...我已经在非阻塞模式下管理了 socket/listen/accept+socket/connect 内容。我有一个方向的数据流(服务器到客户端),但显然不是客户端到服务器(我希望 FD 5 变得可读,但显然不是)。需要更多工作。
#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;
}
这会产生:
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
(没有停止条件,需要kill掉!)
我也想实现一个 pipe() kernel call for a single process, I don't have socketpair(),但我可以使用非阻塞套接字。这是我现在拥有的:
#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;
}
程序说:
Successfully bound to port 44581
Socket successfully created..
connection with the server failed...
肯定是客户端socket有问题,或者accept调用有问题。操作系统很奇怪,但它是一个 FreeBSD 分支,根本不支持进程调用。任何帮助将不胜感激!
OP posted 代码编译不干净!
编译时,始终启用警告,然后修复这些警告。
这是编译器的输出:
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.
虽然最后一条编译器消息说 "compilation finished successfully" 实际上编译器会为每个警告输出一些 'work around'。这并不意味着编译器生成了正确的代码。
请修复所有警告,然后post编辑您的问题
建议阅读 connect()
、accept()
和 getsockname()
这可能会帮助您取得一些进步...我已经在非阻塞模式下管理了 socket/listen/accept+socket/connect 内容。我有一个方向的数据流(服务器到客户端),但显然不是客户端到服务器(我希望 FD 5 变得可读,但显然不是)。需要更多工作。
#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;
}
这会产生:
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
(没有停止条件,需要kill掉!)