Java. Giao thức HTTP và làm việc với WEB. Xử lý một datagram đến. Thiếu kiểm soát luồng trong UDP

35 câu trả lời

Bản tóm tắt

Ổ cắm TCP là một phiên bản của điểm cuối, được xác định bởi địa chỉ IP và cổng trong bối cảnh kết nối TCP hoặc trạng thái nghe cụ thể.

Cổng là mã định danh ảo hóa, chỉ định điểm cuối của dịch vụ (trái ngược với điểm cuối của phiên bản dịch vụ hoặc mã định danh phiên của nó).

Ổ cắm TCP không phải là kết nối, đây là điểm cuối của một kết nối cụ thể.

Có thể có các kết nối đồng thời tới điểm cuối dịch vụ, vì kết nối được xác định bởi cả điểm cuối cục bộ và điểm cuối từ xa, cho phép lưu lượng truy cập được định tuyến đến một phiên bản dịch vụ cụ thể.

Chỉ có thể có một ổ cắm trình nghe cho một tổ hợp địa chỉ và cổng nhất định.

Triển lãm

Đây là một câu hỏi thú vị khiến tôi phải xem xét lại một số điều mà tôi nghĩ rằng tôi đã biết từ trong ra ngoài. Bạn có thể nghĩ rằng một cái tên như "ổ cắm" sẽ được đặt: dường như được chọn để gợi lên hình ảnh của điểm cuối mà bạn cắm cáp mạng vào, ở đó có những điểm tương đồng về chức năng mạnh mẽ. Tuy nhiên, trong ngôn ngữ kết nối mạng, từ "ổ cắm" mang nhiều ý nghĩa đến mức cần phải xem xét lại cẩn thận.

Theo nghĩa rộng nhất, cảng là điểm vào hoặc ra. Từ porte trong tiếng Pháp, mặc dù không được sử dụng trong ngữ cảnh mạng, nhưng có nghĩa đen là cửa hoặc cổng, nhấn mạnh thêm thực tế rằng các cổng là điểm cuối vận chuyển, cho dù bạn đang gửi dữ liệu hay các thùng chứa thép lớn.

Với mục đích của cuộc thảo luận này, tôi sẽ giới hạn sự xem xét của mình trong bối cảnh của các mạng TCP-IP. Mô hình OSI rất tốt nhưng chưa bao giờ được triển khai đầy đủ, càng chưa được triển khai rộng rãi trong môi trường điện áp cao, lưu lượng truy cập cao.

Sự kết hợp giữa địa chỉ IP và cổng được gọi là điểm cuối và đôi khi được gọi là ổ cắm. Việc sử dụng này liên quan đến RFC793, đặc tả TCP gốc.

Kết nối TCP được xác định bởi hai điểm cuối hay còn gọi là ổ cắm.

Điểm cuối (ổ cắm) được xác định bằng sự kết hợp giữa địa chỉ mạng và ID cổng. Lưu ý rằng địa chỉ/cổng không xác định đầy đủ ổ cắm (sẽ nói thêm về điều này sau).

Mục đích của cổng là phân biệt giữa nhiều điểm cuối trên một địa chỉ mạng nhất định. Chúng ta có thể nói rằng cổng là một điểm cuối ảo hóa. Sự ảo hóa này giúp có thể thực hiện nhiều kết nối đồng thời trên một giao diện mạng.

Đây là một cặp ổ cắm (bộ 4 bao gồm địa chỉ IP máy khách, số cổng máy khách, địa chỉ IP máy chủ và số cổng máy chủ) chỉ định hai điểm cuối xác định duy nhất mỗi kết nối TCP với Internet. (TCP-IP minh họa Tập 1, W. Richard Stevens)

Trong hầu hết các ngôn ngữ dựa trên C, các kết nối TCP được thiết lập và xử lý bằng các phương thức trên một phiên bản của lớp Socket. Mặc dù thông thường nó hoạt động ở mức độ trừu tượng cao hơn, thường là một thể hiện của lớp NetworkStream, nhưng nó thường cung cấp một tham chiếu đến một đối tượng socket. Đối với bộ mã hóa, đối tượng socket này dường như đại diện cho một kết nối vì kết nối được tạo và quản lý bằng các phương thức của đối tượng socket.

Trong C#, để thiết lập kết nối TCP (với trình nghe hiện có), trước tiên bạn tạo TcpClient. Nếu bạn không chỉ định điểm cuối cho hàm tạo TcpClient, nó sẽ sử dụng các giá trị mặc định - bất kể điểm cuối cục bộ được xác định theo cách nào. Sau đó, bạn gọi phương thức Connect trên phiên bản đã tạo. Phương pháp này yêu cầu một tham số mô tả điểm cuối khác.

Điều này hơi khó hiểu và khiến bạn tin rằng ổ cắm là một kết nối và là một ổ khóa. Tôi đã hiểu lầm điều này cho đến khi Richard Dorman đặt câu hỏi.

Sau khi đọc và suy nghĩ rất nhiều, giờ đây tôi tin chắc rằng sẽ hợp lý hơn nhiều nếu có một lớp TcpConnection với một hàm tạo có hai đối số: LocalEndpoint và RemoteEndpoint. Bạn có thể hỗ trợ một đối số RemoteEndpoint duy nhất khi các giá trị mặc định cho điểm cuối cục bộ được chấp nhận. Điều này không rõ ràng trên các máy tính đa lõi, nhưng sự mơ hồ này có thể được giải quyết bằng cách sử dụng bảng định tuyến bằng cách chọn giao diện có tuyến đường ngắn nhất đến điểm cuối từ xa.

Sự rõ ràng cũng sẽ được tăng cường theo những cách khác. Ổ cắm không được xác định bởi sự kết hợp giữa địa chỉ IP và cổng:

[...] TCP phân tách các phân đoạn đến bằng cách sử dụng tất cả bốn giá trị chứa địa chỉ cục bộ và nước ngoài: địa chỉ IP đích, số cổng đích, địa chỉ IP nguồn và số cổng nguồn. TCP không thể xác định tiến trình nào đang nhận phân đoạn đến chỉ bằng cách nhìn vào cổng đích. Ngoài ra, điểm cuối duy nhất trong số [nhiều] điểm cuối tại [số cổng đã cho] sẽ chấp nhận yêu cầu kết nối đến là điểm cuối ở trạng thái lắng nghe. (tr255, Minh họa TCP-IP Tập 1, W. Richard Stevens)

Như bạn có thể thấy, không chỉ có thể mà còn có khả năng một dịch vụ mạng có nhiều ổ cắm có cùng địa chỉ/cổng nhưng chỉ có một ổ cắm người nghe tại một tổ hợp địa chỉ/cổng cụ thể. Việc triển khai điển hình của thư viện là một lớp socket, một phiên bản của lớp này được sử dụng để tạo và quản lý kết nối. Điều này cực kỳ đáng tiếc vì nó gây ra sự nhầm lẫn và dẫn đến sự nhầm lẫn rộng rãi giữa hai khái niệm này.

Khagrawal không tin tôi (xem bình luận), vì vậy đây là mẫu thực sự. Tôi đã kết nối trình duyệt web của mình với http://dilbert.com rồi chạy netstat -an -p tcp . Sáu dòng đầu ra cuối cùng chứa hai ví dụ về cách địa chỉ và cổng không đủ để nhận dạng duy nhất một ổ cắm. Có hai kết nối khác nhau giữa 192.168.1.3 (máy trạm của tôi) và 54.252.92.236:80

TCP 192.168.1.3:63240 54.252.94.236:80 SYN_SENT TCP 192.168.1.3:63241 54.252.94.236:80 SYN_SENT TCP 192.168.1.3:63242 207.38.110.62:80 _SENT TCP 192.168.1.3:63243 207.38.110.62:80 SYN_SENT TCP 192.168 .1.3:64161 65.54.225.168:443 THÀNH LẬP

Vì ổ cắm là điểm cuối của kết nối nên có hai ổ cắm có tổ hợp địa chỉ/cổng 207.38.110.62:80 và hai ổ cắm khác có tổ hợp địa chỉ/cổng 54.252.94.236:80 .

Tôi nghĩ sự hiểu lầm về Khagrawal xuất phát từ việc tôi sử dụng từ "xác định" rất cẩn thận. Ý tôi là "xác định hoàn toàn, duy nhất và duy nhất". Trong ví dụ trên, có hai điểm cuối có tổ hợp địa chỉ/cổng 54.252.94.236:80. Nếu bạn có địa chỉ và cổng, bạn không có đủ thông tin để phân tách các đầu nối đó. Không có đủ thông tin để xác định ổ cắm.

Phép cộng

Đoạn hai của phần 2.7 của RFC793 nêu rõ:

Kết nối được xác định hoàn toàn bằng một cặp ổ cắm ở hai đầu. Ổ cắm cục bộ có thể tham gia vào nhiều kết nối với nhiều ổ cắm nước ngoài khác nhau.

Định nghĩa socket này không hữu ích từ góc độ lập trình vì nó không giống với đối tượng socket, là điểm cuối cho một kết nối cụ thể. Đối với một lập trình viên và hầu hết đối tượng này là một lập trình viên, đây là một sự khác biệt quan trọng về mặt chức năng.

