├── .gitignore ├── README.md ├── app.go ├── demo.png ├── go.mod └── netassistant /.gitignore: -------------------------------------------------------------------------------- 1 | baloneo.netassistant 2 | go.sum 3 | .vscode/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Network Assistant 2 | 3 | Linux Network Assistant 4 | 5 | A network packet delivery/debug tool on the Linux desktop 6 | 7 | - [x] TCP Client 8 | - [x] TCP Server 9 | - [x] UDP Client 10 | - [x] UDP Server 11 | 12 | ## Run 13 | Download `netassistant` from releases. 14 | 15 | ### Build 16 | ``` 17 | go get 18 | go build . 19 | ``` 20 | 21 | ![APP](./demo.png) 22 | -------------------------------------------------------------------------------- /app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "encoding/hex" 7 | "fmt" 8 | "net" 9 | "os" 10 | "strconv" 11 | "strings" 12 | "time" 13 | 14 | "github.com/gotk3/gotk3/gdk" 15 | "github.com/gotk3/gotk3/glib" 16 | "github.com/gotk3/gotk3/gtk" 17 | "github.com/op/go-logging" 18 | ) 19 | 20 | const ( 21 | icon = `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` 22 | IT_END string = "end" 23 | IT_APP_NAME string = "Network Assistant" 24 | IT_SETTING string = "Settings" 25 | IT_TYPE string = "Type" 26 | IT_TCP_CLIENT string = "TCP Client" 27 | IT_TCP_SERVER string = "TCP Server" 28 | IT_UDP_CLIENT string = "UDP Client" 29 | IT_UDP_SERVER string = "UDP Server" 30 | IT_PORT string = "Port" 31 | IT_CONNECT string = "Connect" 32 | IT_RECV_SETTINGS string = "Recv Settings" 33 | IT_SEND_SETTINGS string = "Send Settings" 34 | IT_SAVE_TO_FILE string = "Save to file" 35 | IT_SHOW_RECV_TIME string = "Show recv time" 36 | IT_SHOW_HEX string = "Show hex" 37 | IT_PAUSE string = "Pause" 38 | IT_SAVE string = "Save" 39 | IT_CLEAR string = "Clear" 40 | IT_SEND string = "Send" 41 | IT_APPEND_RN string = "Append \\r\\n" 42 | IT_AUTO_CLEAR string = "Auto clear" 43 | IT_SEND_HEX string = "Send HEX" 44 | IT_SEND_CIRC string = "Send circularly" 45 | IT_LOAD_DATA string = "Load data" 46 | IT_DATA_RECVED string = "Data received" 47 | IT_WAIT_CONN string = "Waiting connection" 48 | IT_SEND_COUNT string = "Send count:" 49 | IT_RECEVER_COUNT string = "Recv count:" 50 | IT_RESET string = "Reset" 51 | IT_NO_CONN string = "there's no connection" 52 | IT_DISCONNECT string = "Disconnect" 53 | IT_LOCAL_IP string = "Local ip" 54 | IT_LOCAL_PORT string = "Local port" 55 | IT_STOP string = "Stop" 56 | ) 57 | 58 | var ( 59 | log = logging.MustGetLogger("APP") 60 | i18nTextMap = map[string]string{ 61 | IT_END: "结束", 62 | IT_APP_NAME: "网络调试助手", 63 | IT_SETTING: "设置", 64 | IT_TYPE: "类型", 65 | IT_TCP_CLIENT: "TCP客户端", 66 | IT_TCP_SERVER: "TCP服务端", 67 | IT_UDP_CLIENT: "UDP客户端", 68 | IT_UDP_SERVER: "UDP服务端", 69 | IT_PORT: "端口", 70 | IT_CONNECT: "连接", 71 | IT_RECV_SETTINGS: "接收设置", 72 | IT_SEND_SETTINGS: "发送设置", 73 | IT_SAVE_TO_FILE: "保存到文件", 74 | IT_SHOW_RECV_TIME: "显示接收时间", 75 | IT_SHOW_HEX: "16进制显示", 76 | IT_PAUSE: "暂停", 77 | IT_SAVE: "保存", 78 | IT_CLEAR: "清除", 79 | IT_SEND: "发送", 80 | IT_APPEND_RN: "追加\\r\\n", 81 | IT_AUTO_CLEAR: "自动清除", 82 | IT_SEND_HEX: "发送16进制", 83 | IT_SEND_CIRC: "循环发送", 84 | IT_LOAD_DATA: "加载数据", 85 | IT_DATA_RECVED: "已接收数据", 86 | IT_WAIT_CONN: "等待连接", 87 | IT_SEND_COUNT: "发送数:", 88 | IT_RECEVER_COUNT: "接收数:", 89 | IT_RESET: "重置", 90 | IT_NO_CONN: "没有连接", 91 | IT_DISCONNECT: "断开连接", 92 | IT_LOCAL_IP: "本地IP", 93 | IT_LOCAL_PORT: "本地端口", 94 | IT_STOP: "停止", 95 | } 96 | systemLangIsZh = strings.HasPrefix(os.Getenv("LANG"), "zh_") 97 | ) 98 | 99 | func getI18nText(key string) string { 100 | if systemLangIsZh { 101 | if v, exist := i18nTextMap[key]; exist { 102 | return v 103 | } 104 | } 105 | return key 106 | 107 | } 108 | 109 | // NetAssistantApp Main 110 | type NetAssistantApp struct { 111 | receCount int 112 | sendCount int 113 | 114 | chanClose chan bool 115 | listener net.Listener 116 | connList []net.Conn 117 | fileName string 118 | 119 | appWindow *gtk.ApplicationWindow 120 | combProtoType *gtk.ComboBoxText 121 | entryIP *gtk.Entry 122 | entryPort *gtk.Entry 123 | btnConnect *gtk.Button 124 | btnClearRecvDisplay *gtk.Button 125 | btnClearSendDisplay *gtk.Button 126 | labelStatus *gtk.Label 127 | labelSendCount *gtk.Label 128 | labelReceveCount *gtk.Label 129 | btnCleanCount *gtk.Button 130 | tvDataReceive *gtk.TextView 131 | swDataRec *gtk.ScrolledWindow 132 | tvDataSend *gtk.TextView 133 | btnSend *gtk.Button 134 | entryCurAddr *gtk.Entry 135 | entryCurPort *gtk.Entry 136 | cbHexDisplay *gtk.CheckButton 137 | cbPauseDisplay *gtk.CheckButton 138 | cbDisplayDate *gtk.CheckButton 139 | cbDataSourceCycleSend *gtk.CheckButton 140 | cbSendByHex *gtk.CheckButton 141 | tbReceData *gtk.TextBuffer 142 | tbSendData *gtk.TextBuffer 143 | entryCycleTime *gtk.Entry 144 | cbAutoCleanAfterSend *gtk.CheckButton 145 | cbReceive2File *gtk.CheckButton 146 | btnSaveData *gtk.Button 147 | btnLoadData *gtk.Button 148 | labelLocalAddr *gtk.Label 149 | labelLocalPort *gtk.Label 150 | cbAppendNewLine *gtk.CheckButton 151 | } 152 | 153 | // NetAssistantAppNew create new instance 154 | func NetAssistantAppNew() *NetAssistantApp { 155 | obj := &NetAssistantApp{} 156 | obj.chanClose = make(chan bool) 157 | return obj 158 | } 159 | 160 | func appendConntent2File(filename string, content []byte) { 161 | fd, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) 162 | defer fd.Close() 163 | fd.Write(content) 164 | } 165 | 166 | func (app *NetAssistantApp) getRecvData() string { 167 | buff, err := app.tvDataReceive.GetBuffer() 168 | if err != nil { 169 | log.Error(err) 170 | return "" 171 | } 172 | 173 | start, end := buff.GetBounds() 174 | data, err := buff.GetText(start, end, true) 175 | if err != nil { 176 | log.Error(err) 177 | return "" 178 | } 179 | return data 180 | } 181 | 182 | func (app *NetAssistantApp) update(recvStr string) { 183 | list := []string{} 184 | if app.cbHexDisplay.GetActive() { 185 | for i := 0; i < len(recvStr); i++ { 186 | log.Debug(i, recvStr[i]) 187 | list = append(list, fmt.Sprintf("%X", recvStr[i])) 188 | } 189 | recvStr = strings.Join(list, " ") 190 | } 191 | 192 | if app.cbDisplayDate.GetActive() { 193 | recvStr = fmt.Sprintf("[%s]%s\n", time.Now().Format(time.DateTime+".000000"), recvStr) 194 | } 195 | 196 | if app.cbReceive2File.GetActive() { 197 | appendConntent2File(app.fileName, []byte(recvStr)) 198 | } 199 | 200 | iter := app.tbReceData.GetEndIter() 201 | app.tbReceData.Insert(iter, recvStr) 202 | app.labelReceveCount.SetText(getI18nText(IT_RECEVER_COUNT) + strconv.Itoa(app.receCount)) 203 | app.tbReceData.CreateMark(getI18nText(IT_END), iter, false) 204 | mark := app.tbReceData.GetMark(getI18nText(IT_END)) 205 | app.tvDataReceive.ScrollMarkOnscreen(mark) 206 | } 207 | 208 | func (app *NetAssistantApp) updateSendCount(count int) { 209 | app.sendCount += count 210 | app.labelSendCount.SetText(getI18nText(IT_SEND_COUNT) + strconv.Itoa(app.sendCount)) 211 | } 212 | 213 | func (app *NetAssistantApp) handler(conn net.Conn) { 214 | defer conn.Close() // close connection 215 | for { 216 | reader := bufio.NewReader(conn) 217 | var buf [2048]byte 218 | n, err := reader.Read(buf[:]) 219 | if err != nil { 220 | log.Info("connection closed:", err) 221 | _, ok := conn.(*net.UDPConn) 222 | if !ok { 223 | ss := conn.RemoteAddr().String() 224 | tips := fmt.Sprintf(`connection closed: %s `, ss) 225 | glib.IdleAdd(func() { 226 | app.labelStatus.SetMarkup(tips) 227 | }) 228 | } 229 | for index, connItem := range app.connList { 230 | if conn.LocalAddr().String() == connItem.LocalAddr().String() { 231 | app.connList = append(app.connList[:index], app.connList[index+1:]...) 232 | } 233 | } 234 | return 235 | } 236 | app.receCount += n 237 | recvStr := string(buf[:n]) 238 | if !app.cbPauseDisplay.GetActive() { 239 | glib.IdleAdd(func() { 240 | list := []string{} 241 | if app.cbHexDisplay.GetActive() { 242 | for i := 0; i < len(recvStr); i++ { 243 | log.Debug(i, recvStr[i]) 244 | list = append(list, fmt.Sprintf("%02X", recvStr[i])) 245 | } 246 | recvStr = strings.Join(list, " ") 247 | } 248 | 249 | if app.cbDisplayDate.GetActive() { 250 | recvStr = fmt.Sprintf("[%s]%s\n", time.Now().Format(time.DateTime+".000000"), recvStr) 251 | } 252 | 253 | if app.cbReceive2File.GetActive() { 254 | appendConntent2File(app.fileName, []byte(recvStr)) 255 | } 256 | 257 | iter := app.tbReceData.GetEndIter() 258 | app.tbReceData.Insert(iter, recvStr) 259 | app.labelReceveCount.SetText(getI18nText(IT_RECEVER_COUNT) + strconv.Itoa(app.receCount)) 260 | app.tbReceData.CreateMark(getI18nText(IT_END), iter, false) 261 | mark := app.tbReceData.GetMark(getI18nText(IT_END)) 262 | app.tvDataReceive.ScrollMarkOnscreen(mark) 263 | }) //Make sure is running on the gui thread. 264 | } 265 | } 266 | } 267 | 268 | func (app *NetAssistantApp) onBtnCleanCount() { 269 | app.receCount = 0 270 | app.sendCount = 0 271 | app.labelReceveCount.SetText(getI18nText(IT_RECEVER_COUNT)) 272 | app.labelSendCount.SetText(getI18nText(IT_SEND_COUNT)) 273 | app.labelStatus.SetText("") 274 | } 275 | 276 | func (app *NetAssistantApp) onCbReceive2File() { 277 | if app.cbReceive2File.GetActive() { 278 | dialog, _ := gtk.FileChooserNativeDialogNew("Select File", app.appWindow, gtk.FILE_CHOOSER_ACTION_OPEN, "Select", "Cancel") 279 | res := dialog.Run() 280 | if res == int(gtk.RESPONSE_ACCEPT) { 281 | fileName := dialog.FileChooser.GetFilename() 282 | app.fileName = fileName 283 | } 284 | dialog.Destroy() 285 | } 286 | } 287 | 288 | func (app *NetAssistantApp) onBtnLoadData() { 289 | dialog, _ := gtk.FileChooserNativeDialogNew("Select File", app.appWindow, gtk.FILE_CHOOSER_ACTION_OPEN, "Select", "Cancel") 290 | res := dialog.Run() 291 | if res == int(gtk.RESPONSE_ACCEPT) { 292 | fileName := dialog.FileChooser.GetFilename() 293 | data, err := os.ReadFile(fileName) 294 | if err != nil { 295 | log.Error(err) 296 | } else { 297 | buf, _ := app.tvDataSend.GetBuffer() 298 | buf.SetText(string(data)) 299 | } 300 | } 301 | dialog.Destroy() 302 | } 303 | 304 | func (app *NetAssistantApp) onBtnSaveData() { 305 | dialog, _ := gtk.FileChooserNativeDialogNew(getI18nText(IT_SAVE_TO_FILE), app.appWindow, gtk.FILE_CHOOSER_ACTION_SAVE, "Save", "Cancel") 306 | res := dialog.Run() 307 | if res == int(gtk.RESPONSE_ACCEPT) { 308 | fileName := dialog.FileChooser.GetFilename() 309 | appendConntent2File(fileName, []byte(app.getRecvData())) 310 | } 311 | dialog.Destroy() 312 | } 313 | 314 | func (app *NetAssistantApp) addConnection(conn net.Conn) { 315 | app.connList = append(app.connList, conn) 316 | } 317 | 318 | func (app *NetAssistantApp) updateStatus(msg string) { 319 | app.labelStatus.SetMarkup(msg) 320 | } 321 | 322 | func (app *NetAssistantApp) updateAllStatus(msg, curIP, curPort string) { 323 | app.labelStatus.SetMarkup(msg) 324 | app.entryCurAddr.SetText(curIP) 325 | app.entryCurPort.SetText(curPort) 326 | } 327 | 328 | func (app *NetAssistantApp) createConnect(serverType int, strIP, strPort string) error { 329 | addr := strIP + ":" + strPort 330 | if serverType == 0 { // TCP Client 331 | conn, err := net.Dial("tcp", addr) 332 | if err == nil { 333 | go app.handler(conn) 334 | app.addConnection(conn) 335 | locallConnInfo := strings.Split(conn.LocalAddr().String(), ":") 336 | app.updateAllStatus("TCP client connection succeeds", locallConnInfo[0], locallConnInfo[1]) 337 | } else { 338 | app.updateAllStatus(err.Error(), "", "") 339 | log.Error(err.Error()) 340 | return err 341 | } 342 | } 343 | if serverType == 1 { // TCP Server 344 | listen, err := net.Listen("tcp", addr) 345 | if err == nil { 346 | 347 | app.updateStatus("TCP server connection succeeds") 348 | go func() { 349 | for { 350 | conn, err := listen.Accept() 351 | if err != nil { 352 | log.Error("accept err:", err) 353 | return 354 | } 355 | ss := conn.RemoteAddr().String() 356 | tips := fmt.Sprintf(`new connection:%s `, ss) 357 | glib.IdleAdd(func() { 358 | app.labelStatus.SetMarkup(tips) 359 | }) 360 | app.connList = append(app.connList, conn) 361 | go app.handler(conn) 362 | } 363 | }() 364 | app.updateAllStatus("TCP server connection succeeds", strIP, strPort) 365 | app.listener = listen 366 | } else { 367 | app.updateStatus(err.Error()) 368 | log.Error(err) 369 | return err 370 | } 371 | } 372 | 373 | if serverType == 2 { // UDP Client 374 | conn, err := net.Dial("udp4", addr) 375 | if err == nil { 376 | go app.handler(conn) 377 | app.addConnection(conn) 378 | localConnInfo := strings.Split(conn.LocalAddr().String(), ":") 379 | app.updateAllStatus("UDP client connection succeeds", localConnInfo[0], localConnInfo[1]) 380 | } else { 381 | app.updateStatus(err.Error()) 382 | log.Error(err) 383 | return err 384 | } 385 | } 386 | 387 | if serverType == 3 { // UDP Server 388 | address, err := net.ResolveUDPAddr("udp4", addr) 389 | if err != nil { 390 | app.updateStatus("UDP server connection succeeds" + err.Error()) 391 | } else { 392 | udpConn, err := net.ListenUDP("udp4", address) 393 | if err == nil { 394 | go app.handler(udpConn) 395 | app.addConnection(udpConn) 396 | localConnInfo := strings.Split(udpConn.LocalAddr().String(), ":") 397 | app.updateAllStatus("UDP server connection succeeds", localConnInfo[0], localConnInfo[1]) 398 | app.labelLocalAddr.SetLabel("Taget UDP IP") 399 | app.labelLocalPort.SetLabel("Taget UDP Port") 400 | app.entryCurAddr.SetEditable(true) 401 | app.entryCurAddr.SetText("") 402 | app.entryCurPort.SetEditable(true) 403 | app.entryCurPort.SetText("") 404 | } else { 405 | app.updateStatus(err.Error()) 406 | log.Error(err) 407 | return err 408 | } 409 | } 410 | } 411 | 412 | return nil 413 | } 414 | 415 | func (app *NetAssistantApp) disconnect(serverType int) error { 416 | if serverType == 1 { 417 | if app.listener != nil { 418 | app.listener.Close() 419 | } 420 | } 421 | 422 | for _, conn := range app.connList { 423 | conn.Close() 424 | } 425 | 426 | if serverType == 3 { 427 | app.labelLocalAddr.SetLabel(getI18nText(IT_LOCAL_IP)) 428 | app.labelLocalPort.SetLabel(getI18nText(IT_LOCAL_PORT)) 429 | app.entryCurAddr.SetEditable(false) 430 | app.entryCurAddr.SetText("") 431 | app.entryCurPort.SetEditable(false) 432 | app.entryCurPort.SetText("") 433 | } 434 | 435 | app.updateStatus(getI18nText(IT_WAIT_CONN)) 436 | app.connList = []net.Conn{} 437 | return nil 438 | } 439 | 440 | func (app *NetAssistantApp) onBtnConnect(button *gtk.Button) { 441 | strIP, _ := app.entryIP.GetText() 442 | strPort, _ := app.entryPort.GetText() 443 | serverType := app.combProtoType.GetActive() 444 | label, _ := app.btnConnect.GetLabel() 445 | 446 | isDisconnect := label == getI18nText(IT_DISCONNECT) 447 | if isDisconnect { 448 | if err := app.disconnect(serverType); err == nil { 449 | app.btnConnect.SetLabel(getI18nText(IT_CONNECT)) 450 | app.combProtoType.SetSensitive(true) 451 | } 452 | } else { 453 | if err := app.createConnect(serverType, strIP, strPort); err == nil { 454 | app.btnConnect.SetLabel(getI18nText(IT_DISCONNECT)) 455 | app.combProtoType.SetSensitive(false) 456 | } 457 | } 458 | } 459 | 460 | func (app *NetAssistantApp) onBtnSend() { 461 | buff, err := app.tvDataSend.GetBuffer() 462 | if err != nil { 463 | log.Error(err) 464 | } 465 | 466 | start, end := buff.GetBounds() 467 | data, _ := buff.GetText(start, end, true) 468 | 469 | if app.cbAppendNewLine.GetActive() { 470 | data += "\r\n" 471 | } 472 | 473 | sendData := []byte(data) 474 | 475 | if app.cbSendByHex.GetActive() { 476 | data = strings.Replace(data, " ", "", -1) 477 | data = strings.Replace(data, "\n", "", -1) 478 | hexData, err := hex.DecodeString(data) 479 | if err != nil { 480 | log.Error(err) 481 | strTips := fmt.Sprintf(`😱%s`, err) 482 | app.labelStatus.SetMarkup(strTips) 483 | } else { 484 | sendData = hexData 485 | } 486 | log.Info(hexData) 487 | } 488 | 489 | label, err := app.btnSend.GetLabel() 490 | if label != getI18nText(IT_SEND) { 491 | app.chanClose <- true 492 | app.btnSend.SetLabel(getI18nText(IT_SEND)) 493 | return 494 | } 495 | 496 | if app.cbDataSourceCycleSend.GetActive() { // loop send 497 | app.btnSend.SetLabel(getI18nText(IT_STOP)) 498 | strCycleTime, err := app.entryCycleTime.GetText() 499 | if err != nil { 500 | strCycleTime = "1000" 501 | } 502 | cycle, err := strconv.Atoi(strCycleTime) 503 | if err != nil { 504 | cycle = 1000 505 | } 506 | go func(cycleTime int) { 507 | END: 508 | for { 509 | select { 510 | case <-app.chanClose: // waiting close 511 | break END 512 | default: 513 | for _, conn := range app.connList { // range current connection 514 | if udpConnection, ok := conn.(*net.UDPConn); ok && app.combProtoType.GetActive() == 3 { // if the connection is UDP 515 | strIP, _ := app.entryCurAddr.GetText() 516 | strPort, _ := app.entryCurPort.GetText() 517 | address, err := net.ResolveUDPAddr("udp4", strIP+":"+strPort) 518 | if err == nil { 519 | udpConnection.WriteToUDP(sendData, address) // send udp message 520 | } else { 521 | log.Error(err) 522 | } 523 | 524 | } else { 525 | conn.Write(sendData) // send message 526 | } 527 | } 528 | if len(app.connList) == 0 { 529 | glib.IdleAdd(func() { 530 | app.labelStatus.SetText(getI18nText(IT_NO_CONN)) 531 | app.btnSend.SetLabel(getI18nText(IT_SEND)) 532 | }) 533 | break END 534 | } 535 | } 536 | time.Sleep(time.Duration(cycleTime) * time.Millisecond) 537 | } 538 | }(cycle) 539 | } else { // once send 540 | for _, conn := range app.connList { 541 | if cc, ok := conn.(*net.UDPConn); ok && app.combProtoType.GetActive() == 3 { 542 | strIP, _ := app.entryCurAddr.GetText() 543 | strPort, _ := app.entryCurPort.GetText() 544 | address, err := net.ResolveUDPAddr("udp4", strIP+":"+strPort) 545 | if err == nil { 546 | cc.WriteToUDP(sendData, address) 547 | } 548 | } else { 549 | conn.Write(sendData) 550 | } 551 | log.Info("Write data", data) 552 | app.updateSendCount(len(sendData)) 553 | } 554 | } 555 | 556 | if app.cbAutoCleanAfterSend.GetActive() { 557 | buff.SetText("") 558 | } 559 | 560 | } 561 | 562 | func (app *NetAssistantApp) onBtnClearRecvDisplay() { 563 | app.tbReceData.SetText("") 564 | } 565 | 566 | func (app *NetAssistantApp) doActivate(application *gtk.Application) { 567 | app.appWindow, _ = gtk.ApplicationWindowNew(application) 568 | app.appWindow.SetPosition(gtk.WIN_POS_CENTER) 569 | app.appWindow.SetResizable(false) 570 | loader, _ := gdk.PixbufLoaderNew() 571 | data, _ := base64.StdEncoding.DecodeString(icon) 572 | loader.Write(data) 573 | buf, _ := loader.GetPixbuf() 574 | app.appWindow.SetIcon(buf) 575 | 576 | app.appWindow.SetBorderWidth(10) 577 | app.appWindow.SetTitle(getI18nText(IT_APP_NAME)) 578 | 579 | // container 580 | windowContainer, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 10) 581 | windowContainerMiddle, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 10) 582 | windowContainerLeft, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 10) 583 | windowContainerRight, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 10) 584 | windowContainerBottom, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 10) 585 | 586 | // Left box 587 | // type of service frame 588 | frame, _ := gtk.FrameNew(getI18nText(IT_SETTING)) 589 | frame.SetLabelAlign(0.1, 0.5) 590 | verticalBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 10) // 591 | frame.Add(verticalBox) 592 | labelProtType, _ := gtk.LabelNew(getI18nText(IT_TYPE)) 593 | labelProtType.SetXAlign(0) 594 | app.combProtoType, _ = gtk.ComboBoxTextNew() 595 | app.combProtoType.AppendText(getI18nText(IT_TCP_CLIENT)) 596 | app.combProtoType.AppendText(getI18nText(IT_TCP_SERVER)) 597 | app.combProtoType.AppendText(getI18nText(IT_UDP_CLIENT)) 598 | app.combProtoType.AppendText(getI18nText(IT_UDP_SERVER)) 599 | app.combProtoType.SetActive(0) 600 | verticalBox.PackStart(labelProtType, false, false, 0) 601 | verticalBox.PackStart(app.combProtoType, false, false, 0) 602 | labelIP, _ := gtk.LabelNew("IP") 603 | labelIP.SetXAlign(0) 604 | app.entryIP, _ = gtk.EntryNew() 605 | app.entryIP.SetText("127.0.0.1") 606 | verticalBox.PackStart(labelIP, false, false, 0) 607 | verticalBox.PackStart(app.entryIP, false, false, 0) 608 | labelPort, _ := gtk.LabelNew(getI18nText(IT_PORT)) 609 | labelPort.SetXAlign(0) 610 | app.entryPort, _ = gtk.EntryNew() 611 | app.entryPort.SetText("50023") 612 | verticalBox.PackStart(labelPort, false, false, 0) 613 | verticalBox.PackStart(app.entryPort, false, false, 0) 614 | app.btnConnect, _ = gtk.ButtonNewWithLabel(getI18nText(IT_CONNECT)) 615 | app.btnConnect.Connect("clicked", app.onBtnConnect) 616 | verticalBox.PackStart(app.btnConnect, false, false, 0) 617 | 618 | // Recv Settings, Send Settings 619 | notebookTab, _ := gtk.NotebookNew() 620 | label1, _ := gtk.LabelNew(getI18nText(IT_RECV_SETTINGS)) 621 | label2, _ := gtk.LabelNew(getI18nText(IT_SEND_SETTINGS)) 622 | 623 | // Recv Settings 624 | frame1ContentBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 10) 625 | app.cbReceive2File, _ = gtk.CheckButtonNewWithLabel(getI18nText(IT_SAVE_TO_FILE)) 626 | app.cbReceive2File.Connect("toggled", app.onCbReceive2File) 627 | app.cbDisplayDate, _ = gtk.CheckButtonNewWithLabel(getI18nText(IT_SHOW_RECV_TIME)) 628 | app.cbHexDisplay, _ = gtk.CheckButtonNewWithLabel(getI18nText(IT_SHOW_HEX)) 629 | app.cbPauseDisplay, _ = gtk.CheckButtonNewWithLabel(getI18nText(IT_PAUSE)) 630 | btnHboxContainer, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 10) 631 | app.btnSaveData, _ = gtk.ButtonNewWithLabel(getI18nText(IT_SAVE)) 632 | app.btnSaveData.Connect("clicked", app.onBtnSaveData) 633 | app.btnClearRecvDisplay, _ = gtk.ButtonNewWithLabel(getI18nText(IT_CLEAR)) 634 | app.btnClearRecvDisplay.Connect("clicked", app.onBtnClearRecvDisplay) 635 | 636 | btnHboxContainer.PackStart(app.btnSaveData, true, false, 0) 637 | btnHboxContainer.PackStart(app.btnClearRecvDisplay, true, false, 0) 638 | frame1ContentBox.PackStart(app.cbReceive2File, false, false, 0) 639 | frame1ContentBox.PackStart(app.cbDisplayDate, false, false, 0) 640 | frame1ContentBox.PackStart(app.cbHexDisplay, false, false, 0) 641 | frame1ContentBox.PackStart(app.cbPauseDisplay, false, false, 0) 642 | frame1ContentBox.PackStart(btnHboxContainer, false, false, 0) 643 | frame1ContentBox.SetBorderWidth(10) 644 | 645 | // Send Settings 646 | frame2ContentBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 10) 647 | app.cbAppendNewLine, _ = gtk.CheckButtonNewWithLabel(getI18nText(IT_APPEND_RN)) 648 | app.cbAutoCleanAfterSend, _ = gtk.CheckButtonNewWithLabel(getI18nText(IT_AUTO_CLEAR)) 649 | app.cbSendByHex, _ = gtk.CheckButtonNewWithLabel(getI18nText(IT_SEND_HEX)) 650 | app.cbDataSourceCycleSend, _ = gtk.CheckButtonNewWithLabel(getI18nText(IT_SEND_CIRC)) 651 | app.entryCycleTime, _ = gtk.EntryNew() 652 | app.entryCycleTime.SetPlaceholderText("default 1000(ms)") 653 | btnHboxContainer2, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 10) 654 | app.btnLoadData, _ = gtk.ButtonNewWithLabel(getI18nText(IT_LOAD_DATA)) 655 | app.btnClearSendDisplay, _ = gtk.ButtonNewWithLabel(getI18nText(IT_CLEAR)) 656 | app.btnLoadData.Connect("clicked", app.onBtnLoadData) 657 | app.btnClearSendDisplay.Connect("clicked", func() { 658 | buff, _ := app.tvDataSend.GetBuffer() 659 | buff.SetText("") 660 | }) 661 | 662 | frame2ContentBox.PackStart(app.cbAppendNewLine, false, false, 0) 663 | frame2ContentBox.PackStart(app.cbAutoCleanAfterSend, false, false, 0) 664 | frame2ContentBox.PackStart(app.cbSendByHex, false, false, 0) 665 | frame2ContentBox.PackStart(app.cbDataSourceCycleSend, false, false, 0) 666 | frame2ContentBox.PackStart(app.entryCycleTime, false, false, 0) 667 | btnHboxContainer2.PackStart(app.btnLoadData, true, false, 0) 668 | btnHboxContainer2.PackStart(app.btnClearSendDisplay, true, false, 0) 669 | frame2ContentBox.PackStart(btnHboxContainer2, false, false, 0) 670 | frame2ContentBox.SetBorderWidth(10) 671 | 672 | frame1, _ := gtk.FrameNew("") 673 | frame1.Add(frame1ContentBox) 674 | frame2, _ := gtk.FrameNew("") 675 | frame2.Add(frame2ContentBox) 676 | 677 | notebookTab.AppendPage(frame1, label1) 678 | notebookTab.AppendPage(frame2, label2) 679 | 680 | // Data Received 681 | titleDataReceiveArea, _ := gtk.LabelNew(getI18nText(IT_DATA_RECVED)) 682 | titleDataReceiveArea.SetXAlign(0) 683 | windowContainerRight.PackStart(titleDataReceiveArea, false, false, 0) 684 | app.swDataRec, _ = gtk.ScrolledWindowNew(nil, nil) 685 | app.tvDataReceive, _ = gtk.TextViewNew() 686 | app.tvDataReceive.SetEditable(false) 687 | app.tvDataReceive.SetWrapMode(gtk.WRAP_CHAR) 688 | app.swDataRec.Add(app.tvDataReceive) 689 | windowContainerRight.PackStart(app.swDataRec, true, true, 0) 690 | 691 | // Local info 692 | middleContainer, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 10) 693 | app.labelLocalAddr, _ = gtk.LabelNew(getI18nText(IT_LOCAL_IP)) 694 | app.entryCurAddr, _ = gtk.EntryNew() 695 | app.entryCurAddr.SetEditable(false) 696 | app.labelLocalPort, _ = gtk.LabelNew(getI18nText(IT_LOCAL_PORT)) 697 | app.entryCurPort, _ = gtk.EntryNew() 698 | app.entryCurPort.SetEditable(false) 699 | middleContainer.PackStart(app.labelLocalAddr, false, false, 0) 700 | middleContainer.PackStart(app.entryCurAddr, false, false, 0) 701 | middleContainer.PackStart(app.labelLocalPort, false, false, 0) 702 | middleContainer.PackStart(app.entryCurPort, false, false, 0) 703 | windowContainerRight.PackStart(middleContainer, false, false, 0) 704 | 705 | // send area 706 | bottomContainer, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 10) 707 | scrollerDataSend, _ := gtk.ScrolledWindowNew(nil, nil) 708 | app.tvDataSend, _ = gtk.TextViewNew() 709 | app.tvDataSend.SetWrapMode(gtk.WRAP_CHAR) 710 | scrollerDataSend.Add(app.tvDataSend) 711 | scrollerDataSend.SetSizeRequest(-1, 180) 712 | boxSendBtn, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) 713 | app.btnSend, _ = gtk.ButtonNewWithLabel(getI18nText(IT_SEND)) 714 | app.btnSend.Connect("clicked", app.onBtnSend) 715 | boxSendBtn.PackEnd(app.btnSend, false, false, 0) 716 | app.btnSend.SetSizeRequest(80, -1) 717 | bottomContainer.PackStart(scrollerDataSend, true, true, 0) 718 | bottomContainer.PackEnd(boxSendBtn, false, false, 0) 719 | windowContainerRight.PackStart(bottomContainer, false, false, 0) 720 | 721 | // bottom area 722 | app.labelStatus, _ = gtk.LabelNew("") 723 | app.labelStatus.SetMarkup(getI18nText(IT_WAIT_CONN)) 724 | windowContainerBottom.PackStart(app.labelStatus, true, false, 0) 725 | app.labelSendCount, _ = gtk.LabelNew(getI18nText(IT_SEND_COUNT)) 726 | windowContainerBottom.PackStart(app.labelSendCount, true, false, 0) 727 | app.labelReceveCount, _ = gtk.LabelNew(getI18nText(IT_RECEVER_COUNT)) 728 | windowContainerBottom.PackStart(app.labelReceveCount, true, false, 0) 729 | app.btnCleanCount, _ = gtk.ButtonNewWithLabel(getI18nText(IT_RESET)) 730 | app.btnCleanCount.Connect("clicked", app.onBtnCleanCount) 731 | 732 | windowContainerBottom.PackEnd(app.btnCleanCount, false, false, 0) 733 | 734 | app.appWindow.Add(windowContainer) 735 | 736 | windowContainerLeft.PackStart(frame, false, false, 0) 737 | windowContainerLeft.PackStart(notebookTab, false, false, 0) 738 | windowContainerMiddle.PackStart(windowContainerLeft, false, false, 0) 739 | windowContainerMiddle.PackStart(windowContainerRight, false, false, 0) 740 | 741 | windowContainer.PackStart(windowContainerMiddle, false, false, 0) 742 | windowContainer.PackStart(windowContainerBottom, false, false, 0) 743 | 744 | app.appWindow.SetDefaultSize(400, 400) 745 | app.appWindow.ShowAll() 746 | 747 | if app.tbReceData == nil { 748 | app.tbReceData, _ = gtk.TextBufferNew(nil) 749 | app.tvDataReceive.SetBuffer(app.tbReceData) 750 | } 751 | } 752 | 753 | func init() { 754 | var format = logging.MustStringFormatter( 755 | `%{color}%{time:15:04:05.000} %{shortfile} func:%{longfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`, 756 | ) 757 | 758 | backend1 := logging.NewLogBackend(os.Stderr, "", 0) 759 | backend1Leveled := logging.AddModuleLevel(backend1) 760 | formatter := logging.NewBackendFormatter(backend1, format) 761 | backend1Leveled = logging.SetBackend(formatter) 762 | if os.Getenv("NET_ASS_DEBUG") == "on" { 763 | backend1Leveled.SetLevel(logging.DEBUG, "") 764 | fmt.Println("NET_ASS_DEBUG", "on") 765 | } else { 766 | backend1Leveled.SetLevel(logging.CRITICAL, "") 767 | } 768 | } 769 | 770 | func main() { 771 | const appID = "com.github.bytevoyager.netassistant" 772 | application, err := gtk.ApplicationNew(appID, glib.APPLICATION_NON_UNIQUE) 773 | 774 | if err != nil { 775 | log.Fatal("Could not create application.", err) 776 | } 777 | 778 | app := NetAssistantAppNew() 779 | application.Connect("activate", app.doActivate) 780 | 781 | application.Run(os.Args) 782 | } 783 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byte-voyager/NetAssistant/78c9e0e78383ba3b67bcbcc963524e2aac71c641/demo.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module netassistant 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/gotk3/gotk3 v0.6.2 7 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 8 | ) 9 | -------------------------------------------------------------------------------- /netassistant: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byte-voyager/NetAssistant/78c9e0e78383ba3b67bcbcc963524e2aac71c641/netassistant --------------------------------------------------------------------------------