Gần đây tôi đã đi sâu vào thế giới tuyệt vời của mạng máy tính. Một trong những dự án thú vị mà tôi đã tạo là một ứng dụng phòng trò chuyện đơn giản hỗ trợ nhắn tin theo thời gian thực giữa các khách hàng khác nhau
zeyu2001 / pychat
Ứng dụng phòng trò chuyện Python thời gian thực với Tkinter GUI
Tại bất kỳ thời điểm nào trong hướng dẫn này, bạn có thể tham khảo mã nguồn của tôi trong GitHub. Mục đích của hướng dẫn này là giới thiệu một số lý thuyết mạng cơ bản đồng thời cung cấp kinh nghiệm lập trình socket thực tế. Nếu tại bất kỳ thời điểm nào, bạn thấy rằng mình đã cảm thấy thoải mái với lý thuyết liên quan, vui lòng bỏ qua phần tiếp theo
điều kiện tiên quyết
Bạn nên biết Python cơ bản. Ngoài ra, không có gì khác. Trong quá trình tạo ứng dụng này, bạn cũng sẽ tìm hiểu về mạng máy tính cơ bản và kiến trúc máy khách-máy chủ
Chúng tôi sẽ sử dụng Tkinter và ổ cắm, cả hai đều có sẵn trong thư viện chuẩn của Python. Không có gì bạn cần phải cài đặt
Kiến trúc máy khách-máy chủ
Kiến trúc máy khách-máy chủ là một mô hình điện toán cơ bản trong đó máy khách yêu cầu dịch vụ từ máy chủ, trong khi máy chủ xử lý các yêu cầu này và cung cấp dịch vụ cho máy khách
Trên thực tế, trình duyệt web của bạn là ứng dụng khách yêu cầu dịch vụ web từ máy chủ web của Medium thông qua Giao thức truyền siêu văn bản [HTTP] ngay bây giờ. Máy chủ web của Medium xử lý yêu cầu của bạn và trả về các tài liệu HTML, biểu định kiểu CSS, tệp JavaScript và hình ảnh để trình duyệt web của bạn có thể hiển thị trang web theo cách của nó
Hơn nữa, một máy chủ có thể phục vụ nhiều máy khách. Trong ứng dụng của chúng tôi, chúng tôi muốn nhiều khách hàng nói chuyện trong thời gian thực. Phần mềm máy khách sẽ gửi tin nhắn đến máy chủ phòng trò chuyện và máy chủ phòng trò chuyện sẽ phát tin nhắn của chúng tôi tới tất cả các máy khách được kết nối khác
Nghị định thư, Nghị định thư, Nghị định thư
Giao tiếp qua mạng sử dụng cái mà chúng tôi gọi là ngăn xếp giao thức — xây dựng các cuộc hội thoại cấp cao hơn, phức tạp hơn trên các cuộc hội thoại đơn giản hơn, thô sơ hơn. Các lớp mạng có thể được biểu diễn bằng mô hình OSI và mô hình TCP/IP. Mỗi lớp mạng tương ứng với một nhóm các giao thức mạng dành riêng cho từng lớp
Với mục đích của ứng dụng này, chúng ta không cần quan tâm đến nhiều giao thức cấp thấp hơn. Nhưng chúng tôi cần biết rằng chúng tôi đang sử dụng một thứ gọi là Giao thức điều khiển truyền tải [TCP]
TCP và UDP là các giao thức tầng vận chuyển — chúng chi phối cách dữ liệu được gửi từ điểm này sang điểm khác. Chúng tôi đang xây dựng dựa trên TCP, điều đó có nghĩa là chúng tôi không cần quan tâm đến việc dữ liệu được gửi như thế nào, chỉ cần dữ liệu được gửi là gì và ở đâu
Sự khác biệt chính giữa TCP và UDP là TCP đảm bảo phân phối đáng tin cậy, không có bất kỳ thông tin nào bị mất, trùng lặp hoặc không theo thứ tự. UDP không đảm bảo điều tương tự và để nó cho lớp ứng dụng xử lý các gói bị rơi. Nó yêu cầu máy chủ xác nhận đã nhận dữ liệu
UDP thường được sử dụng cho các lần truyền nhạy cảm với thời gian trong đó một gói bị mất được ưu tiên hơn là chờ các gói bị mất được truyền lại. Ví dụ, hãy tưởng tượng một cuộc gọi thoại trong thời gian thực. Nếu một gói đã bị mất, sẽ không có ý nghĩa gì nếu đợi đoạn âm thanh đó đến bởi vì vào thời điểm đó, nó đã quá cũ. Hơn nữa, bạn vẫn có thể ghép nối cuộc trò chuyện với nhau mà không cần gói âm thanh đó.
Mặt khác, TCP sẽ tiếp tục ngoan cố gửi lại đoạn âm thanh bị mất đó mặc dù nó đã quá cũ để sử dụng. Tuy nhiên, đối với hầu hết các ứng dụng khác, khía cạnh này của TCP cực kỳ có giá trị. Trong ứng dụng trò chuyện của chúng tôi, chúng tôi không muốn phải xử lý các gói bị mất vì chúng tôi luôn muốn nhận được các tin nhắn hoàn chỉnh mà không có bất kỳ lỗi nào
Máy chủ phòng chat
Hãy bắt đầu với tập lệnh máy chủ của chúng tôi. Chúng tôi bắt đầu bằng cách xác định một lớp
Source [IP: Port Number] → Destination [IP: Port Number]
1 trong Source [IP: Port Number] → Destination [IP: Port Number]
2#!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Khái niệm cơ bản về luồng
Lưu ý rằng chúng tôi đang sử dụng đa luồng để cho phép nhiều đoạn mã chạy đồng thời. Lớp
Source [IP: Port Number] → Destination [IP: Port Number]
1 của chúng ta kế thừa từ lớp Source [IP: Port Number] → Destination [IP: Port Number]
4 của Python, do đó tạo ra một luồng. Chúng ta cần xác định logic cho luồng Source [IP: Port Number] → Destination [IP: Port Number]
1 của mình trong phương thức Source [IP: Port Number] → Destination [IP: Port Number]
6. Khi Source [IP: Port Number] → Destination [IP: Port Number]
7 được gọi trên đối tượng Source [IP: Port Number] → Destination [IP: Port Number]
1, thì Source [IP: Port Number] → Destination [IP: Port Number]
6 sẽ được thực thi song song với luồng chínhKhái niệm cơ bản về ổ cắm
Ổ cắm là địa chỉ IP + cặp số cổng. Một địa chỉ IP xác định một máy chủ lưu trữ. Tuy nhiên, một máy chủ có thể có nhiều ứng dụng khác nhau chạy cùng một lúc. Làm cách nào để hệ điều hành [HĐH] của bạn biết rằng phản hồi HTTP dành cho trình duyệt web của bạn chứ không phải Call of Duty. Chiến tranh hiện đại? . cuộc gọi của nhiệm vụ. Modern Warfare sử dụng phạm vi cổng TCP 27014–27050 chẳng hạn
Do đó, bất kỳ giao tiếp nào giữa hai thiết bị mạng đều cần một cặp ổ cắm
Source [IP: Port Number] → Destination [IP: Port Number]
Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Tạo một ổ cắm
Hãy bắt đầu xác định logic luồng của chúng tôi. Thêm phần sau vào phương thức
Source [IP: Port Number] → Destination [IP: Port Number]
6Source [IP: Port Number] → Destination [IP: Port Number]
2Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Đầu tiên, chúng tôi đã tạo một đối tượng
Source [IP: Port Number] → Destination [IP: Port Number]
21. Source [IP: Port Number] → Destination [IP: Port Number]
22 có hai đối số. họ địa chỉ và loại ổ cắm. Họ địa chỉ Source [IP: Port Number] → Destination [IP: Port Number]
23 được sử dụng cho mạng IP. Loại ổ cắm Source [IP: Port Number] → Destination [IP: Port Number]
24 được sử dụng cho các luồng dữ liệu được điều khiển theo luồng đáng tin cậy, chẳng hạn như các luồng do TCP cung cấp. Mặt khác, UDP yêu cầu loại ổ cắm dựa trên gói, Source [IP: Port Number] → Destination [IP: Port Number]
25Tiếp theo, chúng tôi đặt tùy chọn
Source [IP: Port Number] → Destination [IP: Port Number]
26. Bạn có thể đọc thêm về các tùy chọn socket trong trang hướng dẫn sử dụng Linux socket[7]. Tùy chọn này cho phép máy chủ sử dụng cùng một cổng sau khi kết nối cũ bị đóng [thông thường, bạn sẽ phải đợi trong vài phút]Sau đó, chúng tôi sử dụng
Source [IP: Port Number] → Destination [IP: Port Number]
27 để liên kết đối tượng Source [IP: Port Number] → Destination [IP: Port Number]
22 với một địa chỉ ổ cắm trên máy chủ. Source [IP: Port Number] → Destination [IP: Port Number]
27 nhận vào một bộ, ở định dạng#!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
2Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Lưu ý rằng một máy có thể có nhiều giao diện IP bên ngoài. bạn có thể tìm thấy của mình bằng cách sử dụng lệnh
#!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
20 tại thiết bị đầu cuối [hoặc #!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
21 cho Windows]Bạn nên biết rằng kết quả đầu tiên tôi nhận được, giao diện
#!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
22, là một kết quả rất đặc biệt. Đây là giao diện loopback, chỉ các chương trình khác chạy trên cùng một máy mới có thể truy cập được. Nó có địa chỉ IP là 127. 0. 0. 1 và chỉ có ý nghĩa cục bộ trong máy của bạn. Nó có tên máy chủ là 'localhost' và cung cấp một môi trường thử nghiệm an toànTrong lệnh
Source [IP: Port Number] → Destination [IP: Port Number]
27, chúng tôi có thể chỉ định bất kỳ giao diện IP nào [ngay cả giao diện loopback. ] hoặc chúng ta có thể sử dụng một chuỗi rỗng #!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
24 làm ký tự đại diện để cho biết rằng chúng ta sẵn sàng nhận các gói đến máy chủ thông qua bất kỳ giao diện mạng nào của nó. Trong chương trình của chúng tôi, chúng tôi sẽ để điều này cho người dùng, như bạn sẽ thấy sauCuối cùng, chúng tôi sử dụng
#!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
25 để chỉ ra rằng đây là ổ cắm nghe. TCP sử dụng hai loại ổ cắm. ổ cắm nghe và ổ cắm được kết nối. Sau khi gọi #!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
25 trên ổ cắm, ổ cắm này sẽ trở thành ổ cắm lắng nghe và chỉ có thể tạo điều kiện thiết lập kết nối TCP thông qua bắt tay chứ không phải chuyển dữ liệu thực sự. Chúng tôi sẽ cần tạo một ổ cắm hoàn toàn mới bất cứ khi nào máy khách kết nối, để gửi và nhận dữ liệu. Tiếp tục điChấp nhận kết nối
Source [IP: Port Number] → Destination [IP: Port Number]
0Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Chúng tôi tạo một vòng lặp vô hạn để lắng nghe các kết nối khách hàng mới. Cuộc gọi
#!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
27 sẽ đợi một máy khách mới kết nối và khi thực hiện xong, trả về một ổ cắm được kết nối mới cùng với địa chỉ ổ cắm của máy khách được kết nốiMột vài phương pháp khác để giới thiệu.
#!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
28 trả về địa chỉ ổ cắm ở đầu kia của kết nối [trong trường hợp này là máy khách] trong khi #!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
29 trả về địa chỉ ổ cắm mà đối tượng ổ cắm được liên kếtChúng tôi cần một cách để giao tiếp với từng khách hàng riêng lẻ, nhưng đồng thời, chúng tôi cần lắng nghe các kết nối mới từ các khách hàng tiềm năng khác. Cách chúng tôi thực hiện việc này là tạo một luồng
Source [IP: Port Number] → Destination [IP: Port Number]
00 mới [chúng tôi sẽ xác định điều này sau] mỗi khi một máy khách mới kết nối và luồng này chạy song song với luồng Source [IP: Port Number] → Destination [IP: Port Number]
1. Máy chủ cũng cần một cách để quản lý tất cả các kết nối máy khách đang hoạt động, do đó, nó lưu trữ các kết nối đang hoạt động dưới dạng đối tượng Source [IP: Port Number] → Destination [IP: Port Number]
00 trong Source [IP: Port Number] → Destination [IP: Port Number]
03'Phát thanh truyền hình'
Ứng dụng phòng trò chuyện nhỏ của chúng tôi sẽ hoạt động như thế nào
Máy khách gửi tin nhắn đến máy chủ từ dòng lệnh hoặc GUI
Máy chủ nhận và xử lý tin nhắn
Máy chủ gửi tin nhắn đến tất cả các máy khách được kết nối khác
Máy khách sẽ hiển thị thông báo trong dòng lệnh hoặc GUI
Hãy thêm chức năng 'broadcasting' từ bước 3 vào lớp
Source [IP: Port Number] → Destination [IP: Port Number]
1. Tôi thận trọng khi sử dụng từ 'phát sóng' vì phát sóng đề cập đến một khái niệm hoàn toàn khác trong ngữ cảnh mạng máy tính. Chúng tôi thực sự đang gửi nhiều unicast, là truyền một-một đến từng khách hàng được kết nối riêng lẻSource [IP: Port Number] → Destination [IP: Port Number]
9Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Lưu ý rằng
Source [IP: Port Number] → Destination [IP: Port Number]
03 là danh sách các đối tượng Source [IP: Port Number] → Destination [IP: Port Number]
00 đại diện cho các kết nối máy khách đang hoạt động. Chúng ta sẽ định nghĩa lớp Source [IP: Port Number] → Destination [IP: Port Number]
00 bên dướigửi và nhận
Lớp
Source [IP: Port Number] → Destination [IP: Port Number]
00 sẽ tạo điều kiện giao tiếp với các khách hàng cá nhânSource [IP: Port Number] → Destination [IP: Port Number]
4Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Một lần nữa, chúng tôi tạo ra một vòng lặp vô hạn. Lần này, thay vì lắng nghe các kết nối mới, chúng tôi đang lắng nghe dữ liệu do khách hàng gửi
Khi
Source [IP: Port Number] → Destination [IP: Port Number]
09 được gọi, nó sẽ đợi dữ liệu đến. Nếu không có dữ liệu, Source [IP: Port Number] → Destination [IP: Port Number]
09 sẽ không trả về [nó bị 'chặn'] và chương trình tạm dừng cho đến khi có dữ liệu. Các cuộc gọi như #!/usr/bin/env python3
import threading
import socket
import argparse
import os
class Server[threading.Thread]:
def __init__[self, host, port]:
super[].__init__[]
self.connections = []
self.host = host
self.port = port
def run[self]:
pass
27 và Source [IP: Port Number] → Destination [IP: Port Number]
09 khiến chương trình đợi cho đến khi một số dữ liệu mới đến, cho phép nó quay trở lại, được gọi là cuộc gọi chặn. Dữ liệu được gửi và nhận qua mạng dưới dạng chuỗi phụ, do đó cần được mã hóa và giải mã bằng cách sử dụng Source [IP: Port Number] → Destination [IP: Port Number]
93 và Source [IP: Port Number] → Destination [IP: Port Number]
94 tương ứngSource [IP: Port Number] → Destination [IP: Port Number]
09 nhận một đối số, Source [IP: Port Number] → Destination [IP: Port Number]
96, chỉ định lượng dữ liệu tối đa được nhận cùng một lúcMặt khác,
Source [IP: Port Number] → Destination [IP: Port Number]
97 gửi dữ liệu từ ổ cắm đến thiết bị ngang hàng được kết nối của nóMột vấn đề với cả
Source [IP: Port Number] → Destination [IP: Port Number]
97 và Source [IP: Port Number] → Destination [IP: Port Number]
09 là có một khả năng nhỏ là chỉ một phần dữ liệu được gửi hoặc nhận, bởi vì bộ đệm gửi đi hoặc đến đã gần đầy, vì vậy nó xếp bất kỳ dữ liệu nào có thể vào hàng đợi, trong khi để lại phần dữ liệu còn lại . Điều này trở thành vấn đề khi Source [IP: Port Number] → Destination [IP: Port Number]
97 trả về, nhưng trên thực tế, một số dữ liệu vẫn chưa được gửi. Chúng tôi có thể đặt Source [IP: Port Number] → Destination [IP: Port Number]
97 trong một vòng lặp hoặc chúng tôi có thể sử dụng Source [IP: Port Number] → Destination [IP: Port Number]
42 như một cách đơn giản để nói “Tôi muốn gửi tất cả dữ liệu”ổ cắm đã đóng
Một điều bạn sẽ nhận thấy trong đoạn mã trên là chúng tôi có thể biết liệu máy khách đã đóng kết thúc kết nối hay chưa. Khi ổ cắm máy khách được đóng lại, ____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____
Vì vậy, khi chúng tôi thấy rằng
Source [IP: Port Number] → Destination [IP: Port Number]
09 trả về một chuỗi rỗng [trong câu lệnh khác], chúng tôi cũng Source [IP: Port Number] → Destination [IP: Port Number]
47 phía kết nối của chúng tôi, loại bỏ luồng Source [IP: Port Number] → Destination [IP: Port Number]
00 khỏi danh sách các kết nối đang hoạt động và kết thúc luồngLần chỉnh sửa cuối cùng
Source [IP: Port Number] → Destination [IP: Port Number]
5Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Chúng tôi cho phép người dùng chỉ định địa chỉ máy chủ và số cổng tại dòng lệnh và nhập 'q' tại bất kỳ điểm nào từ dòng lệnh để kết thúc chương trình
Bây giờ bạn có thể chạy tập lệnh máy chủ của mình
khách hàng phòng chat
Máy chủ không thực sự làm được gì nhiều nếu không có máy khách kết nối với nó. Hãy tạo một tệp
Source [IP: Port Number] → Destination [IP: Port Number]
49Source [IP: Port Number] → Destination [IP: Port Number]
7Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Trong khi tạo tập lệnh máy chủ, tôi hy vọng tôi đã giải thích rõ ràng phần lớn lý thuyết cho bạn. Các quy tắc tương tự được áp dụng ở đây, ngoại trừ chúng tôi có một chuỗi Gửi luôn lắng nghe đầu vào của người dùng từ dòng lệnh. Chúng tôi sẽ thêm GUI sau, nhưng nó tuân theo nhiều logic giống nhau. Mọi dữ liệu nhận được sẽ được hiển thị trên giao diện máy khách và mọi dữ liệu được gửi sẽ được máy chủ xử lý để truyền đến các máy khách được kết nối khác
Một lần nữa, chúng tôi sử dụng đa luồng. Lần này, nó là để cho các hoạt động gửi và nhận chạy song song với nhau. Bằng cách này, phòng trò chuyện của chúng tôi hoạt động theo thời gian thực [thay vì xen kẽ giữa các cuộc gọi
Source [IP: Port Number] → Destination [IP: Port Number]
97 và Source [IP: Port Number] → Destination [IP: Port Number]
09]Kết nối với máy chủ
Source [IP: Port Number] → Destination [IP: Port Number]
0Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Máy khách gọi phương thức
Source [IP: Port Number] → Destination [IP: Port Number]
52 để kết nối với một địa chỉ ổ cắm được chỉ định [một lần nữa, ở dạng Tuple] của máy chủ. Nếu ổ cắm được chỉ định chưa sẵn sàng nhận kết nối [e. g. bạn chưa chạy tập lệnh máy chủ], cuộc gọi Source [IP: Port Number] → Destination [IP: Port Number]
52 sẽ không thành côngLần chỉnh sửa cuối cùng
Source [IP: Port Number] → Destination [IP: Port Number]
0Vào chế độ toàn màn hình Thoát chế độ toàn màn hình
Kiểm tra ứng dụng dòng lệnh của chúng tôi
Những gì chúng tôi đã làm là xây dựng một phòng chat dòng lệnh hoạt động đầy đủ
Bạn có thể chạy tập lệnh máy chủ của mình bằng cách cung cấp cho nó bất kỳ giao diện IP nào của máy [bạn cũng có thể sử dụng tên máy chủ của mình. ]. Nếu bạn thích, bạn có thể chỉ cần sử dụng '127. 0. 0. 1’ hoặc ‘localhost’ nếu bạn chỉ thử nghiệm điều này trên một máy
Các quy tắc tương tự áp dụng cho tập lệnh máy khách. Mở một cửa sổ đầu cuối riêng biệt và chạy
Source [IP: Port Number] → Destination [IP: Port Number]
49Mở ba cửa sổ đầu cuối trở lên và bạn có thể có nhiều ứng dụng khách. Ngoài ra, lấy một máy khác được kết nối với cùng mạng LAN và chạy
Source [IP: Port Number] → Destination [IP: Port Number]
49 từ đó. Nếu nó nằm trong một mạng LAN khác, tường lửa của bạn có thể chặn nóTrong ảnh chụp màn hình ở trên, tôi đang chạy ba cửa sổ đầu cuối, với hai máy khách được kết nối với cùng một máy chủ
Tạo GUI
Chúng tôi vẫn chưa hoàn thành. Một ứng dụng dòng lệnh là tốt và tất cả, nhưng tạo một GUI thì sao?
Chúng tôi chỉ cần thực hiện một số điều chỉnh đối với
Source [IP: Port Number] → Destination [IP: Port Number]
49 của mình và thêm một số mã khác để tạo GUI. Bạn có thể tìm hiểu thêm về Tkinter bằng cách đọc tài liệu của nó, nhưng điều đó nằm ngoài phạm vi của hướng dẫn nàyMã
Source [IP: Port Number] → Destination [IP: Port Number]
49 hoàn chỉnh có sẵn bên dướivà để tham khảo, mã
Source [IP: Port Number] → Destination [IP: Port Number]
2 hoàn chỉnhSản phẩm cuối cùng
Chạy
Source [IP: Port Number] → Destination [IP: Port Number]
2 và Source [IP: Port Number] → Destination [IP: Port Number]
49 giống như bạn đã làm trước đây. Sau khi nhập tên của bạn, GUI sẽ bật lênTôi đã thiết kế cái này để bạn có thể sử dụng dòng lệnh hoặc GUI để tương tác với ứng dụng để gỡ lỗi dễ dàng hơn và xem những gì đang diễn ra đằng sau hậu trường
Sự kết luận
Đó là tất cả. Tôi hy vọng rằng bạn thích đọc nó nhiều như tôi đã thích viết nó. Thế giới mạng máy tính thực sự khá thú vị và đây chỉ là phần nổi của tảng băng chìm