Skip to content

Commit

Permalink
add tcp server chapter
Browse files Browse the repository at this point in the history
  • Loading branch information
kehiy committed Jun 13, 2023
1 parent 91e13bb commit 23a9ef2
Showing 1 changed file with 131 additions and 0 deletions.
131 changes: 131 additions & 0 deletions content/chapter 5/5.5-tcp-server-begginer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
title: '5.5 سرور tcp مقدماتی'
slug: go-tcp-server-begginer
weight: 9005
---

ما میتوانیم در گو با استفاده از کتابخانه net یک سرور [tcp](https://fa.wikipedia.org/wiki/%D9%BE%D8%B1%D9%88%D8%AA%DA%A9%D9%84_%D9%87%D8%AF%D8%A7%DB%8C%D8%AA_%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D9%84) ایجاد کنیم
بعد از تکمیل شدن سرور با استفاده از دستور `telnet` به آن متصل میشویم

در قطعه کد زیر
با تایع `acceptLoop()` درخواست های اتصال را مپذیریم
و با تایع `readLoop()` پیام های اتصال را میخوانیم


```go
package main

import (
"fmt"
"log"
"net"
)

// ساختار هر پیام در سرور
type Message struct {
// ادرس ip ارسال کننده پیام
from string
// متن و محتوای پیام
payload []byte
}

// ساختار سرور
type Server struct {
// ادرس و یا پورت سرور
listenAddr string
// listener
ln net.Listener
// چنل پیام برای انتقال پیام های دریفاتی از اتصال ها بین گوروتین ها
msgch chan Message
}

// ایجاد یک سرور جدید
func newServer(listenAddr string) *Server {
return &Server{
listenAddr: listenAddr,
msgch: make(chan Message, 10),
}
}

// شروع سرور و دریافت اتصال های جدید
func (s *Server) start() error {
// شروع سرور
ln, err := net.Listen("tcp", s.listenAddr)
if err != nil {
return err
}
defer ln.Close()
// مقدار دهی listener
s.ln = ln
// با تابع acceptLoop اتصال های جدید به سرور را مدیریت میکنیم
// با استفاده از go هر اتصال را روی یک گوروتین مجزا مدیریت میکنیم
go s.acceptLoop()

return nil
}

func (s *Server) acceptLoop() {
for {
// اتصال های موجود را تایید میکنیم متغییر conn را با اتصال مورد نظر مقدار دهی میکنیم
conn, err := s.ln.Accept()
if err != nil {
fmt.Println("accept error:", err)
continue
}
// با استفاده از این تابع مقادیر ارسال شده توسط اتصال را به چنل message میدهیم
go s.readLoop(conn)
}
}

func (s *Server) readLoop(conn net.Conn) {
defer conn.Close()
buf := make([]byte, 2048)

for {
// پیام ارسال شده توسط هر اتصال را به متغییر buf میدهیم
n, err := conn.Read(buf)
if err != nil {
fmt.Println("read error:", err)
continue
}

s.msgch <- Message{
// ادرس ip ارسال کننده پیام از نوع net.IP
from: conn.RemoteAddr().String(),
// متن پیام
payload: buf[:n],
}

// بعد از دریافت هر پیام یک پیام به عنوان پاسخ ارسال میکنیم
conn.Write([]byte("your message recived!\n"))
}
}

func main() {
// ساخت سرور
server := newServer(":3000")

go func() {
// در ازای هر پیام مقادیر آن را چاپ میکنیم
for msg := range server.msgch {
fmt.Printf("recived new from connection(%s): %s\n", msg.from, msg.payload)
}
}()

// شروع سرور
log.Fatal(server.start())

}

```

بعد از پایان پیاده سازی سرور tcp
با دستور زیر سرور خود را اجرا میکنیم:
`go run main.go`

و با دستور زیر در یک ترمینال مجزا به سرور متصل میشویم:
`telnet localhost 3000`

حال با نوشتن پیام و ارسال آن در ترمینال `telnet` متن پیام و ادرس اتصال در ترمینال سرور قابل مشاهده است.

توجه داشته باشید که دستور ‍`telnet` در ویندوز نیاز به فعال سازی دارد.

0 comments on commit 23a9ef2

Please sign in to comment.