Liên kết

    Các giao thức minh họa TCP-IP Tập 1, W. Richard Stevens, 1994 Addison Wesley

    Ổ cắmđại diện cho một kết nối duy nhất giữa hai ứng dụng mạng. Hai ứng dụng này thường chạy trên các máy tính khác nhau, nhưng các ổ cắm cũng có thể được sử dụng để liên lạc giữa các tiến trình trên cùng một máy tính. Các ứng dụng có thể tạo nhiều socket để giao tiếp với nhau. Ổ cắm là hai chiều, nghĩa là cả hai phía của kết nối đều có khả năng gửi và nhận dữ liệu. Do đó, về mặt lý thuyết, một socket có thể được tạo ở bất kỳ cấp độ nào của mô hình OSI từ cấp 2 trở lên. Các lập trình viên thường sử dụng socket trong lập trình mạng, mặc dù là gián tiếp. Các thư viện lập trình như Winsock ẩn nhiều chi tiết cấp thấp của lập trình socket. Ổ cắm đã được sử dụng rộng rãi từ đầu những năm 1980.

    Hải cảngđại diện cho điểm cuối hoặc "kênh" cho giao tiếp mạng. Số cổng cho phép các ứng dụng khác nhau trên cùng một máy tính sử dụng tài nguyên mạng mà không can thiệp lẫn nhau. Số cổng phổ biến nhất trong lập trình mạng, đặc biệt là trong lập trình socket. Tuy nhiên, đôi khi số cổng sẽ hiển thị đối với người dùng thông thường. Ví dụ: một số trang web mà một người truy cập trên Internet sử dụng URL sau:

    Với một số tương tự

    Mặc dù đối với ổ cắm rất nhiều thông tin kỹ thuật đã được đưa ra chi tiết...Tôi muốn thêm câu trả lời của mình, để đề phòng, nếu ai đó vẫn không thể cảm nhận được sự khác biệt giữa ip, cổng và ổ cắm

    Hãy xem xét máy chủ S,

    và hãy nói người X, Y, Z cần một dịch vụ (giả sử là dịch vụ trò chuyện) từ đây may chủ

    Địa chỉ IP nóiAi? máy chủ trò chuyện "S" mà X, Y, Z muốn liên hệ

    được rồi, bạn hiểu "máy chủ là ai"

    nhưng giả sử máy chủ “S” cũng cung cấp một số dịch vụ khác cho người khác, giả sử “S” cung cấp dịch vụ lưu trữ cho người A, B, C

    cổng nói ---> cái mà? dịch vụ mà bạn (X, Y, Z) cần, tức là dịch vụ trò chuyện, không phải dịch vụ lưu trữ

    được rồi.. bạn cho máy chủ biết rằng bạn cần dịch vụ trò chuyện chứ không phải bộ nhớ

    bạn ba tuổi và máy chủ có thể muốn xác định cả ba tuổi theo cách khác nhau

    đến ổ cắm

    Hiện nay ổ cắm nóiCái mà? kết nối cụ thể

    nghĩa là, hãy nói

    ổ cắm 1 mỗi người X

    ổ cắm 2 cho người Y

    và 3 ổ cắm cho người Z

    Tôi hy vọng điều này sẽ giúp được ai đó vẫn còn bối rối :)

    Đầu tiên, tôi nghĩ chúng ta nên bắt đầu với một chút hiểu biết về những gì cần có để có được gói hàng từ A đến B.

    Một định nghĩa chung cho mạng là sử dụng mô hình OSI, mô hình này chia mạng thành nhiều lớp tùy theo mục đích của nó. Có một vài điều quan trọng mà chúng tôi sẽ đề cập ở đây:

    • Lớp liên kết dữ liệu. Lớp này chịu trách nhiệm nhận các gói dữ liệu từ thiết bị mạng này sang thiết bị mạng khác và nằm ngay phía trên lớp thực sự truyền. Nó nói về địa chỉ MAC và biết cách tìm máy chủ dựa trên địa chỉ MAC (phần cứng) của chúng, nhưng không có gì hơn thế.
    • Lớp mạng là lớp cho phép dữ liệu được vận chuyển qua các máy và vượt qua các ranh giới vật lý như thiết bị vật lý. Về cơ bản, lớp mạng phải hỗ trợ một cơ chế dựa trên địa chỉ bổ sung có liên quan đến địa chỉ vật lý; nhập địa chỉ IP (IPv4). Địa chỉ IP có thể nhận gói hàng của bạn từ A đến B qua Internet nhưng không biết gì về cách xử lý từng chuyến bay. Điều này được xử lý bởi lớp trên theo thông tin định tuyến.
    • Lớp vận chuyển. Lớp này chịu trách nhiệm xác định cách lấy thông tin từ A đến B và mọi hạn chế, kiểm tra hoặc lỗi đối với hành vi đó. Ví dụ: TCP thêm thông tin bổ sung vào gói để có thể suy ra nếu gói bị mất.

    TCP chứa đựng, trong số những thứ khác, khái niệm về cổng. Đây thực tế là các điểm cuối dữ liệu khác nhau trên cùng một địa chỉ IP mà Ổ cắm Internet (AF_INET) có thể liên kết.

    Câu trả lời ngắn gọn.

    MỘT Hải cảng có thể được mô tả như địa chỉ nội bộ bên trong một máy chủ xác định một chương trình hoặc quy trình.

    MỘT ổ cắm có thể được mô tả như giao diện phần mềm, cho phép một chương trình giao tiếp với các chương trình hoặc quy trình khác, trực tuyến hoặc cục bộ.

    Thông thường, bạn sẽ nhận được rất nhiều nội dung mang tính lý thuyết, nhưng một trong những cách dễ nhất để phân biệt giữa hai nội dung này là:

    Để nhận được dịch vụ, bạn cần có số dịch vụ. Số dịch vụ này được gọi là cổng. Giống như cái đó.

    Ví dụ: HTTP dưới dạng dịch vụ chạy trên cổng 80.

    Bây giờ nhiều người có thể yêu cầu dịch vụ và kết nối máy khách-máy chủ được thiết lập. Sẽ có nhiều kết nối. Mỗi kết nối đại diện cho một khách hàng. Để hỗ trợ mỗi kết nối, máy chủ tạo một ổ cắm cho mỗi kết nối để hỗ trợ máy khách của nó.

    Dường như có rất nhiều câu trả lời so sánh ổ cắm với kết nối giữa hai PC. Tôi nghĩ điều này hoàn toàn sai. Ổ cắm luôn là điểm cuối trên 1 PC, có thể được kết nối hoặc không - tất nhiên tại một số thời điểm, tất cả chúng ta đều đã sử dụng ổ cắm bộ thu hoặc UDP*. Phần quan trọng là nó được nhắm mục tiêu và hoạt động. Việc gửi tin nhắn tới tệp 1.1.1.1:1234 khó có thể hoạt động vì không có ổ cắm cho điểm cuối đó.

    Các ổ cắm là giao thức cụ thể - vì vậy việc triển khai tính duy nhất là TCP / và UDP / sử dụng * (ipaddress: port), khác với, ví dụ: IPX (Mạng, Nút và ... trò chơi, ổ cắm - nhưng một ổ cắm khác với bên dưới thuật ngữ chung "số ổ cắm" IPX tương đương với cổng IP). Nhưng tất cả đều cung cấp một điểm cuối có thể định địa chỉ duy nhất.

    Khi IP đã trở thành giao thức thống trị, một cổng (theo thuật ngữ mạng) đã trở thành số ít với số cổng UDP hoặc TCP là một phần của địa chỉ ổ cắm.

    • UDP là kết nối trung lập - điều này có nghĩa là một mạch ảo không bao giờ được tạo giữa hai điểm cuối. Tuy nhiên, với tư cách là điểm cuối, chúng tôi vẫn đề cập đến ổ cắm UDP. Các hàm API làm rõ rằng cả hai đều chỉ là các loại ổ cắm khác nhau. SOCK_DGRAM là UDP (chỉ gửi tin nhắn) và SOCK_STREAM là TCP (tạo mạch ảo).

      Về mặt kỹ thuật, tiêu đề IP chứa địa chỉ IP và giao thức qua IP (UDP hoặc TCP) chứa số cổng. Điều này cho phép sử dụng các giao thức khác (chẳng hạn như ICMP, không có số cổng nhưng có thông tin địa chỉ IP).

      Đây là các thuật ngữ thuộc hai miền khác nhau: "port" là khái niệm từ mạng TCP/IP, "socket" là API (lập trình). Một "ổ cắm" được tạo (bằng mã) bằng cách lấy cổng, tên máy chủ hoặc bộ điều hợp mạng và kết hợp chúng thành cấu trúc dữ liệu mà bạn có thể sử dụng để gửi hoặc nhận dữ liệu.

      Kết nối TCP-IP là các đường dẫn hai chiều kết nối một địa chỉ: tổ hợp cổng với địa chỉ khác: tổ hợp cổng. Vì vậy, bất cứ khi nào bạn mở kết nối từ máy cục bộ của mình đến một cổng trên máy chủ từ xa (ví dụ: www.google.com:80), bạn cũng liên kết số cổng mới trên máy của mình với kết nối để máy chủ có thể gửi mọi thứ trở lại bạn (ví dụ 127.0.0.1:65234). Sẽ rất hữu ích khi sử dụng netstat để xem các kết nối của bạn với máy tính:

      > netstat -nWp tcp (trên OS X) Kết nối Internet đang hoạt động Proto Recv-Q Send-Q Địa chỉ cục bộ Địa chỉ nước ngoài (trạng thái) tcp4 0 0 192.168.0.6.49871 17.172.232.57.5223 THÀNH LẬP ...

      Ổ cắm là một loại bộ mô tả tệp đặc biệt được một quy trình sử dụng để yêu cầu các dịch vụ mạng từ hệ điều hành. Địa chỉ socket là một bộ ba: (giao thức, địa chỉ cục bộ, quy trình cục bộ), trong đó quy trình cục bộ được xác định bằng số cổng.

      Ví dụ: trong bộ TCP/IP:

      (tcp, 193.44.234.3, 12345)

      Cuộc trò chuyện là một đường dây liên lạc giữa hai quá trình, do đó mô tả mối liên hệ giữa hai quá trình. Một liên kết là một bộ gồm 5 bộ xác định hoàn toàn hai quy trình có chứa kết nối: (giao thức, địa chỉ cục bộ, quy trình cục bộ, địa chỉ nước ngoài, quy trình nước ngoài)

      Ví dụ: trong bộ TCP/IP:

      (tcp, 193.44.234.3, 1500, 193.44.234.5, 21)

      có thể là một hiệp hội hợp lệ.

      Bán liên kết: (giao thức, địa chỉ cục bộ, quy trình cục bộ)

      (giao thức, địa chỉ nước ngoài, quy trình nước ngoài)

      xác định từng nửa của kết nối.

      Một nửa liên kết còn được gọi là ổ cắm hoặc địa chỉ truyền tải. Nghĩa là, socket là điểm cuối cho giao tiếp có thể được đặt tên và đánh địa chỉ trên mạng. Giao diện ổ cắm là một trong một số giao diện lập trình ứng dụng (API) cho các giao thức truyền thông. Được thiết kế như một giao diện lập trình truyền thông phổ quát, nó được giới thiệu lần đầu tiên bởi hệ thống UNIX 4.2BSD. Mặc dù nó không được chuẩn hóa nhưng trên thực tế nó đã trở thành tiêu chuẩn của ngành.

      Cổng là phần dễ dàng, nó chỉ đơn giản là mã định danh duy nhất cho ổ cắm. Ổ cắm là thứ mà các tiến trình có thể sử dụng để thiết lập kết nối và liên lạc với nhau. Tall Jeff có một ví dụ tương tự về điện thoại nhưng chưa hoàn hảo nên tôi quyết định sửa nó:

      Một ứng dụng bao gồm một cặp quy trình giao tiếp qua mạng (cặp máy khách-máy chủ). Các quy trình này gửi và nhận tin nhắn đến và từ mạng thông qua giao diện lập trình ổ cắm. Xem xét sự tương tự được trình bày trong cuốn sách "Mạng máy tính: Cách tiếp cận từ trên xuống". Có một ngôi nhà muốn giao tiếp với một ngôi nhà khác. Ở đây ngôi nhà cũng tương tự như quy trình và cửa ra vào. Quá trình gửi giả định rằng có cơ sở hạ tầng ở phía bên kia cánh cửa sẽ truyền dữ liệu đến đích. Khi tin nhắn đến từ phía bên kia, nó sẽ đi qua cửa nhận (ổ cắm) vào nhà (quy trình). Hình minh họa này từ cùng một cuốn sách có thể giúp bạn:

      Ổ cắm là một phần của lớp vận chuyển, cung cấp giao tiếp logic với các ứng dụng. Điều này có nghĩa là từ góc độ ứng dụng, cả hai nút đều được kết nối trực tiếp với nhau, mặc dù có nhiều bộ định tuyến và/hoặc chuyển mạch giữa chúng. Vì vậy, ổ cắm không phải là kết nối mà là điểm cuối của kết nối. Các giao thức lớp vận chuyển chỉ được thực hiện trên các máy chủ chứ không phải trên các bộ định tuyến trung gian.
      Cổng cung cấp một phương tiện đánh địa chỉ nội bộ cho máy. Mục tiêu chính là cho phép nhiều quy trình gửi và nhận dữ liệu qua mạng mà không can thiệp vào các quy trình khác (dữ liệu của chúng). Tất cả các ổ cắm đều được cung cấp một số cổng. Khi một phân đoạn đến máy chủ, lớp vận chuyển sẽ kiểm tra số cổng đích của phân đoạn đó. Sau đó nó chuyển đoạn này tới socket thích hợp. Nhiệm vụ cung cấp dữ liệu trên phân đoạn lớp vận chuyển đến đúng ổ cắm được gọi là giảm chấn. Dữ liệu phân đoạn sau đó được chuyển đến quy trình được gắn vào ổ cắm.

      Ổ cắm là cấu trúc của phần mềm của bạn. Nó ít nhiều là một tập tin; nó có các hoạt động như đọc và viết. Nó không phải là một thứ vật chất; đó là một cách để phần mềm của bạn tham chiếu những thứ vật lý.

      Cổng là một thứ giống như thiết bị. Mỗi máy chủ có một hoặc nhiều mạng (vật lý); máy chủ có một địa chỉ trên mỗi mạng. Mỗi địa chỉ có thể có hàng nghìn cổng.

      Chỉ có một ổ cắm có thể sử dụng một cổng tại một địa chỉ. Ổ cắm phân bổ một cổng giống như phân bổ thiết bị cho I/O hệ thống tệp. Khi một cổng được phân bổ, không ổ cắm nào khác có thể kết nối với cổng đó. Cổng sẽ được giải phóng khi đóng ổ cắm.

      Ổ cắm là một điểm cuối của đường liên lạc hai chiều giữa hai chương trình đang chạy trên mạng. Ổ cắm được liên kết với một số cổng để lớp TCP có thể xác định ứng dụng mà dữ liệu dự định được gửi đến.

      Thuật ngữ TCP/IP tương đối mà tôi giả sử ngụ ý câu hỏi này. Trong điều khoản của Giáo dân:

      PORT là số điện thoại của một ngôi nhà cụ thể theo mã zip cụ thể. Mã bưu chính của một thành phố có thể được coi là địa chỉ IP của một thành phố và tất cả các ngôi nhà trong thành phố đó.

      Mặt khác, SOCKET giống như một cuộc gọi điện thoại được thiết lập giữa các điện thoại của một cặp nhà đang nói chuyện với nhau. Những cuộc gọi này có thể được thiết lập giữa các ngôi nhà trong cùng một thành phố hoặc hai ngôi nhà ở các thành phố khác nhau. Đường dẫn được thiết lập tạm thời giữa hai điện thoại nói chuyện với nhau được gọi là SOCKET.

      Cổng và ổ cắm có thể được so sánh với một chi nhánh ngân hàng.

      Số tòa nhà của Ngân hàng tương tự như địa chỉ IP. Ngân hàng có nhiều bộ phận khác nhau như:

      1. Phòng tài khoản tiết kiệm
      2. Phòng cho vay cá nhân
      3. Phòng cho vay thế chấp
      4. Phòng khiếu nại

      Như vậy, 1 (Phòng Tài khoản Tiết kiệm), 2 (Phòng Cho vay Cá nhân), 3 (Phòng Cho vay Mua nhà) và 4 (Phòng Giải quyết Khiếu nại) là các cổng.

      Bây giờ hãy để chúng tôi kể cho bạn rằng bạn đi mở tài khoản tiết kiệm, bạn đến ngân hàng (địa chỉ IP), sau đó bạn đến "bộ phận tài khoản tiết kiệm" (cổng số 1), sau đó bạn gặp một trong những nhân viên làm việc trong "tài khoản tiết kiệm phòng ban" ". Hãy gọi nó là SAVINGACCOUNT_EMPLOYEE1 để mở tài khoản.

      SAVINGACCOUNT_EMPLOYEE1 là tay cầm ổ cắm của bạn, do đó có thể từ SAVINGACCOUNT_EMPLOYEE1 đến SAVINGACCOUNT_EMPLOYEEN. Đây là tất cả các mô tả ổ cắm.

      Tương tự như vậy, các bộ phận khác sẽ có công việc dưới quyền của họ và chúng cũng tương tự như socket.

      Ổ cắm là điểm cuối của giao tiếp. Ổ cắm này không liên quan trực tiếp đến họ giao thức TCP/IP; nó có thể được sử dụng với bất kỳ giao thức nào được hệ thống của bạn hỗ trợ. API Ổ cắm C mong muốn trước tiên bạn nhận được một đối tượng ổ cắm trống từ hệ thống, sau đó có thể được liên kết với một địa chỉ ổ cắm cục bộ (để nhận trực tiếp lưu lượng truy cập đến cho các giao thức không kết nối hoặc chấp nhận các yêu cầu kết nối đến cho các giao thức hướng kết nối) hoặc bạn có thể kết nối với địa chỉ ổ cắm từ xa (đối với mọi loại giao thức). Bạn thậm chí có thể thực hiện cả hai nếu bạn muốn kiểm soát cả hai: địa chỉ ổ cắm cục bộ mà ổ cắm được liên kết và địa chỉ ổ cắm từ xa mà ổ cắm được liên kết. Đối với các giao thức không kết nối, việc kết nối ổ cắm thậm chí không cần thiết, nhưng nếu không, bạn cũng sẽ phải chuyển địa chỉ đích với mỗi gói bạn muốn gửi qua ổ cắm, nếu không thì ổ cắm sẽ biết nơi để gửi dữ liệu đó đến? Ưu điểm là bạn có thể sử dụng một ổ cắm để gửi các gói đến các địa chỉ ổ cắm khác nhau. Khi bạn đã định cấu hình ổ cắm và thậm chí có thể kết nối nó, hãy coi đó là kênh liên lạc hai chiều. Bạn có thể sử dụng nó để truyền dữ liệu đến một số đích và một đích khác có thể sử dụng nó để truyền dữ liệu cho bạn. Những gì bạn ghi vào ổ cắm sẽ được gửi và những gì nhận được đều có thể đọc được.

      Mặt khác, cổng là thứ mà chỉ một số giao thức nhất định trong ngăn xếp giao thức TCP/IP mới có. Các gói TCP và UDP đều có cổng. Cổng chỉ là một con số. Sự kết hợp giữa cổng nguồn và cổng đích xác định kênh liên lạc giữa hai máy chủ. Ví dụ: bạn có thể có một máy chủ cần vừa là máy chủ HTTP đơn giản vừa là máy chủ FTP đơn giản. Nếu bây giờ một gói đến địa chỉ của máy chủ này, làm thế nào để biết đó là gói dành cho máy chủ HTTP hay máy chủ FTP? Chà, nó sẽ biết vì máy chủ HTTP sẽ chạy trên cổng 80 và máy chủ FTP sẽ ở cổng 21, vì vậy nếu một gói đến với cổng đích 80, nó sẽ được dành cho máy chủ HTTP chứ không phải máy chủ FTP. Ngoài ra, gói có cổng nguồn vì không có cổng nguồn như vậy, máy chủ chỉ có thể có một kết nối đến một địa chỉ IP tại một thời điểm. Cổng nguồn cho phép máy chủ phân biệt giữa các kết nối giống hệt nhau: chúng đều có cùng một cổng đích, ví dụ cổng 80, cùng một địa chỉ IP đích, luôn cùng một địa chỉ máy chủ và cùng một địa chỉ IP nguồn vì chúng đều đến từ cùng một máy khách , nhưng vì chúng có các cổng nguồn khác nhau nên máy chủ có thể phân biệt chúng. Và khi máy chủ gửi lại phản hồi, nó sẽ thực hiện phản hồi trên cổng xuất phát yêu cầu, bằng cách này, khách hàng cũng có thể phân biệt giữa các phản hồi khác nhau mà nó nhận được.

      Một cổng có thể có một hoặc nhiều giắc cắm được kết nối với một IP bên ngoài khác, chẳng hạn như nhiều ổ cắm.

      TCP 192.168.100.2:9001 155.94.246.179:39255 THÀNH LẬP 1312 TCP 192.168.100.2:9001 171.25.193.9:61832 THÀNH LẬP 1312 TCP 192.168.100.2:90 01 78.62.199.226:37912 THÀNH LẬP 1312 TCP 192.168.100.2:9001 188.193.64.150: 40900 THÀNH LẬP 1312 TCP 192.168.100.2:9001 198.23.194.149:43970 THÀNH LẬP 1312 TCP 192.168.100.2:9001 198.49.73.11:38842 THÀNH LẬP 1312

      Ổ cắm là một bản tóm tắt được hạt nhân cung cấp cho các ứng dụng của người dùng để nhập/xuất dữ liệu. Loại ổ cắm được xác định bởi giao thức xử lý, giao tiếp IPC, v.v. Vì vậy, nếu ai đó tạo một ổ cắm TCP, anh ta có thể thực hiện các thao tác như đọc dữ liệu vào ổ cắm và ghi dữ liệu vào nó bằng các phương pháp đơn giản và xử lý giao thức lớp thấp hơn như dịch TCP và chuyển tiếp gói đến các giao thức mạng thấp hơn được thực hiện bằng cách triển khai ổ cắm cụ thể trong kernel . Ưu điểm là người dùng không phải lo lắng về cách xử lý các giao thức cụ thể và có thể chỉ cần đọc và ghi dữ liệu vào socket giống như một bộ đệm thông thường. Điều tương tự cũng đúng trong trường hợp IPC, người dùng chỉ cần đọc và ghi dữ liệu vào socket và kernel xử lý tất cả các chi tiết cấp thấp hơn tùy thuộc vào loại socket được tạo.

    Socket vs Socket phần 2, hay nói “không” với giao thức TCP - Archive WASM.RU

    Trong phần đầu tiên, dành cho những kiến ​​thức cơ bản về cách sử dụng socket MSWindows trong các chương trình hợp ngữ, chúng ta đã nói về socket là gì, chúng được tạo ra như thế nào và những tham số nào được chỉ định. Đồng thời, nó đã được đề cập khi chuyển giao thức UDP không hướng kết nối, giao thức này không đảm bảo việc phân phối các gói cũng như thứ tự chúng đến đích. Ví dụ hướng dẫn sau đó sử dụng giao thức TCP yêu thích của chúng tôi. Và mọi thứ với chúng tôi đều ổn, nhưng cuối cùng vẫn có một số câu hỏi chưa được giải quyết, đặc biệt là cách tổ chức trao đổi lẫn nhau giữa nhiều máy tính trên mạng, cách chuyển một thứ gì đó sang nhiều máy tính cùng một lúc, v.v.

    Nói chung, không cần thiết phải đọc phần đầu tiên để hiểu phần hiện tại, mặc dù tôi sẽ liên tục tham khảo nó trong suốt quá trình. Vì vậy, nó đi. Haha...

    Vì vậy, chúng tôi đặt ra một vấn đề: chúng tôi có một mạng cục bộ gồm hàng tá máy tính, chúng tôi cần tổ chức trao đổi tin nhắn giữa hai máy tính bất kỳ trong số chúng và (tùy chọn) giữa một máy tính và tất cả những máy tính khác.

    Tôi nghe thấy, tôi nghe thấy một loạt lời nhắc có nội dung, hãy sử dụng các tính năng tích hợp sẵn của Windows, như:

    net gửi 192.168.0.4 Zhenya gửi lời chào đến bạn!

    net gửi Node4 Đang chờ câu trả lời của bạn!

    Chỉ có hai sự phản đối về điều này. Đầu tiên, bạn không bao giờ biết hệ điều hành hoặc các chương trình làm sẵn khác của chúng tôi có thể làm gì, chúng tôi muốn học cách viết chương trình của riêng mình, phải không? Và thứ hai, thực tế không phải là tin nhắn được truyền từ người này sang người khác. Trong trường hợp tổng quát, người điều hành có thể không biết gì cả... Hoặc thậm chí không nên biết gì cả...

    Đối với tôi, điều quan trọng nhất khi thiết lập nhiệm vụ này là đảm bảo khả năng chuyển một thứ gì đó đến tất cả các máy tính trên mạng cùng một lúc. Hãy tưởng tượng rằng chúng tôi đã viết một chương trình nào đó... Ai đã nói - một Trojan? Không, không và KHÔNG! Không có Trojan. Ví dụ: chỉ là một chương trình kế toán nhỏ (rất). Sau một thời gian, nó đã có thể hoạt động trên nhiều máy tính trong mạng cục bộ của chúng tôi. Và giờ đã đến, đã đến lúc phải cân đối số dư, có thể nói là tổng hợp kết quả của quý... Mọi việc phải được thực hiện nhanh chóng và tốt nhất là cùng một lúc. Vẫn chưa rõ cách thực hiện điều này trong khuôn khổ tài liệu mà chúng tôi đã nghiên cứu ở phần đầu tiên.

    Câu trả lời, như mọi khi, đến từ WindowsAPI. Chúng tôi tìm kiếm và tìm thấy. Chức năng gửi đến() – gửi dữ liệu đến địa chỉ được chỉ định. Vậy thì nó có gì khác biệt so với hàm đã được nghiên cứu ở phần đầu tiên? gửi() ? Hoá ra là thế gửi đến() có thể phát sóng đến một địa chỉ IP đặc biệt. Tuy nhiên, xin lưu ý, điều này chỉ hoạt động đối với các ổ cắm thuộc loại SOCK_DGRAM! Và các ổ cắm được mở bằng giá trị SOCK_DGRAM làm tham số loại ổ cắm hoạt động thông qua giao thức UDP chứ không phải TCP! Điều này làm rõ ý nghĩa phụ đề của bài viết này... Tất nhiên, đây chỉ là một thủ đoạn văn chương, không có giao thức nào tốt hơn hay tệ hơn giao thức nào, chúng chỉ là... khác nhau, thế thôi. Mặc dù cả hai đều là giao thức lớp vận chuyển “...cung cấp khả năng truyền dữ liệu giữa các quy trình ứng dụng.” Cả hai đều sử dụng giao thức lớp mạng như IP để truyền (nhận) dữ liệu. Thông qua đó chúng (dữ liệu) sẽ chuyển sang cấp độ vật lý, tức là. trên đường truyền Thứ Tư... Và hôm nay là thứ Tư như thế nào, ai biết được. Có thể đó là cáp đồng, hoặc có thể không phải thứ Tư mà là thứ Năm, không phải cáp đồng mà là phát sóng...

    Sơ đồ tương tác của các giao thức mạng.

    UDPbạn ser Dđảo chữ P rotocol

    TCP-T giải phóng Cđiều khiển P rotocol

    ICMP-I Internet Cđiều khiển M tiểu luận P rotocol (giao thức trao đổi tin nhắn điều khiển)

    ARPMỘTđịa chỉ R giải pháp P rotocol (giao thức khám phá địa chỉ)

    Nói chung, nếu bản vẽ không giúp ích được gì cho bạn thì cũng không thành vấn đề. Điều quan trọng là phải hiểu một điều rằng TCP là giao thức lớp vận chuyển cung cấp đáng tin cậy vận chuyển dữ liệu giữa các tiến trình ứng dụng bằng cách thiết lập kết nối logic (nhấn mạnh của tôi). Nhưng UDP thì không. Và xa hơn. Ở đâu đó, ở cấp ứng dụng, tại một trong những hình chữ nhật trống, ứng dụng của chúng ta sẽ được đặt.

    Hãy kết thúc phần giới thiệu ở đây và chuyển sang xem cách sử dụng nó ngay từ đầu.

    Để trình diễn tất cả tài liệu, như thường lệ, một ví dụ đào tạo được sử dụng, có thể tải xuống< >. Chúng tôi bỏ qua phần chung cho tất cả các ứng dụng Windows và chỉ mô tả những gì liên quan đến hoạt động của socket. Đầu tiên bạn cần khởi tạo Windows Sockets DLL bằng hàm WSAKhởi động() , sẽ trả về 0 nếu thành công hoặc nếu không thì là một trong các mã lỗi. Sau đó, khi khởi tạo cửa sổ ứng dụng chính, hãy mở socket để nhận tin nhắn:

      gọi ổ cắm, AF_INET, \

      SOCK_DGRAM, \ ; chỉ định loại ổ cắm - giao thức UDP!

      0 ; loại giao thức

      Nếu eax != INVALID_SOCKET ; nếu không có lỗi

      mov hSocket, eax ; nhớ xử lý

    Sau đó, như thường lệ, chúng ta cần yêu cầu Windows gửi tin nhắn đến cửa sổ được chỉ định từ ổ cắm mà chúng ta đã mở:

      gọi WSAAsyncSelect, hSocket, hWnd, WM_SOCKET, FD_READ

    Ở đâu hSocket- mô tả ổ cắm
    hWnd- xử lý cửa sổ mà thông báo thủ tục sẽ được gửi tới
    WM_SOCKET- tin nhắn, được chúng tôi xác định trong phần.const
    FD_READ– một mặt nạ chỉ định các sự kiện mà chúng tôi quan tâm, trong trường hợp này đó là sự sẵn sàng của dữ liệu từ ổ cắm để đọc.

    Tôi nghe, tôi nghe thấy một điệp khúc ngạc nhiên với giọng tuyệt vọng: họ hứa về một ứng dụng ẩn, nhưng đây là cửa sổ chính và tất cả những thứ đó... Thực tế là bạn không thể làm gì nếu không có nó, bởi vì... Hệ điều hành gửi tất cả tin nhắn đến ứng dụng của chúng ta thông qua thủ tục cửa sổ của nó. Giải pháp rất đơn giản. Nếu cần, hãy ẩn cửa sổ ứng dụng quan trọng nhất này. Làm sao? Ví dụ: nhận xét dòng:

      gọi ShowWindow, hwnd, SW_SHOWNORMAL

    hoặc chính xác hơn là sử dụng:

      gọi ShowWindow, hwnd, SW_HIDE

    Sau đó, ứng dụng của chúng tôi cũng sẽ khởi động, cửa sổ chính sẽ được tạo, một thông báo WM_CREATE sẽ được gửi tới nó từ Windows với tất cả các hậu quả... Chỉ có cửa sổ của nó sẽ không hiển thị trên màn hình nền hoặc trên thanh tác vụ. Nếu đây là điều bạn mong muốn thì tôi rất vui. Dù sao đi nữa, chúng ta hãy tiếp tục...

    Để thực hiện việc này, chúng tôi chuyển đổi số cổng thành thứ tự byte mạng bằng hàm API đặc biệt:

      gọi htons, Cổng

      mov tội lỗi.sin_port, rìu

      mov sin.sin_family, AF_INET

      mov tội lỗi.sin_addr, INADDR_ANY

    Lạc đề trữ tình một chút, không cần thiết phải hiểu ý bài này .

    Số cổng cho ổ cắm của chúng tôi đã được thảo luận ở cuối phần một. Thật khó để đưa ra khuyến nghị về những gì họ nên làm. Điều duy nhất có thể nói là họ không thể như vậy. Sẽ là không khôn ngoan nếu cố gắng sử dụng số cổng được xác định cho các dịch vụ được sử dụng rộng rãi như:

    thông qua giao thức TCP: 20, 21 – ftp; 23 – telnet; 25 – smtp; 80 – http; 139 - Dịch vụ phiên NetBIOS;

    thông qua giao thức UDP: 53 – DNS; 137, 138 – NetBIOS; 161 – SNMP;

    Tất nhiên, API có chức năng đặc biệt getservbyport() , với số cổng sẽ trả về tên của dịch vụ tương ứng. Chính xác hơn, bản thân hàm trả về một con trỏ tới một cấu trúc, bên trong cấu trúc đó có một con trỏ tới tên này...

    Bạn có thể gọi nó như thế này:

      gọi htons, Cảng; chuyển đổi số cổng thành thứ tự byte mạng

      gọi getservbyport, ax, 0;

    Lưu ý những gì Tài liệu tham khảo của lập trình viên Win32 nói về getservbyport:

    “...trả về một con trỏ tới một cấu trúc được phân phối bởi Windows Sockets. Ứng dụng không bao giờ nên cố gắng sửa đổi cấu trúc này hoặc bất kỳ thành phần nào của nó. Ngoài ra, chỉ có một bản sao của cấu trúc này được phân bổ chochảy, vì vậy ứng dụng phải sao chép mọi thông tin nó yêu cầu trước bất kỳ lệnh gọi nào khác đến chức năng Windows Sockets."

    Và đây là cấu trúc của chính nó:

    1. s_name DWORD ?; con trỏ tới một chuỗi có tên dịch vụ

      s_bí danh DWORD ?;

      s_port WORD ?; số cổng

      s_proto DWORD ?;

    API cũng có chức năng “ghép nối”, có thể nói: getservbyname(), dựa trên tên dịch vụ, sẽ trả về thông tin về số cổng được sử dụng.

    Thật không may, chúng ta sẽ không thể thu được bất kỳ lợi ích thiết thực nào từ những chức năng này. Vì vậy, hãy biết rằng chúng tồn tại và quên chúng đi...

      gọi liên kết, hSocket, addr sin, sizeof sin

      Nếu eax == SOCKET_ERROR; nếu có lỗi

      gọi MessageBox, NULL, addr ...

    Tại thời điểm này, công việc chuẩn bị tạo và cấu hình socket nhận bằng datagram có thể được coi là hoàn thành. Không cần thiết lập ổ cắm để nghe trên cổng bằng chức năng gọi Nghe, như chúng ta đã làm với socket SOCK_STREAM trong phần đầu tiên. Bây giờ, trong quy trình cửa sổ chính của ứng dụng, chúng ta có thể thêm mã sẽ được thực thi khi có thông báo WM_SOCKET đến từ ổ cắm:

      ; nếu nhận được tin nhắn từ ổ cắm (hSocket)

      Khác nếu uMsg == WM_SOCKET

    1. Nếu ax == FD_READ;

    2. Nếu ax == NULL ; không có lỗi

      ; nhận dữ liệu (64 byte) từ ổ cắm vào bộ đệm BytRecu

      gọi recv, hSocket, addr BytRecu, 64, 0;

    Bây giờ hãy nói về cách mở ổ cắm để gửi tin nhắn. Dưới đây là tất cả các hành động cần thiết của chương trình:

      gọi ổ cắm, AF_INET, SOCK_DGRAM, 0

        gọi htons, Cổng

        mov sin_to.sin_port, rìu

        mov sin_to.sin_family, AF_INET

        gọi inet_addr, addr Địa chỉIP

        mov sin_to.sin_addr, eax

      Khi chuyển dữ liệu, tất cả những gì bạn cần làm là:

        gọi sendto, hSocket1, addr BytSend1, 64, 0, \

        addr sin_to, kích thước của sin_to

      Các giá trị tham số khi gọi hàm API này như sau:

      hSocket1- xử lý ổ cắm đã mở trước đó
      addrBytSend1- địa chỉ của bộ đệm chứa dữ liệu để truyền
      64 - kích thước của dữ liệu trong bộ đệm, tính bằng byte
      0 - chỉ báo..., trong ví dụ MSDN nó chỉ là 0
      địa chỉ_to- con trỏ tới cấu trúc chứa địa chỉ đích
      sizeofsin_to– kích thước của cấu trúc này tính bằng byte.

      Nếu khi thực hiện một hàm gửi đến() không có lỗi xảy ra thì nó trả về số byte được truyền, nếu không thì đầu ra là SOCKET_ERROR trong eax.

      Bây giờ là lúc nói về địa chỉ quảng bá đã được đề cập ở phần đầu. Trong cấu trúc chúng tôi đã điền trước vào trường địa chỉ IP đích, cho biết trên thực tế, nơi sẽ gửi dữ liệu. Nếu địa chỉ này là 127.0.0.1 thì đương nhiên dữ liệu của chúng ta sẽ không đi đâu xa hơn máy tính của chúng ta. Tài liệu nêu rõ rằng gói được gửi tới mạng có địa chỉ 127.x.x.x sẽ không được truyền trên bất kỳ mạng nào. Hơn nữa, bộ định tuyến hoặc cổng không bao giờ được truyền thông tin định tuyến cho mạng số 127 - địa chỉ này không phải là địa chỉ mạng. Để gửi “truyền” tới tất cả các máy tính trên mạng cục bộ cùng một lúc, bạn cần sử dụng một địa chỉ được hình thành từ địa chỉ IP của chính chúng tôi, nhưng với tất cả các địa chỉ ở octet thấp, chẳng hạn như 192.168.0.255.

      Thực ra đó là tất cả. Khi chương trình đóng, bạn cần đóng các ổ cắm và giải phóng tài nguyên DLL của Ổ cắm; việc này được thực hiện đơn giản:

        gọi Closesocket, hSocket

        gọi closesocket, hSocket1

        gọi WSACleanup

      Đối với các ứng dụng đa luồng sau WSADọn dẹp hoạt động socket được hoàn thành cho tất cả các chủ đề.

      Phần khó nhất của bài viết này đối với tôi là quyết định cách tốt nhất để minh họa việc sử dụng API Windows Sockets. Có thể bạn đã từng thấy một cách tiếp cận, khi cả ổ cắm để nhận và ổ cắm để gửi tin nhắn đều được sử dụng đồng thời trong một ứng dụng. Một phương pháp khác có vẻ không kém phần hấp dẫn, khi mã của cái này và cái kia được phân tách rõ ràng, ngay cả những gì tồn tại trong các ứng dụng khác nhau. Cuối cùng, tôi cũng đã triển khai phương pháp này, phương pháp này có thể dễ hiểu hơn một chút đối với người mới bắt đầu. Trong lần thứ hai<архиве

      Không có chức năng này gửi() sẽ tạo ra SOCKET_ERROR!

      Cuối cùng, chúng ta có thể lưu ý một số vấn đề thường gặp phát sinh khi làm việc với socket. Để xử lý thông báo cửa sổ cho biết trạng thái của socket đã thay đổi, chúng tôi sử dụng tin nhắn trực tiếp từ Windows đến cửa sổ ứng dụng chính như thường lệ. Có một cách tiếp cận khác khi tạo các cửa sổ riêng cho từng socket.

      Nói chung, việc xử lý tin nhắn tập trung bằng cửa sổ chính là một phương pháp có vẻ dễ hiểu hơn nhưng vẫn có thể gây rắc rối trong thực tế. Nếu một chương trình đang sử dụng nhiều ổ cắm cùng lúc, thì chương trình đó cần lưu trữ danh sách các bộ mô tả ổ cắm. Khi một thông báo từ các ổ cắm xuất hiện, quy trình cửa sổ chính trong danh sách sẽ tìm kiếm thông tin liên quan đến bộ mô tả ổ cắm đó và gửi thêm thông báo thay đổi trạng thái tới quy trình dành cho việc này. Cái nào đã phản ứng theo cách này hay cách khác, thực hiện điều gì đó ở đó... Cách tiếp cận này buộc việc xử lý các tác vụ mạng phải được tích hợp vào lõi chương trình, điều này gây khó khăn cho việc tạo thư viện các chức năng mạng. Mỗi lần sử dụng các chức năng mạng này, mã bổ sung phải được thêm vào trình xử lý cửa sổ chính của ứng dụng.

      Trong phương pháp xử lý tin nhắn thứ hai, ứng dụng sẽ tạo một cửa sổ ẩn để nhận chúng. Nó dùng để tách thủ tục cửa sổ chính của ứng dụng khỏi việc xử lý các tin nhắn mạng. Cách tiếp cận này có thể đơn giản hóa ứng dụng chính và giúp việc sử dụng mã mạng hiện có trong các chương trình khác trở nên dễ dàng hơn. Mặt tiêu cực của phương pháp này là việc sử dụng quá nhiều bộ nhớ người dùng Windows, bởi vì Đối với mỗi cửa sổ được tạo, một khối lượng khá lớn được dành riêng.

      Lựa chọn phương pháp nào là do bạn quyết định. Một điều nữa. Trong khi thử nghiệm, bạn có thể cần phải tắt tường lửa cá nhân của mình. Ví dụ: Outpost Pro 2.1.275 ở chế độ học tập đã phản hồi lại nỗ lực chuyển sang ổ cắm, nhưng khi quá trình truyền được cho phép theo cách thủ công, dữ liệu vẫn không đến. Rất nhiều cho UDP. Mặc dù điều này có thể không xảy ra. Không có vấn đề gì với ZoneAlarmPro 5.0.590 của tôi trong tình huống tương tự.

      P.S. Khi kết thúc phần thứ hai của bài viết, tôi vô tình tìm thấy mã nguồn của Trojan trên Internet bằng ngôn ngữ MASM yêu thích của chúng tôi. Mọi thứ biên dịch và chạy, có một điều là máy khách không muốn kết nối với máy chủ, và ngay cả trong Windows 2000 sp4 đôi khi nó cũng gặp lỗi, báo rằng ứng dụng sẽ bị đóng và tất cả những thứ đó... Cá nhân tôi thì sao? Điểm giống với Trojan này là chương trình không chỉ lưu giữ nhật ký các lần nhấp chuột hoặc “tách” một tệp có mật khẩu và gửi nó qua email, mà còn có một loạt các chức năng được điều khiển từ xa, được triển khai theo cách rất nguyên bản. Nếu chúng tôi cố gắng đưa toàn bộ hoạt động kinh doanh này vào cuộc sống, thì có lẽ phần thứ ba sẽ sớm xuất hiện, dành cho mô tả về cách triển khai cụ thể... Đối với những người đã đọc kỹ cả hai bài viết và hiểu hoạt động của các hàm API socket, có không có gì phức tạp ở đó. Có vẻ như... Nhân tiện, chính tác giả viết trong readme rằng ông viết nó (Trojan) cho mục đích giáo dục. Ồ, được rồi. Chúng tôi sẽ sử dụng cái này.

      Giám đốc

    ổ cắm

    Ổ cắm là một đầu của kênh liên lạc hai chiều giữa hai chương trình chạy trên mạng. Bằng cách kết nối hai ổ cắm với nhau, bạn có thể truyền dữ liệu giữa các tiến trình khác nhau (cục bộ hoặc từ xa). Việc triển khai socket cung cấp khả năng đóng gói các giao thức lớp mạng và lớp vận chuyển.

    Ổ cắm ban đầu được phát triển cho UNIX tại Đại học California, Berkeley. Trong UNIX, phương thức I/O giao tiếp tuân theo thuật toán mở/đọc/ghi/đóng. Trước khi có thể sử dụng một tài nguyên, nó phải được mở với các quyền thích hợp và các cài đặt khác. Khi một tài nguyên được mở, dữ liệu có thể được đọc hoặc ghi vào. Sau khi sử dụng tài nguyên, người dùng phải gọi phương thức Close() để báo hiệu cho hệ điều hành rằng việc sử dụng tài nguyên đã hoàn tất.

    Khi nào các tính năng được thêm vào hệ điều hành UNIX? Giao tiếp giữa các quá trình (IPC) và trao đổi mạng, mô hình đầu vào-đầu ra quen thuộc đã được mượn. Tất cả các tài nguyên được hiển thị để liên lạc trong UNIX và Windows đều được xác định bằng các thẻ điều khiển. Những mô tả này, hoặc tay cầm, có thể trỏ đến một tệp, bộ nhớ hoặc một số kênh liên lạc khác, nhưng thực tế là trỏ đến cấu trúc dữ liệu nội bộ được hệ điều hành sử dụng. Ổ cắm, có cùng tài nguyên, cũng được biểu thị bằng một bộ mô tả. Vì vậy, đối với socket, tuổi thọ của một tay cầm có thể được chia thành ba giai đoạn: mở (tạo) socket, nhận hoặc gửi đến socket và cuối cùng là đóng socket.

    Giao diện IPC để liên lạc giữa các quy trình khác nhau được xây dựng dựa trên các phương thức I/O. Chúng giúp các socket gửi và nhận dữ liệu dễ dàng hơn. Mỗi mục tiêu được chỉ định bởi một địa chỉ ổ cắm, vì vậy địa chỉ này có thể được chỉ định trong máy khách để thiết lập kết nối đến mục tiêu.

    Các loại ổ cắm

    Có hai loại ổ cắm chính - ổ cắm luồng và ổ cắm datagram.

    Ổ cắm luồng

    Ổ cắm luồng là ổ cắm dựa trên kết nối bao gồm một luồng byte có thể hai chiều, nghĩa là ứng dụng có thể gửi và nhận dữ liệu thông qua điểm cuối này.

    Ổ cắm luồng đảm bảo sửa lỗi, xử lý việc phân phối và duy trì tính nhất quán của dữ liệu. Nó có thể được dựa vào để cung cấp dữ liệu trùng lặp, có trật tự. Ổ cắm luồng cũng phù hợp để truyền lượng lớn dữ liệu vì chi phí thiết lập kết nối riêng cho mỗi tin nhắn được gửi có thể bị cấm đối với lượng dữ liệu nhỏ. Ổ cắm luồng đạt được mức chất lượng này bằng cách sử dụng giao thức Giao thức điều khiển truyền dẫn (TCP). TCP đảm bảo rằng dữ liệu đến được phía bên kia theo đúng trình tự và không có lỗi.

    Đối với loại ổ cắm này, đường dẫn được hình thành trước khi gửi tin nhắn. Điều này đảm bảo rằng cả hai bên tham gia tương tác đều chấp nhận và phản hồi. Nếu một ứng dụng gửi hai tin nhắn đến người nhận, đảm bảo rằng các tin nhắn sẽ được nhận theo cùng một trình tự.

    Tuy nhiên, các tin nhắn riêng lẻ có thể được chia thành các gói và không có cách nào để xác định ranh giới của các bản ghi. Khi sử dụng TCP, giao thức này đảm nhiệm việc chia dữ liệu được truyền thành các gói có kích thước phù hợp, gửi chúng đến mạng và tập hợp lại chúng ở phía bên kia. Ứng dụng chỉ biết rằng nó sẽ gửi một số byte nhất định đến lớp TCP và phía bên kia sẽ nhận các byte đó. Đổi lại, TCP chia dữ liệu này thành các gói có kích thước phù hợp một cách hiệu quả, nhận các gói này ở phía bên kia, trích xuất dữ liệu từ chúng và kết hợp chúng lại với nhau.

    Các luồng dựa trên các kết nối rõ ràng: socket A yêu cầu kết nối với socket B và socket B chấp nhận hoặc từ chối yêu cầu kết nối.

    Nếu dữ liệu phải được đảm bảo sẽ được chuyển sang phía bên kia hoặc kích thước của dữ liệu lớn, thì ổ cắm luồng sẽ thích hợp hơn ổ cắm gói dữ liệu. Do đó, nếu việc liên lạc đáng tin cậy giữa hai ứng dụng là vô cùng quan trọng, hãy chọn ổ cắm luồng.

    Máy chủ email là một ví dụ về ứng dụng phải phân phối nội dung theo đúng thứ tự, không bị trùng lặp hoặc thiếu sót. Ổ cắm luồng dựa vào TCP để đảm bảo tin nhắn được gửi đến đích của chúng.

    Ổ cắm datagram

    Ổ cắm gói dữ liệu đôi khi được gọi là ổ cắm không kết nối, tức là không có kết nối rõ ràng nào được thiết lập giữa chúng - thông báo được gửi đến ổ cắm được chỉ định và theo đó, có thể được nhận từ ổ cắm được chỉ định.

    Ổ cắm luồng cung cấp phương pháp đáng tin cậy hơn ổ cắm datagram, nhưng đối với một số ứng dụng, chi phí liên quan đến việc thiết lập kết nối rõ ràng là không thể chấp nhận được (ví dụ: máy chủ thời gian trong ngày cung cấp đồng bộ hóa thời gian cho máy khách của nó). Xét cho cùng, việc thiết lập một kết nối đáng tin cậy đến máy chủ cần có thời gian, điều này chỉ gây ra sự chậm trễ trong dịch vụ và tác vụ của ứng dụng máy chủ không thành công. Để giảm chi phí, bạn nên sử dụng ổ cắm datagram.

    Việc sử dụng các ổ cắm datagram yêu cầu việc truyền dữ liệu từ máy khách đến máy chủ phải được xử lý bởi Giao thức gói dữ liệu người dùng (UDP). Trong giao thức này, một số hạn chế được áp dụng đối với kích thước của tin nhắn và không giống như các ổ cắm luồng, có thể gửi tin nhắn đến máy chủ đích một cách đáng tin cậy, các ổ cắm datagram không cung cấp độ tin cậy. Nếu dữ liệu bị mất ở đâu đó trên mạng, máy chủ sẽ không báo lỗi.

    Ngoài hai loại đã thảo luận, còn có một dạng ổ cắm tổng quát, được gọi là chưa được xử lý hoặc thô.

    Ổ cắm thô

    Mục đích chính của việc sử dụng raw socket là bỏ qua cơ chế mà máy tính xử lý TCP/IP. Điều này đạt được bằng cách cung cấp một triển khai đặc biệt của ngăn xếp TCP/IP ghi đè cơ chế được cung cấp bởi ngăn xếp TCP/IP trong kernel - gói được truyền trực tiếp đến ứng dụng và do đó được xử lý hiệu quả hơn nhiều so với khi đi qua máy khách. ngăn xếp giao thức chính.

    Theo định nghĩa, ổ cắm thô là ổ cắm nhận gói, bỏ qua các lớp TCP và UDP trong ngăn xếp TCP/IP và gửi chúng trực tiếp đến ứng dụng.

    Khi sử dụng các ổ cắm như vậy, gói không đi qua bộ lọc TCP/IP, tức là. không được xử lý dưới bất kỳ hình thức nào và xuất hiện ở dạng thô. Trong trường hợp này, ứng dụng nhận có trách nhiệm xử lý đúng tất cả dữ liệu và thực hiện các hành động như loại bỏ tiêu đề và trường phân tích cú pháp - như đưa một ngăn xếp TCP/IP nhỏ vào ứng dụng.

    Tuy nhiên, không thường xuyên bạn có thể cần một chương trình xử lý các ổ cắm thô. Trừ khi bạn đang viết phần mềm hệ thống hoặc một chương trình giống như trình bắt gói tin, bạn sẽ không cần phải đi sâu vào chi tiết như vậy. Ổ cắm thô chủ yếu được sử dụng để phát triển các ứng dụng giao thức cấp thấp chuyên dụng. Ví dụ: nhiều tiện ích TCP/IP khác nhau như trace Route, ping hoặc arp sử dụng ổ cắm thô.

    Làm việc với các socket thô đòi hỏi kiến ​​thức vững chắc về các giao thức TCP/UDP/IP cơ bản.

    Cổng

    Cổng được xác định để cho phép giải quyết vấn đề giao tiếp đồng thời với nhiều ứng dụng. Về cơ bản, nó mở rộng khái niệm về địa chỉ IP. Một máy tính chạy nhiều ứng dụng cùng lúc nhận gói từ mạng có thể xác định quy trình đích bằng cách sử dụng số cổng duy nhất được chỉ định khi kết nối được thiết lập.

    Ổ cắm bao gồm địa chỉ IP của máy và số cổng được ứng dụng TCP sử dụng. Vì địa chỉ IP là duy nhất trên Internet và số cổng là duy nhất trên một máy riêng lẻ nên số ổ cắm cũng là duy nhất trên toàn bộ Internet. Đặc tính này cho phép một tiến trình giao tiếp qua mạng với một tiến trình khác chỉ dựa trên số ổ cắm.

    Số cổng được dành riêng cho một số dịch vụ nhất định - đây là những số cổng phổ biến, chẳng hạn như cổng 21, được sử dụng trong FTP. Ứng dụng của bạn có thể sử dụng bất kỳ số cổng nào chưa được bảo lưu và chưa được sử dụng. Hãng Cơ quan quản lý số được gán Internet (IANA) duy trì một danh sách các số cổng thường được biết đến.

    Thông thường, một ứng dụng máy khách-máy chủ sử dụng ổ cắm bao gồm hai ứng dụng khác nhau - một máy khách khởi tạo kết nối đến đích (máy chủ) và một máy chủ đang chờ kết nối từ máy khách.

    Ví dụ: về phía máy khách, ứng dụng phải biết địa chỉ đích và số cổng. Bằng cách gửi yêu cầu kết nối, máy khách sẽ cố gắng thiết lập kết nối với máy chủ:

    Nếu sự kiện phát triển thành công, miễn là máy chủ được khởi động trước khi máy khách cố gắng kết nối với nó, thì máy chủ sẽ đồng ý kết nối. Sau khi được sự đồng ý, ứng dụng máy chủ sẽ tạo một ổ cắm mới để tương tác cụ thể với máy khách đã thiết lập kết nối:

    Bây giờ máy khách và máy chủ có thể tương tác với nhau, đọc từng tin nhắn từ ổ cắm riêng của chúng và theo đó, viết tin nhắn.

    Làm việc với các socket trong .NET

    Hỗ trợ socket trong .NET được cung cấp bởi các lớp trong không gian tên System.Net.Sockets- Hãy bắt đầu với mô tả ngắn gọn của họ.

    Các lớp làm việc với socket
    Lớp học Sự miêu tả
    Tùy chọn Multicast Lớp MulticastOption đặt giá trị địa chỉ IP để tham gia hoặc rời khỏi nhóm IP.
    Luồng mạng Lớp NetworkStream triển khai lớp luồng cơ sở để gửi và nhận dữ liệu. Đây là sự trừu tượng hóa cấp cao thể hiện kết nối với kênh liên lạc TCP/IP.
    TcpClient Lớp TcpClient xây dựng trên lớp Socket để cung cấp dịch vụ TCP cấp cao hơn. TcpClient cung cấp một số phương thức gửi và nhận dữ liệu qua mạng.
    Trình nghe Tcp Lớp này cũng được xây dựng trên lớp Socket cấp thấp. Mục đích chính của nó là các ứng dụng máy chủ. Nó lắng nghe các yêu cầu kết nối đến từ máy khách và thông báo cho ứng dụng về bất kỳ kết nối nào.
    UdpClient UDP là một giao thức không kết nối, do đó cần có các chức năng khác nhau để triển khai các dịch vụ UDP trong .NET.
    Ổ cắm ngoại lệ Ngoại lệ này được đưa ra khi xảy ra lỗi trên ổ cắm.
    Ổ cắm Lớp cuối cùng trong không gian tên System.Net.Sockets chính là lớp Socket. Nó cung cấp các chức năng cơ bản của một ứng dụng socket.

    Lớp ổ cắm

    Lớp Socket đóng vai trò quan trọng trong lập trình mạng, cung cấp cả chức năng máy khách và máy chủ. Về cơ bản, các lệnh gọi đến các phương thức trong lớp này thực hiện các kiểm tra cần thiết liên quan đến bảo mật, bao gồm kiểm tra các quyền bảo mật, sau đó chúng được chuyển tiếp đến các đối tác của phương thức đó trong Windows Sockets API.

    Trước khi chuyển sang ví dụ về cách sử dụng lớp Socket, chúng ta hãy xem xét một số thuộc tính và phương thức quan trọng của lớp này:

    Thuộc tính và phương thức của lớp Socket
    Thuộc tính hoặc phương thức Sự miêu tả
    Địa chỉGia đình Cung cấp họ địa chỉ socket - một giá trị từ bảng liệt kê Socket.AddressFamily.
    Có sẵn Trả về lượng dữ liệu có sẵn để đọc.
    Chặn Nhận hoặc đặt giá trị cho biết ổ cắm có ở chế độ chặn hay không.
    Đã kết nối Trả về giá trị cho biết ổ cắm có được kết nối với máy chủ từ xa hay không.
    Điểm cuối cục bộ Cung cấp điểm cuối cục bộ.
    Loại giao thức Cung cấp loại giao thức của ổ cắm.
    Điểm cuối từ xa Cung cấp điểm cuối ổ cắm từ xa.
    Loại ổ cắm Cung cấp loại ổ cắm.
    Chấp nhận() Tạo một ổ cắm mới để xử lý yêu cầu kết nối đến.
    Trói buộc() Liên kết ổ cắm với điểm cuối cục bộ để lắng nghe các yêu cầu kết nối đến.
    Đóng() Buộc ổ cắm đóng lại.
    Kết nối() Thiết lập kết nối với máy chủ từ xa.
    GetSocketOption() Trả về giá trị SocketOption.
    IOControl() Đặt chế độ hoạt động cấp thấp cho ổ cắm. Phương thức này cung cấp quyền truy cập cấp thấp vào lớp Socket cơ bản.
    Nghe() Đặt ổ cắm ở chế độ nghe (chờ). Phương pháp này chỉ dành cho các ứng dụng máy chủ.
    Nhận được() Nhận dữ liệu từ ổ cắm được kết nối.
    Thăm dò ý kiến() Xác định trạng thái của ổ cắm.
    Lựa chọn() Kiểm tra trạng thái của một hoặc nhiều ổ cắm.
    Gửi() Gửi dữ liệu đến ổ cắm được kết nối.
    SetSocketOption() Đặt tùy chọn ổ cắm.
    Tắt() Vô hiệu hóa các hoạt động gửi và nhận trên ổ cắm.

    Cập nhật lần cuối: 31/10/2015

    Giao thức UDP không yêu cầu kết nối cố định và nhiều người có thể thấy làm việc với UDP dễ dàng hơn so với TCP. Hầu hết các nguyên tắc khi làm việc với UDP đều giống với TCP.

    Đầu tiên, một ổ cắm được tạo:

    Ổ cắm ổ cắm = Ổ cắm mới (AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

    Nếu ổ cắm phải nhận tin nhắn thì bạn cần liên kết nó với một địa chỉ cục bộ và một trong các cổng bằng phương thức Bind:

    IPEndPoint localIP = IPEndPoint mới(IPAddress.Parse("127.0.0.1"), 5555); socket.Bind(localIP);

    Sau này, bạn có thể gửi và nhận tin nhắn. Để nhận tin nhắn, hãy sử dụng phương thức AcceptFrom():

    Dữ liệu byte = byte mới; // bộ đệm cho dữ liệu đã nhận //địa chỉ nơi dữ liệu đến EndPoint remoteIp = new IPEndPoint(IPAddress.Any, 0); int byte = socket.ReceiveFrom(data, ref remoteIp);

    Là một tham số, phương thức này được truyền một mảng byte để đọc dữ liệu và điểm từ xa mà dữ liệu này xuất phát. Phương thức trả về số byte đã đọc.

    Để gửi dữ liệu, hãy sử dụng phương thức SendTo():

    Thông báo chuỗi = Console.ReadLine(); dữ liệu byte = Encoding.Unicode.GetBytes(tin nhắn); EndPoint remotePoint = IPEndPoint mới(IPAddress.Parse("127.0.0.1"), remotePort); listenSocket.SendTo(data, remotePoint);

    Một mảng dữ liệu sẽ được gửi, cũng như địa chỉ mà dữ liệu này sẽ được gửi đến, sẽ được truyền cho phương thức.

    Hãy tạo một chương trình máy khách UDP:

    Sử dụng hệ thống; sử dụng System.Text; sử dụng System.Threading.Tasks; sử dụng System.Net; sử dụng System.Net.Sockets; namespace SocketUdpClient ( class Program ( static int localPort; // cổng nhận tin nhắn static int remotePort; // cổng gửi tin nhắn static Socket listenSocket; static void Main(string args) ( Console.Write("Nhập cổng nhận tin nhắn: ") ; localPort = Int32.Parse(Console.ReadLine()); Console.Write("Nhập cổng để gửi tin nhắn: "); remotePort = Int32.Parse(Console.ReadLine()); Console.WriteLine("To gửi tin nhắn, nhập tin nhắn và nhấn Enter"); Console.WriteLine(); thử ( listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); Task listenTask = new Task(Listen); listenTask.Start( ); // gửi tin nhắn đến các cổng khác nhau while (true) ( ​​​string message = Console.ReadLine(); byte data = Encoding.Unicode.GetBytes(message); EndPoint remotePoint = new IPEndPoint(IPAddress.Parse("127.0. 0.1"), remotePort) ; listenSocket.SendTo(data, remotePoint); ) Catch (Ngoại lệ cũ) ( Console.WriteLine(ex.Message); ) cuối cùng ( Close(); ) ) // luồng để chấp nhận kết nối riêng tĩnh void Listen() ( try ( //Nghe tại địa chỉ IPEndPoint localIP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), localPort); listenSocket .Bind(localIP); while (true) ( ​​// nhận thông báo StringBuilder builder = new StringBuilder(); int bytes = 0; // số byte nhận được dữ liệu byte = byte mới; // bộ đệm cho dữ liệu đã nhận / / địa chỉ, dữ liệu nào đến EndPoint remoteIp = new IPEndPoint(IPAddress.Any, 0); do ( bytes = listenSocket.ReceiveFrom(data, ref remoteIp); builder.Append(Encoding.Unicode.GetString(data, 0, bytes) ); ) while (listeningSocket.Available > 0); // lấy thông tin kết nối IPEndPoint remoteFullIp = remoteIp as IPEndPoint; // in thông báo Console.WriteLine("(0):(1) - (2)", remoteFullIp.Address .ToString() , remoteFullIp.Port, builder.ToString()); ) ) Catch (Exception ex) ( Console.WriteLine(ex.Message); ) cuối cùng ( Close(); ) ) // đóng socket Private static void Close() ( if (listeningSocket != null) ( listenSocket.Shutdown(SocketShutdown.Both); listenSocket.Close(); listenSocket = null; ) ) ) )

    Đầu tiên, người dùng vào các cổng để nhận dữ liệu và gửi. Giả định rằng hai ứng dụng khách sẽ tương tác với nhau đang chạy trên cùng một máy cục bộ. Nếu địa chỉ của khách hàng khác nhau, bạn cũng có thể cung cấp cách nhập địa chỉ để gửi dữ liệu.

    Sau khi vào các cổng, nhiệm vụ nghe tin nhắn đến sẽ được khởi chạy. Không giống như máy chủ tcp, không cần gọi các phương thức Listen và Accept. Trong một vòng lặp vô hạn, chúng ta có thể trực tiếp nhận dữ liệu bằng phương thức AcceptFrom(), phương thức này sẽ chặn luồng đang gọi cho đến khi phần dữ liệu tiếp theo đến.

    Phương thức này trả về, thông qua tham số ref, điểm từ xa mà dữ liệu được nhận:

    IPEndPoint remoteFullIp = remoteIp dưới dạng IPEndPoint;

    Nghĩa là, mặc dù trong trường hợp này, việc nhận và gửi tin nhắn được phân biệt và máy khách hiện tại chỉ gửi dữ liệu đến cổng đã nhập ban đầu, chúng ta có thể dễ dàng thêm khả năng phản hồi tin nhắn bằng cách sử dụng dữ liệu của điểm từ xa đã nhận ( địa chỉ và cổng).

    Main thread gửi tin nhắn bằng phương thức SendTo()

    Do đó, ứng dụng ngay lập tức thực hiện các chức năng của cả máy chủ và máy khách.

    Bây giờ hãy khởi chạy hai bản sao của ứng dụng và nhập dữ liệu khác nhau cho các cổng. Khách hàng đầu tiên:

    Nhập cổng nhận tin nhắn: 4004 Nhập cổng gửi tin nhắn: 4005 Để gửi tin nhắn, nhập tin nhắn và nhấn Enter 127.0.0.1:4005 - hello port 4004 chào buổi chiều, cổng 4005 thời tiết tuyệt vời

    Khách hàng thứ hai:

    Nhập cổng nhận tin nhắn: 4005 Nhập cổng gửi tin nhắn: 4004 Để gửi tin nhắn, nhập tin nhắn và nhấn Enter hello port 4004 127.0.0.1:4004 - chào buổi chiều, cổng 4005 127.0.0.1:4004 - thời tiết tuyệt vời