diff options
| author | Andrew <saintruler@gmail.com> | 2021-04-27 21:41:44 +0400 |
|---|---|---|
| committer | Andrew <saintruler@gmail.com> | 2021-04-27 21:41:44 +0400 |
| commit | f3088f22e319b7183f9ad29d582574d3a06666ed (patch) | |
| tree | 89e2f076d83d50f050964c3e92ffd0093bac197f /client | |
| parent | 1fb7ca2bd298cbe340f7790cc40a808b1a4ab245 (diff) | |
Added GUI to client. renamed Message to Request. Added PING request type to sender server.
Diffstat (limited to 'client')
| -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 |
4 files changed, 311 insertions, 138 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 } |