Golang UDP Multicast - go

Im doing some testing for receiving multicast UDP stream and save it to file using the below functions:
func Record2(udp string, filePath string) {
c1, err := net.ListenPacket("udp4", udp)
if err != nil {
return
}
defer c1.Close()
p1 := ipv4.NewPacketConn(c1)
en0, err := net.InterfaceByName("enp3s0")
addr, _ := net.ResolveUDPAddr("udp4", udp)
if err := p1.JoinGroup(en0, addr); err != nil {
}
Bee.DelFile(filePath)
fo, err := os.Create(filePath)
defer fo.Close()
if err != nil {
panic(err)
}
io.Copy(fo, p1)
}
func Record(udp string, filePath string) {
addr, err := net.ResolveUDPAddr("udp4", udp)
if err != nil {
return
}
Bee.DelFile(filePath)
fo, err := os.Create(filePath)
defer fo.Close()
if err != nil {
panic(err)
}
l, err := net.ListenMulticastUDP("udp4", nil, addr)
defer l.Close()
l.SetReadBuffer(maxDatagramSize)
io.Copy(fo, l)
}
go Record2("224.1.2.2:1234", "file1")
go Record2("224.1.2.1:1234", "file2")
go Record("224.1.2.2:1234", "file3")
Record("224.1.2.1:1234", "file4")
the problem is that udp stream have different content while all files saved got the same content of the first address
this is not occuring when using UDP addresses with different ports
im not sure if this is related to SO_REUSEADDR
how to fix that?

Related

Transfering file using tcp golang

I'm trying to make a music app that sends file through tcp protocol using go and microservice architecture. Now I'm creating a player service that should:
Get user token and get claims from it
Check is user exists using claims and user_service microservice
Get song from redis
Check is song exists using music_service
Read file by chunks and send it to client using tcp
Redis data looks like this:
{
"user_id": [{
"song_id": "<song_id>"
}]
}
But I faced with a small problem. My music files stored in a flac format and when I receive it on the client, my player doesn't play it. I don't really know what can be the problem. So here's my code:
SERVER
service_setup.go
//this function is called in main function
func setService() {
ln, err := net.Listen("tcp", config.TCPAddress)
if err != nil {
panic("couldn't start tcp server")
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
logger.ErrorLog(fmt.Sprintf("Error: couldn't accept connection. Details: %v", err))
return
}
service.DownloadSong(conn)
}
}
downloader_service.go
func DownloadSong(conn net.Conn) {
token, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
logger.ErrorLog(fmt.Sprintf("Error: couldn't get token. Details: %v", token))
conn.Close()
return
}
claims, err := jwt_funcs.DecodeJwt(token)
if err != nil {
conn.Close()
return
}
songs, err := redis_repo.Get(claims.Id)
if err != nil {
conn.Close()
return
}
for _, song := range songs {
download(song, conn)
}
}
func download(song models.SongsModel, conn net.Conn) {
filePath, err := filepath.Abs(fmt.Sprintf("./songs/%s.flac", song.SongId))
if err != nil {
logger.ErrorLog(fmt.Sprintf("Errror: couldn't create filepath. Details: %v", err))
conn.Close()
return
}
file, err := os.Open(filePath)
defer file.Close()
if err != nil {
logger.ErrorLog(fmt.Sprintf("Errror: couldn't open file. Details: %v", err))
conn.Close()
return
}
read(file, conn)
}
func read(file *os.File, conn net.Conn) {
reader := bufio.NewReader(file)
buf := make([]byte, 15)
defer conn.Close()
for {
_, err := reader.Read(buf)
if err != nil && err == io.EOF {
logger.InfoLog(fmt.Sprintf("Details: %v", err))
fmt.Println()
return
}
conn.Write(buf)
}
}
CLIENT
main.go
func main() {
conn, _ := net.Dial("tcp", "127.0.0.1:6060")
var glMessage []byte
text := "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzYzlhNmE1OWI3ZmQyNTQ2ZjA4ZWEyYSIsInVzZXJuYW1lIjoiMTIiLCJleHAiOjE2NzQyMTE5ODl9.aarSDhrFF1df3i2pIRyjNxTfSHKObqLU3kHJiPreredIhLNCzs7z7jMgRHQIcLaIvCOECN7bX0OaSvKdW7VKsQ\n"
fmt.Fprint(conn, text)
reader := bufio.NewReader(conn)
b := make([]byte, 15)
c := 0
for i, _ := reader.Read(b); int(i) != 0; i, _ = reader.Read(b) {
c += i
glMessage = append(glMessage, b...)
}
os.WriteFile("./test.flac", glMessage, 0644)
}
If you know what can be the problem, please tell me. I'd really appreciate it!
It looks like you're trying to send the music file over the network in 15 byte chunks, which is likely not enough to play the song on the client side.
You can try increasing the chunk size, for example, to 8192 bytes. To do this, replace buf := make([]byte, 15) with buf := make([]byte, 8192).
Also, it's better to write the received data directly to the file rather than storing it in memory. You can do this by creating a file and using os.Create to write the received data to it:
file, err := os.Create("./test.flac")
if err != nil {
fmt.Println("Error: couldn't create file")
return
}
defer file.Close()
for {
i, err := reader.Read(buf)
if err != nil && err == io.EOF {
break
}
file.Write(buf[:i])
}
I believe that this can solve the issue.

Golang: UDP packet forwarding to multiple ports

Need help or suggestions in terms of how to do packet forwarding correctly. I received UDP packets from another server and I want to write a function to send the same packets to different processes running on the same machine.
From Server --> Client(Main Process) --> Process 1, Process 2, Process 3
Example Code: For Sending to one service Running on different Ports.
package main
import (
"fmt"
"log"
"net"
)
var message = make([]byte, 1024)
func main() {
s, err := net.ResolveUDPAddr("udp4", "127.0.0.1:6001")
c, err := net.DialUDP("udp4", nil, s)
// c, err := net.ListenUDP("udp4", s)
if err != nil {
fmt.Println(err)
panic(err)
}
defer c.Close()
message := []byte("message")
for {
_, err = c.Write(message)
fmt.Println(">>> Request packet sent to: 127.0.0.1:6001")
if err != nil {
log.Println(err)
}
// for {
// b := make([]byte, 1024)
fmt.Println("waiting for data")
n, addr, err := c.ReadFromUDP(message)
if err != nil {
fmt.Println(err)
}
fmt.Println(addr)
fmt.Println(string(message[:n]))
SendtoAnotherPort(6002, message[:n])
}
}
// SendtoAnotherPort sends a packet to another port
func SendtoAnotherPort(port int, msg []byte) {
s, err := net.ResolveUDPAddr("udp4", "127.0.0.1:6002")
c, err := net.DialUDP("udp4", nil, s)
// c, err := net.ListenUDP("udp4", s)
if err != nil {
fmt.Println(err)
panic(err)
}
_, err = c.WriteTo(message, s)
if err != nil {
log.Println(err)
}
}
Could this be the correct way of doing it? Or Any other suggestions.

How to close client socket in udp

I have a simple UDP server all that I want is if the IP is equal to 1.1.1.1 for example the client socket will close its like a blacklist system
My code
func main() {
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Please provide a port number!")
return
}
PORT := ":" + arguments[1]
s, err := net.ResolveUDPAddr("udp4", PORT)
if err != nil {
fmt.Println(err)
return
}
connection, err := net.ListenUDP("udp4", s)
if err != nil {
fmt.Println(err)
return
}
defer connection.Close()
buffer := make([]byte, 1024)
for {
n, addr, err := connection.ReadFromUDP(buffer)
fmt.Print("-> ", string(buffer[0:n-1]))
data := []byte(strconv.Itoa(random(1, 1001)))
fmt.Printf("data: %s\n", string(data))
_, err = connection.WriteToUDP(data, addr)
if err != nil {
fmt.Println(err)
return
}
}
}

Write multiple time on TCP conn

I have a TCP server server :
l, err := net.Listen("tcp", "localhost:"+strconv.Itoa(tcpPort))
The server listens to incoming client requests as is :
for {
c, err := l.Accept()
if err != nil {
log.Fatal(err)
}
b := make([]byte, 1024)
c.Read(b)
fmt.Println(string(b)) // "Hello"
}
I have a client :
conn, err := net.Dial("tcp", address)
Now I I write once with conn.Write([]byte("Hello"))
The server catches Hello. But if I have these :
_, err := conn.Write([]byte("Hello"))
if err != nil {
log.Fatal(err)
}
_, err = conn.Write([]byte("World"))
if err != nil {
log.Fatal(err)
}
Then I will get Hello, but not World.
How can I write multiple time on the same connection ?
Full function below
func main() {
l, err := net.Listen("tcp", "localhost:1234")
if err != nil {
log.Fatal(err)
}
defer l.Close()
go func() {
for {
c, err := l.Accept()
if err != nil {
log.Fatal(err)
}
b := make([]byte, 1024)
c.Read(b)
fmt.Println(string(b))
}
}()
conn, err := net.Dial("tcp", "localhost:1234")
if err != nil {
log.Fatal(err)
}
_, err = conn.Write([]byte("hello"))
if err != nil {
log.Fatal(err)
}
_, err = conn.Write([]byte("world"))
if err != nil {
log.Fatal(err)
}
}
Your problem is in the server code, the one that receives data. It only reads once from the tcp stream. If you want it to read "world", replace the single read operation by a loop:
go func() {
for {
c, err := l.Accept()
if err != nil {
log.Fatal(err)
}
for {
b := make([]byte, 1024)
c.Read(b)
fmt.Println(string(b))
}
}
}()
Your problem is not with conn.Write but with reading from connection. Now you read just once from every new opened connection accepted by l.Accept(). Solution is to read repeatedly.
Your code is also limited to handle just one connection. And do not forget to check error from c.Read(b) to know when stop listen on this connection.
go func() {
for {
c, err := l.Accept()
if err != nil {
log.Fatal(err)
}
go func(conn net.Conn) {
for {
b := make([]byte, 1024)
_, err := conn.Read(b)
if err != nil {
if err != io.EOF {
fmt.Println("read error:", err)
}
break
}
fmt.Println(string(b))
}
fmt.Println("Stopping handle connection")
}(c)
}
}()

multicast loopbacks

I need to write multicast listener on Go. I faces the problem of twicing packets when I read it. It seems that I need to set IP_MULTICAST_LOOP to false. It is not easy to do in Go.
I found this post. It seems that it should work. But I still get copies of the same host. How should it be done?
ipAddr, err := net.ResolveUDPAddr("udp", groupAddress)
if err != nil {...}
iface, err := net.InterfaceByName("en0")
if err != nil {...}
conn, err := net.ListenPacket("udp4", groupAddress)
if err != nil {...}
pc := ipv4.NewPacketConn(conn)
if err := pc.JoinGroup(iface, ipAddr); err != nil {...}
if err := pc.SetMulticastLoopback(false); err != nil {...}
if loop, err := pc.MulticastLoopback(); err == nil {...}
buf := make([]byte, 1024)
for {
n, _, addr, err := pc.ReadFrom(buf)
if err != nil {...}
fmt.Printf("recv from %v: [%s] \n", addr, buf[:n])
}
The simplest way is to use the ListenMulticastUDP wrapper in the net package, as actually already explained in the other SO answer you point to, How to set IP_MULTICAST_LOOP on multicast UDPConn in Golang.
If you follow the implementation of ListenMulticastUDP(), you will see that at a certain point it calls setIPv4MulticastLoopback(fd, false).
If you need something more advanced, the documentation of ListenMulticastUDP() suggests to look at https://godoc.org/golang.org/x/net/ipv4 and https://godoc.org/golang.org/x/net/ipv6, which document extensively how to do multicast in Go.
Here is some minimal code (tested on MacOS, but platform-independent) that shows how to use ListenMulticastUDP():
func main() {
// MDNS (https://en.wikipedia.org/wiki/Multicast_DNS)
groupAddress := "224.0.0.251:5353"
ifaceName := "en0"
if err := run(groupAddress, ifaceName); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}
func run(groupAddr string, ifaceName string) error {
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return err
}
gaddr, err := net.ResolveUDPAddr("udp", groupAddr)
if err != nil {
return err
}
conn, err := net.ListenMulticastUDP("udp", iface, gaddr)
if err != nil {
return err
}
buf := make([]byte, 1024)
for {
n, addr, err := conn.ReadFromUDP(buf)
if err != nil {
return err
}
fmt.Printf("recv %4d bytes from %v\n", n, addr)
}
}

Resources