diff options
| -rw-r--r-- | client/client.go | 161 | ||||
| -rw-r--r-- | client/go.mod | 2 | ||||
| -rw-r--r-- | client/main.go | 267 | ||||
| -rw-r--r-- | client/utils.go | 19 | ||||
| -rw-r--r-- | server/main.go | 40 | ||||
| -rw-r--r-- | server/utils.go | 19 |
6 files changed, 350 insertions, 158 deletions
diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..033be8f --- /dev/null +++ b/client/client.go @@ -0,0 +1,161 @@ +package main + +import ( + "encoding/json" + "fmt" + "fyne.io/fyne/v2/data/binding" + "log" + "net" + "sync" +) + +var storage struct { + sync.RWMutex + messages []Request + stringData []string + data binding.ExternalStringList +} + +type UserData struct { + Username string +} + +type Client struct { + senderConn net.Listener + receiverConn net.Listener +} + +func readResponse(conn net.Conn) (Response, error) { + var response Response + var err error + + buf := make([]byte, 4096) + _, err = conn.Read(buf) + if err != nil { + return response, err + } + + size := fromBytes(buf[:4]) + rawResponse := buf[4 : size+4] + return parseResponse(rawResponse) +} + +func sender(conn net.Conn, wg *sync.WaitGroup, user UserData, c chan string) { + var err error + + authMsg, _ := json.Marshal(Request{ + Type: AUTHENTICATE, + Data: AUTHENTICATE, + User: user.Username, + }) + _ = sendMessage(conn, authMsg) + + for { + line := <-c + channelLock.Unlock() + + request, _ := json.Marshal(Request{ + Type: MESSAGE, + Data: line, + User: user.Username, + }) + err = sendMessage(conn, request) + if err != nil { + fmt.Printf("Error in sender: %s", err) + break + } + + response, _ := readResponse(conn) + _ = fmt.Sprintf("%s", response) + } + + exitMsg, _ := json.Marshal(Request{ + Type: EXIT, + Data: EXIT, + User: user.Username, + }) + _ = sendMessage(conn, exitMsg) + fmt.Println("Closed connection") + wg.Done() +} + +func receiver(conn net.Conn, wg *sync.WaitGroup, user UserData) { + var err error + + req := Request{ + Type: AUTHENTICATE, + Data: AUTHENTICATE, + User: user.Username, + } + err = sendMessage(conn, []byte(req.serialize())) + if err != nil { + fmt.Println(err) + return + } + + _, err = readResponse(conn) + if err != nil { + return + } + + for { + response, err := readResponse(conn) + if err != nil { + break + } + + if response.Status == PING { + req := Request{ + Type: PING, + Data: SUCCESS, + User: user.Username, + } + + _ = sendMessage(conn, []byte(req.serialize())) + continue + } + + message, err := parseRequest([]byte(response.Data)) + if err == nil { + storage.Lock() + //storage.messages = append(storage.messages, message) + _ = storage.data.Append(message.toString()) + storage.Unlock() + } else { + fmt.Println(err) + } + + req := Request{ + Type: MESSAGE, + Data: SUCCESS, + User: user.Username, + } + _ = sendMessage(conn, []byte(req.serialize())) + } + wg.Done() +} + +func runClient(user UserData, c chan string) { + var wg sync.WaitGroup + + senderConn, err := net.Dial("tcp", "localhost:8080") + handleError(err) + defer senderConn.Close() + wg.Add(1) + + receiverConn, err := net.Dial("tcp", "localhost:8081") + handleError(err) + defer receiverConn.Close() + wg.Add(1) + + go sender(senderConn, &wg, user, c) + go receiver(receiverConn, &wg, user) + + wg.Wait() +} + +func handleError(err error) { + if err != nil { + log.Fatal(err) + } +} diff --git a/client/go.mod b/client/go.mod index ffafd1f..168a27a 100644 --- a/client/go.mod +++ b/client/go.mod @@ -1,3 +1,5 @@ module vasthecat.ru/coursework-client go 1.16 + +require fyne.io/fyne/v2 v2.0.2 diff --git a/client/main.go b/client/main.go index f90a018..bf11f7a 100644 --- a/client/main.go +++ b/client/main.go @@ -1,156 +1,159 @@ package main import ( - "bufio" - "encoding/json" - "fmt" - "log" - "net" - "os" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/data/binding" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/layout" + "fyne.io/fyne/v2/widget" + "image/color" "sync" ) -var storage struct { - sync.Mutex - messages []Message -} +func chatLayout(window fyne.Window, user UserData, c chan string) { + window.SetTitle("Сообщения") -func parseBuffer(previous []byte, new []byte) ([]Response, []byte) { - buf := append(previous, new...) - bufSize := uint32(len(buf)) - var responses []Response - var idx uint32 = 0 - for { - if idx+4 >= bufSize { - break - } - size := fromBytes(buf[idx : idx+4]) - if size == 0 { - return responses, make([]byte, 0) - } - if idx+4+size >= bufSize { - break - } else { - resp, err := parseResponse(buf[idx+4 : idx+4+size]) - if err == nil { - responses = append(responses, resp) - } - idx += 4 + size - } - } - return responses, buf[idx:] + storage.Lock() + storage.data = binding.BindStringList(&storage.stringData) + storage.Unlock() + go runClient(user, c) + + list := widget.NewListWithData(storage.data, + func() fyne.CanvasObject { + return widget.NewLabel("template") + }, + func(i binding.DataItem, o fyne.CanvasObject) { + o.(*widget.Label).Bind(i.(binding.String)) + }) + + entry := widget.NewEntry() + submit := widget.NewButton("Отправить", func() { + channelLock.Lock() + c <- entry.Text + entry.SetText("") + + storage.RLock() + lastIdx := storage.data.Length() - 1 + list.Select(lastIdx) + list.Unselect(lastIdx) + storage.RUnlock() + }) + + window.SetContent(container.NewBorder( + nil, + container.NewBorder(nil, nil, nil, submit, entry), + nil, nil, + list, + )) } -func readResponse(conn net.Conn) (Response, error) { - var response Response - var err error +func loginLayout(window fyne.Window, c chan string) { + window.SetTitle("Войти в чат") - buf := make([]byte, 4096) - _, err = conn.Read(buf) - if err != nil { - return response, err - } + label := canvas.NewText("Chat", color.White) + label.TextSize = 40 - size := fromBytes(buf[:4]) - rawResponse := buf[4 : size+4] - return parseResponse(rawResponse) -} + loginLabel := widget.NewLabel("Логин: ") + loginEntry := widget.NewEntry() -func main() { - var wg sync.WaitGroup - conn, err := net.Dial("tcp", "localhost:8080") - handleError(err) - defer conn.Close() - wg.Add(1) - go func(conn net.Conn) { - authMsg, _ := json.Marshal(Message{ - Type: AUTHENTICATE, - Data: "auth", - User: "andrew", - }) - _ = sendMessage(conn, authMsg) - reader := bufio.NewReader(os.Stdin) - - for i := 0; i < 1000; i += 1 { - //fmt.Printf("Sending %d request\n", i) - line, _, _ := reader.ReadLine() - request, _ := json.Marshal(Message{ - Type: MESSAGE, - //Data: fmt.Sprintf("Hello, %d", i), - Data: string(line), - User: "andrew", - }) - err = sendMessage(conn, request) - if err != nil { - break + chooseKey := widget.NewButton("Выбрать ключ для подписи", func() { + dialog.ShowFileOpen(func(closer fyne.URIReadCloser, err error) { + if closer == nil { + return + } + bufSize := 4096 + data := make([]byte, 0) + buf := make([]byte, bufSize) + n := bufSize + + for n >= bufSize { + n, _ = closer.Read(buf) + data = append(data, buf...) } - response, _ := readResponse(conn) - _ = fmt.Sprintf("%s", response) - } + closer.Close() + }, window) + }) - exitMsg, _ := json.Marshal(Message{ - Type: MESSAGE, - Data: "exit", - User: "andrew", - }) - _ = sendMessage(conn, exitMsg) - fmt.Println("Closed connection") - wg.Done() - }(conn) - - receiver, err := net.Dial("tcp", "localhost:8081") - handleError(err) - defer receiver.Close() - wg.Add(1) - go func(conn net.Conn) { - req := Message{ - Type: AUTHENTICATE, - Data: AUTHENTICATE, - User: "andrew", - } - err = sendMessage(conn, []byte(req.serialize())) - if err != nil { - fmt.Println(err) - return + loginButton := widget.NewButton("Войти", func() { + var user = UserData{ + Username: loginEntry.Text, } + chatLayout(window, user, c) + }) + + back := widget.NewButton("Назад", func() { + startLayout(window, c) + }) + + mainContainer := container.NewCenter(container.NewVBox( + container.NewCenter(label), layout.NewSpacer(), + container.NewBorder(nil, nil, loginLabel, nil, loginEntry), + chooseKey, + loginButton, + back, + )) + window.SetContent(mainContainer) +} - _, err := readResponse(conn) - if err != nil { - return - } +func registerLayout(window fyne.Window, c chan string) { + window.SetTitle("Регистрация") - for { - response, err := readResponse(conn) - if err != nil { - break - } + label := canvas.NewText("Chat", color.White) + label.TextSize = 40 - message, err := parseMessage([]byte(response.Data)) - if err == nil { - storage.Lock() - storage.messages = append(storage.messages, message) - fmt.Printf("Got message: \"%s\"\n", message.Data) - storage.Unlock() - } else { - fmt.Println(err) - } + loginLabel := widget.NewLabel("Логин: ") + loginEntry := widget.NewEntry() - req := Message{ - Type: MESSAGE, - Data: SUCCESS, - User: "andrew", - } - _ = sendMessage(conn, []byte(req.serialize())) - } - wg.Done() - }(receiver) + generateKeys := widget.NewButton("Сгенерировать ключи подписи", func() { + + }) + + registerButton := widget.NewButton("Зарегистрироваться", func() { - wg.Wait() + }) + + back := widget.NewButton("Назад", func() { + startLayout(window, c) + }) + + window.SetContent(container.NewCenter(container.NewVBox( + container.NewCenter(label), layout.NewSpacer(), + container.NewBorder(nil, nil, loginLabel, nil, loginEntry), + generateKeys, + registerButton, + back, + ))) +} + +func startLayout(window fyne.Window, c chan string) { + window.SetTitle("Chat") + + label := canvas.NewText("Chat", color.White) + label.TextSize = 40 + login := widget.NewButton("Войти", func() { + loginLayout(window, c) + }) + signup := widget.NewButton("Зарегистрироваться", func() { + registerLayout(window, c) + }) + + mainContainer := container.NewCenter(container.NewVBox( + container.NewCenter(label), layout.NewSpacer(), login, signup)) + window.SetContent(mainContainer) } -func handleError(err error) { - if err != nil { - log.Fatal(err) - } +//var channel chan string +var channelLock sync.Mutex + +func main() { + myApp := app.New() + window := myApp.NewWindow("") + channel := make(chan string) + + startLayout(window, channel) + window.ShowAndRun() } diff --git a/client/utils.go b/client/utils.go index 4dc64b7..685a3a9 100644 --- a/client/utils.go +++ b/client/utils.go @@ -2,31 +2,38 @@ package main import ( "encoding/json" + "fmt" "net" ) const ( MESSAGE = "message" AUTHENTICATE = "authenticate" - RENAME = "rename" + EXIT = "exit" + REGISTER = "register" ) const ( SUCCESS = "success" FAILURE = "failure" + PING = "ping" ) -type Message struct { +type Request struct { Type string Data string User string } -func (msg *Message) serialize() string { - data, _ := json.Marshal(msg) +func (req *Request) serialize() string { + data, _ := json.Marshal(req) return string(data) } +func (req *Request) toString() string { + return fmt.Sprintf("%s: %s", req.User, req.Data) +} + type Response struct { Status string Data string @@ -62,8 +69,8 @@ func sendMessage(conn net.Conn, message []byte) error { return nil } -func parseMessage(data []byte) (Message, error) { - var m Message +func parseRequest(data []byte) (Request, error) { + var m Request err := json.Unmarshal(data, &m) return m, err } diff --git a/server/main.go b/server/main.go index 0a785bb..e7863e3 100644 --- a/server/main.go +++ b/server/main.go @@ -10,11 +10,11 @@ import ( "time" ) -func readRequest(conn net.Conn) (Message, error) { +func readRequest(conn net.Conn) (Request, error) { // 1 мегабайт var maxBodySize uint32 = 1048576 - var message Message + var message Request var err error buf := make([]byte, 4096) @@ -35,7 +35,7 @@ func readRequest(conn net.Conn) (Message, error) { } rawMessage := buf[4 : size+4] - return parseMessage(rawMessage) + return parseRequest(rawMessage) } func handleAuthentication(conn net.Conn) bool { @@ -93,7 +93,7 @@ func handleAuthentication(conn net.Conn) bool { return authenticated } -func handleConnection(conn net.Conn, c chan Message) { +func handleConnection(conn net.Conn, c chan Request) { defer conn.Close() authenticated := handleAuthentication(conn) @@ -107,7 +107,9 @@ func handleConnection(conn net.Conn, c chan Message) { channelLock.Lock() c <- message - if message.Data == "exit" { + fmt.Printf("Got new message from %s: %s\n", message.User, message.Data) + + if message.Type == EXIT { break } @@ -122,13 +124,13 @@ func handleConnection(conn net.Conn, c chan Message) { } } fmt.Println("Closed connection (receiver handler)") - received.RLock() - sz := len(received.data) - received.RUnlock() - fmt.Printf("There is %d messages in storage\n", sz) + //received.RLock() + //sz := len(received.data) + //received.RUnlock() + //fmt.Printf("There is %d messages in storage\n", sz) } -func reader(c chan Message) { +func reader(c chan Request) { //const maxMessages uint64 = 65536 //var count uint64 = 0 for { @@ -140,7 +142,7 @@ func reader(c chan Message) { received.Lock() //fmt.Println("Locked received") //if count >= maxMessages { - // received.data = []Message{data} + // received.data = []Request{data} // received.messageShift += maxMessages //} else { // received.data = append(received.data, data) @@ -164,7 +166,7 @@ func handleSender(conn net.Conn) { lastIndex := 0 for { - var message Message + var message Request idx := uint64(lastIndex) if idx < uint64(len(received.data)) { received.RLock() @@ -189,6 +191,16 @@ func handleSender(conn net.Conn) { } } } else { + resp, _ := json.Marshal(Response{ + Status: PING, + Data: PING, + }) + err := sendMessage(conn, resp) + if err != nil { + fmt.Println("Closed connection (sender handler)") + break + } + time.Sleep(100 * time.Millisecond) continue } @@ -197,7 +209,7 @@ func handleSender(conn net.Conn) { var received struct { sync.RWMutex - data []Message + data []Request } var channelLock sync.Mutex @@ -205,7 +217,7 @@ var channelLock sync.Mutex func main() { var wg sync.WaitGroup - c := make(chan Message) + c := make(chan Request) go reader(c) sock, err := net.Listen("tcp", "localhost:8080") diff --git a/server/utils.go b/server/utils.go index f566fd0..41c78ec 100644 --- a/server/utils.go +++ b/server/utils.go @@ -8,21 +8,23 @@ import ( const ( MESSAGE = "message" AUTHENTICATE = "authenticate" - RENAME = "rename" + EXIT = "exit" + REGISTER = "register" ) const ( SUCCESS = "success" FAILURE = "failure" + PING = "ping" ) -type Message struct { +type Request struct { Type string Data string User string } -func (msg *Message) serialize() string { +func (msg *Request) serialize() string { data, _ := json.Marshal(msg) return string(data) } @@ -32,6 +34,12 @@ type Response struct { Data string } +type Message struct { + User string + Data string + Timestamp uint32 +} + func toBytes(num uint32) []byte { buf := make([]byte, 4) var mask uint32 = 0x00FF @@ -62,9 +70,8 @@ func sendMessage(conn net.Conn, message []byte) error { return nil } -func parseMessage(data []byte) (Message, error) { - var m Message - // TODO(andrew): Добавить обработку ошибок +func parseRequest(data []byte) (Request, error) { + var m Request err := json.Unmarshal(data, &m) return m, err } |