├── .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 | 
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
--------------------------------------------------------------------------------