Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

syscall: syscall.Read from syscall.Socketpair lost piece of data #27603

Closed
changkun opened this issue Sep 10, 2018 · 2 comments
Closed

syscall: syscall.Read from syscall.Socketpair lost piece of data #27603

changkun opened this issue Sep 10, 2018 · 2 comments

Comments

@changkun
Copy link
Member

changkun commented Sep 10, 2018

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

1.11

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/changkun/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/changkun/dev/golang"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/Cellar/go/1.11/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.11/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/61/r39b4cjx2bggk1_p7pmpbrdw0000gn/T/go-build581634092=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

package main

import (
	"fmt"
	"syscall"
)

func writeAll(fd int, buf []byte) error {
	for len(buf) > 0 {
		n, err := syscall.Write(fd, buf)
		if n < 0 {
			return err
		}
		fmt.Printf("writeAll: %s\n", string(buf[:n]))
		buf = buf[n:]
	}
	return nil
}
func main() {

	fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
	if err != nil {
		fmt.Printf("create socketpair error: %v\n", err)
		return
	}

	// write message 1
	message1 := "hello,world"
	if err := writeAll(fds[0], []byte(message1)); err != nil {
		fmt.Printf("write all error: %v\n", err)
		return
	}

	// read with limited buffer
	buffer := make([]byte, 5)
	n, err := syscall.Read(fds[1], buffer)
	if err != nil {
		fmt.Printf("syscall read error: %v\n", err)
		return
	}
	fmt.Println("read: ", string(buffer[:n]))

	// write message 2
	message2 := "data."
	if err := writeAll(fds[0], []byte(message2)); err != nil {
		fmt.Printf("write all error: %v\n", err)
		return
	}

	// read with limited buffer
	n, err = syscall.Read(fds[1], buffer)
	if err != nil {
		fmt.Printf("syscall read error: %v\n", err)
		return
	}

	fmt.Println("read: ", string(buffer[:n]))

	// Output:
	//
	// writeAll: hello,world
	// read:  hello
	// writeAll: data.
	// read:  data.
}

What did you expect to see?

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>

static int __write_all(int fd, char* buffer, int length) {
    printf("__write_all: %s\n", buffer);
    while (length > 0) {
        int written = write(fd, buffer, length);
        if (written < 0)
            return -1;
        length -= written;
        buffer += written;
    }
    return length;
}

int main() {
    int sockets[2];
    char buffer[5] = {0};
    char *message1 = "hello,world\0";
    char *message2 = "data.\0";

    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) {
        printf("socket pair error\n");
        return 1;
    }

    // write message 1
    int n = __write_all(sockets[0], message1, 11);
    if (n == -1) {
        printf("write all fail\n");
        return 1;
    }

    // read with limited buffer
    n = read(sockets[1], buffer, 5);
    printf("read: %s, length: %d\n", buffer, n);

    // write message 2
    n = __write_all(sockets[0], message2, 5);
    if (n == -1) {
        printf("write all fail\n");
        return 1;
    }

    // read with limited buffer
    n = read(sockets[1], buffer, 5);
    printf("read: %s, length: %d\n", buffer, n);
    n = read(sockets[1], buffer, 5);
    printf("read: %s, length: %d\n", buffer, n);

    // Output:
    //
    // __write_all: hello,world
    // read: hello, length: 5
    // __write_all: data.
    // read: ,worl, length: 5
    // read: ddata, length: 5

    return 0;
}

No data missing.

What did you see instead?

In above Go example of output comment, data is missing from socket pair

PS

CGO program:

package main

/*
#include <stdio.h>
#include <unistd.h>
char buffer[5] = {0};
void read_call(int fd) {
	int n = read(fd, buffer, 5);
	printf("read: %s, length: %d\n", buffer, n);
}
*/
import "C"
import (
	"fmt"
	"syscall"
)

func writeAll(fd int, buf []byte) error {
	for len(buf) > 0 {
		n, err := syscall.Write(fd, buf)
		if n < 0 {
			return err
		}
		fmt.Printf("writeAll: %s\n", string(buf[:n]))
		buf = buf[n:]
	}
	return nil
}
func main() {

	fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
	if err != nil {
		fmt.Printf("create socketpair error: %v\n", err)
		return
	}

	// write message 1
	message1 := "hello,world"
	if err := writeAll(fds[0], []byte(message1)); err != nil {
		fmt.Printf("write all error: %v\n", err)
		return
	}

	// read with limited buffer
	C.read_call(C.int(fds[1]))

	// write message 2
	message2 := "data."
	if err := writeAll(fds[0], []byte(message2)); err != nil {
		fmt.Printf("write all error: %v\n", err)
		return
	}

	// read with limited buffer
	C.read_call(C.int(fds[1]))

	// Output:
	//
	// writeAll: hello,world
	// read: hello, length: 5
	// writeAll: data.
	// read: data., length: 5
}

The ,world is missing from output.

@lrita
Copy link

lrita commented Sep 11, 2018

In your case, you are using a datagram-oriented socket by syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0).

You should known the differece between SOCK_DGRAM and SOCK_STREAM:

The recvfrom() function shall return the length of the message written to the buffer pointed to by the buffer argument. For message-based sockets, such as SOCK_RAW, SOCK_DGRAM, and SOCK_SEQPACKET, the entire message shall be read in a single operation. If a message is too long to fit in the supplied buffer, and MSG_PEEK is not set in the flags argument, the excess bytes shall be discarded. For stream-based sockets, such as SOCK_STREAM, message boundaries shall be ignored. In this case, data shall be returned to the user as soon as it becomes available, and no data shall be discarded.

from https://linux.die.net/man/3/recvfrom

@changkun
Copy link
Member Author

@lrita Thanks!

@golang golang locked and limited conversation to collaborators Sep 11, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants