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

[Question]:How to close all connections and restart the gnet tcp server after certain time? #485

Closed
3 tasks done
semmars opened this issue Jul 19, 2023 · 12 comments
Closed
3 tasks done
Labels
help wanted Extra attention is needed inferior issue question Further information is requested waiting for response waiting for the response from commenter

Comments

@semmars
Copy link

semmars commented Jul 19, 2023

Actions I've taken before I'm here

  • I've thoroughly read the documentations about this problem but still have no answer.
  • I've searched the Github Issues/Discussions but didn't find any similar problems that have been solved.
  • I've searched the internet for this problem but didn't find anything helpful.

Questions with details

How to close all connections and restart the gnet tcp server after a certain period of time?
please make a full example with push example,
it seems gnet lack of good documentation
thank you very much

Code snippets (optional)

No response

@semmars semmars added help wanted Extra attention is needed question Further information is requested labels Jul 19, 2023
@panjf2000
Copy link
Owner

panjf2000 commented Jul 20, 2023

Call Engine.Stop to stop the server gracefully and you can start the server over.

I think you should look into the documentation more carefully before you open an issue and check off all the checkboxes you accuse it of badness.

@semmars
Copy link
Author

semmars commented Jul 21, 2023

Call Engine.Stop to stop the server gracefully and you can start the server over.

I think you should look into the documentation more carefully before you open an issue and check off all the checkboxes.

I have code below:

type pushServer struct {
	gnet.BuiltinEventEngine
	tick             time.Duration
	connectedSockets sync.Map
}

func main() {
	var port int =8080
	var multicore bool = true
	//set interval to 1 second
	var interval time.Duration = 10 * time.Second
	//var interval time.Duration = 
	var ticker bool = true


	push := &pushServer{tick: interval}
	
	//after 5 minutes stop the server
	time.AfterFunc(5*time.Minute, func() {
		log.Println("Server will shutdown in 5 seconds")
		time.Sleep(5 * time.Second)
		log.Println("Server is shutting down...")
		
		//how to stop the Engine then???????
		push.Engine.Close()
		gnet.Engine.Stop(push)
	})


	log.Fatal(gnet.Run(push, fmt.Sprintf("tcp://:%d", port), gnet.WithMulticore(multicore), gnet.WithTicker(ticker)))
}

@panjf2000
Copy link
Owner

You get the Engine from the OnBoot of EventHandler.

@panjf2000 panjf2000 added the waiting for response waiting for the response from commenter label Jul 21, 2023
@semmars
Copy link
Author

semmars commented Jul 21, 2023

You get the Engine from the OnBoot of EventHandler.

thanks for reply!
I got this code but it just quit the program , after Engine.Stop fire
basically I just want the gnet tcp server close all the connection and stop listening for 10 seconds and restart to listen new connection :
`func (ps *pushServer) OnBoot(eng gnet.Engine) (action gnet.Action) {
log.Println("Server is starting...")

//set timer for 10 seconds
time.AfterFunc(10*time.Second, func() {
	log.Println("Server will shutdown in 5 seconds")
	parentCtx := context.Background()

	deadline := time.Now().Add(4 * time.Second)
	ctx, cancel := context.WithDeadline(parentCtx, deadline)
	defer cancel()
	eng.Stop(ctx)
})

return gnet.None

}`

the full code thanks:

package main
import (
	//"flag"
	"context"
	"fmt"
	"log"
	"sync"
	"time"
	"github.com/panjf2000/gnet/v2"
)

func (ps *pushServer) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	log.Printf("Socket with addr: %s has been opened...\n", c.RemoteAddr().String())
	ps.connectedSockets.Store(c.RemoteAddr().String(), c)
	return
}

func (ps *pushServer) OnClose(c gnet.Conn, err error) (action gnet.Action) {
	log.Printf("Socket with addr: %s is closing...\n", c.RemoteAddr().String())
	ps.connectedSockets.Delete(c.RemoteAddr().String())
	return
}

func (ps *pushServer) OnTick() (delay time.Duration, action gnet.Action) {
	log.Println("It's time to push data to clients!!!")
	ps.connectedSockets.Range(func(key, value interface{}) bool {
		addr := key.(string)
		c := value.(gnet.Conn)
		c.AsyncWrite([]byte(fmt.Sprintf("heart beating to %s", addr)), nil)
		return true
	})
	delay = ps.tick
	return
}

func (ps *pushServer) OnTraffic(c gnet.Conn) gnet.Action {
	data, _ := c.Next(-1)
	c.Write(data)
	return gnet.None
}

func (ps *pushServer) OnBoot(eng gnet.Engine) (action gnet.Action) {
	log.Println("Server is starting...")

	//set timer for 10 seconds
	time.AfterFunc(10*time.Second, func() {
		log.Println("Server will shutdown in 5 seconds")
		parentCtx := context.Background()
		deadline := time.Now().Add(4 * time.Second)
		ctx, cancel := context.WithDeadline(parentCtx, deadline)
		defer cancel()
		eng.Stop(ctx)
	})

	return gnet.None

}

type pushServer struct {
	gnet.BuiltinEventEngine
	tick             time.Duration
	connectedSockets sync.Map
}

func main() {
	var port int = 8080
	var multicore bool = true
	var interval time.Duration = 10 * time.Second
	var ticker bool = true
	push := &pushServer{tick: interval}
	log.Fatal(gnet.Run(push, fmt.Sprintf("tcp://:%d", port), gnet.WithMulticore(multicore), gnet.WithTicker(ticker)))
}

@semmars
Copy link
Author

semmars commented Jul 21, 2023

I figure out I can put a loop in main to re Run gnet , since eng.Stop just quit the program, but I got this error if get error with if there any socket connected , if there is no any connected, no error,:

Server start failed: bind: address already in use
exit status 1

only have to wait around 30 seconds , the gnet can start again, any idea???
thank you very much

func main() {
	var port int = 8080
	var multicore bool = true
	var interval time.Duration = 100 * time.Second
	var ticker bool = false
	push := &pushServer{tick: interval}

	for {
		err := gnet.Run(push, fmt.Sprintf("tcp://:%d", port), gnet.WithMulticore(multicore), gnet.WithTicker(ticker))
		if err != nil {
			log.Fatalf("Server start failed: %v", err)
		}
		fmt.Println("Server is paused.")
		for i := 0; i < 5; i++ {
			fmt.Print(" ", i)
			time.Sleep(1 * time.Second)
                         // it get error with if there any socket connected:Server start failed: bind: address already in use
			//set the sleep time to 30 seconds would work
			//time.Sleep(30 * time.Second)
		}
	}
	}

@panjf2000
Copy link
Owner

bind: address already in use

This is caused by TIME_WAIT of TCP.

If you must restart your server immediately, there are two ways:

  1. Setting SO_LINGER to 0, this is usually a bad idea, the typical reason to set a SO_LINGER timeout of zero is to avoid large numbers of connections sitting in the TIME_WAIT state, taking up all the available resources on the server. Don't do it unless you're 100% sure what you're doing.
  2. Try Options.WithReuseAddr or Options.WithReusePort.

@semmars
Copy link
Author

semmars commented Jul 21, 2023

  1. SO_LINGER

option 2 with Options.WithReuseAddr or Options.WithReusePort works great!! thanks

SO how to set SO_LINGER in gnet way?

@panjf2000
Copy link
Owner

  1. SO_LINGER

option 2 with Options.WithReuseAddr or Options.WithReusePort works great!! thanks

SO how to set SO_LINGER in gnet way?

gnet now supports setting SO_LINGER on connections by calling Conn.SetLinger, there is no way to do that on the listener socket at present.

@semmars
Copy link
Author

semmars commented Jul 21, 2023

Conn.SetLinger

done! works like a charm !
Sometimes we can have 20k iot devices trying to connect to a single server like mad, so we have to test every possible way.
Thank you very much

func (ps *pushServer) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	fmt.Printf("Socket with addr: %s has been opened...\n", c.RemoteAddr().String())
	ps.connectedSockets.Store(c.RemoteAddr().String(), c)
	c.SetLinger(0)
	return
}

@panjf2000
Copy link
Owner

panjf2000 commented Jul 21, 2023

I think you misunderstood my previous comments about SO_LINGER, I only brought it up because you wanted to rebind the network address forcibly, to do that you can set SO_LINGER to zero on the listener socket, but as I said, gnet doesn't support setting SO_LINGER on the listener socket, it only supports setting it on the connection socket, which has nothing to do with your requirement about restarting server immediately. Therefore, it's meaningless to call SetLinger on every Conn, you should undo the code and just use Options.WithReuseAddr or Options.WithReusePort.

@semmars
Copy link
Author

semmars commented Jul 21, 2023

  1. SO_LINGER
func (ps *pushServer) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	fmt.Printf("Socket with addr: %s has been opened...\n", c.RemoteAddr().String())
	ps.connectedSockets.Store(c.RemoteAddr().String(), c)
	c.SetLinger(0)
	return
}

works fine, any good place I can set c.SetLinger(0)?

Options.WithReuseAddr or Options.WithReusePort. version works also fine!!!

@semmars
Copy link
Author

semmars commented Jul 22, 2023

  1. SO_LINGER
func (ps *pushServer) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	fmt.Printf("Socket with addr: %s has been opened...\n", c.RemoteAddr().String())
	ps.connectedSockets.Store(c.RemoteAddr().String(), c)
	c.SetLinger(0)
	return
}

works fine, any good place I can set c.SetLinger(0)?

Options.WithReuseAddr or Options.WithReusePort. version works also fine!!!

I told you a fancy problem: After we applied c.SetLinger(0) to each new connection and repeatedly opened, closed, and restarted the gnet framework over port 8080 for many hours, we encountered a peculiar issue. Gradually, the performance on port 8080 deteriorated, allowing only 4 or 5 connections to be opened at a time. In contrast, the other port, 8081, remained unaffected and could handle thousands of connections without any problems. We attempted to switch to a different TCP framework, but the issue persisted. We concluded that the problem was not with gnet but likely related to the saturation of some TCP buffers or other operating system factors. However, after rebooting the server, everything returned to normal once again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed inferior issue question Further information is requested waiting for response waiting for the response from commenter
Projects
None yet
Development

No branches or pull requests

2 participants