Unix Socket - Hướng dẫn nhanh

Sockets cho phép giao tiếp giữa hai tiến trình khác nhau trên cùng một máy hoặc khác nhau. Nói chính xác hơn, đó là một cách để nói chuyện với các máy tính khác bằng cách sử dụng bộ mô tả tệp Unix tiêu chuẩn. Trong Unix, mọi hành động I / O được thực hiện bằng cách viết hoặc đọc một bộ mô tả tệp. Bộ mô tả tệp chỉ là một số nguyên được liên kết với tệp đang mở và nó có thể là kết nối mạng, tệp văn bản, thiết bị đầu cuối hoặc thứ gì khác.

Đối với một lập trình viên, một socket trông và hoạt động giống như một bộ mô tả tệp cấp thấp. Điều này là do các lệnh như read () và write () hoạt động với socket giống như cách chúng làm với các tệp và đường ống.

Sockets lần đầu tiên được giới thiệu trong 2.1BSD và sau đó được tinh chỉnh thành hình thức hiện tại với 4.2BSD. Tính năng ổ cắm hiện có sẵn với hầu hết các phiên bản hệ thống UNIX hiện tại.

Ổ cắm được sử dụng ở đâu?

Unix Socket được sử dụng trong khuôn khổ ứng dụng máy khách-máy chủ. Máy chủ là một quá trình thực hiện một số chức năng theo yêu cầu từ máy khách. Hầu hết các giao thức cấp ứng dụng như FTP, SMTP và POP3 sử dụng các ổ cắm để thiết lập kết nối giữa máy khách và máy chủ và sau đó để trao đổi dữ liệu.

Các loại ổ cắm

Có bốn loại ổ cắm có sẵn cho người dùng. Hai cái đầu tiên thường được sử dụng nhất và hai cái cuối cùng hiếm khi được sử dụng.

Các quy trình được cho là chỉ giao tiếp giữa các ổ cắm cùng loại nhưng không có hạn chế nào ngăn cản giao tiếp giữa các ổ cắm khác loại.

  • Stream Sockets- Giao hàng trong môi trường mạng được đảm bảo. Nếu bạn gửi qua ổ cắm luồng ba mục "A, B, C", chúng sẽ đến theo cùng một thứ tự - "A, B, C". Các ổ cắm này sử dụng TCP (Transmission Control Protocol) để truyền dữ liệu. Nếu không thể giao hàng, người gửi sẽ nhận được chỉ báo lỗi. Hồ sơ dữ liệu không có bất kỳ ranh giới nào.

  • Datagram Sockets- Giao hàng trong môi trường mạng không đảm bảo. Chúng không có kết nối vì bạn không cần phải có kết nối mở như trong Stream Sockets - bạn xây dựng một gói với thông tin đích và gửi nó đi. Họ sử dụng UDP (User Datagram Protocol).

  • Raw Sockets- Các giao thức này cung cấp cho người dùng quyền truy cập vào các giao thức truyền thông cơ bản, hỗ trợ trừu tượng hóa socket. Các socket này thường được định hướng datagram, mặc dù các đặc điểm chính xác của chúng phụ thuộc vào giao diện được cung cấp bởi giao thức. Ổ cắm thô không dành cho người dùng phổ thông; chúng đã được cung cấp chủ yếu cho những người quan tâm đến việc phát triển các giao thức truyền thông mới hoặc để có quyền truy cập vào một số cơ sở khó hiểu hơn của một giao thức hiện có.

  • Sequenced Packet Sockets- Chúng tương tự như một ổ cắm luồng, ngoại trừ các ranh giới bản ghi được giữ nguyên. Giao diện này chỉ được cung cấp như một phần của sự trừu tượng hóa ổ cắm của Hệ thống mạng (NS) và rất quan trọng trong hầu hết các ứng dụng NS nghiêm túc. Các ổ cắm gói có trình tự cho phép người dùng thao tác các tiêu đề của Giao thức gói trình tự (SPP) hoặc Giao thức dữ liệu Internet (IDP) trên một gói hoặc một nhóm gói, bằng cách viết một tiêu đề nguyên mẫu cùng với bất kỳ dữ liệu nào sẽ được gửi đi hoặc bằng chỉ định tiêu đề mặc định được sử dụng với tất cả dữ liệu gửi đi và cho phép người dùng nhận tiêu đề trên các gói tin đến.

Tiếp theo là gì?

Một vài chương tiếp theo nhằm củng cố kiến ​​thức cơ bản của bạn và chuẩn bị nền tảng trước khi bạn có thể viết các chương trình Máy chủ và Máy khách bằng socket . Nếu bạn trực tiếp muốn xem cách viết chương trình máy khách và máy chủ, thì bạn có thể làm như vậy nhưng không được khuyến khích. Chúng tôi khuyên bạn nên thực hiện từng bước và hoàn thành một vài chương ban đầu này để làm cơ sở cho mình trước khi chuyển sang học lập trình.

Trước khi chúng tôi tiếp tục với nội dung thực tế, chúng ta hãy thảo luận một chút về Địa chỉ mạng - Địa chỉ IP.

Địa chỉ máy chủ IP, hay thường chỉ là địa chỉ IP, được sử dụng để xác định các máy chủ được kết nối với Internet. IP là viết tắt của Giao thức Internet và đề cập đến Lớp Internet của kiến ​​trúc mạng tổng thể của Internet.

Địa chỉ IP là một số lượng 32 bit được hiểu là bốn số 8 bit hoặc bộ tám. Mỗi địa chỉ IP xác định duy nhất mạng người dùng tham gia, máy chủ lưu trữ trên mạng và lớp của mạng người dùng.

Địa chỉ IP thường được viết bằng ký hiệu dấu chấm-thập phân có dạng N1.N2.N3.N4, trong đó mỗi Ni là một số thập phân từ 0 đến 255 thập phân (từ 00 đến FF thập lục phân).

Lớp địa chỉ

Địa chỉ IP được quản lý và tạo bởi Cơ quan cấp số hiệu Internet (IANA). Có năm lớp địa chỉ khác nhau. Bạn có thể xác định địa chỉ IP thuộc lớp nào bằng cách kiểm tra bốn bit đầu tiên của địa chỉ IP.

  • Class A địa chỉ bắt đầu bằng 0xxx, hoặc là 1 to 126 số thập phân.

  • Class B địa chỉ bắt đầu bằng 10xx, hoặc là 128 to 191 số thập phân.

  • Class C địa chỉ bắt đầu bằng 110x, hoặc là 192 to 223 số thập phân.

  • Class D địa chỉ bắt đầu bằng 1110, hoặc là 224 to 239 số thập phân.

  • Class E địa chỉ bắt đầu bằng 1111, hoặc là 240 to 254 số thập phân.

Địa chỉ bắt đầu bằng 01111111, hoặc là 127 thập phân, được dành riêng cho lặp lại và thử nghiệm nội bộ trên máy cục bộ [Bạn có thể kiểm tra điều này: bạn luôn có thể ping 127.0.0.1, chỉ về bản thân]; Địa chỉ lớp D được dành riêng cho đa hướng; Địa chỉ lớp E được dành để sử dụng trong tương lai. Chúng không nên được sử dụng cho địa chỉ máy chủ.

Thí dụ

Class Leftmost bits Start address Finish address
A 0xxx 0.0.0.0 127.255.255.255
B 10xx 128.0.0.0 191.255.255.255
C 110x 192.0.0.0 223.255.255.255
D 1110 224.0.0.0 239.255.255.255
E 1111 240.0.0.0 255.255.255.255

Mạng con

Mạng con hoặc mạng con về cơ bản có nghĩa là phân nhánh ra khỏi mạng. Nó có thể được thực hiện vì nhiều lý do như mạng trong một tổ chức, sử dụng các phương tiện vật lý khác nhau (như Ethernet, FDDI, WAN, v.v.), duy trì không gian địa chỉ và bảo mật. Lý do phổ biến nhất là kiểm soát lưu lượng mạng.

Ý tưởng cơ bản trong mạng con là phân chia phần định danh máy chủ của địa chỉ IP thành hai phần -

  • Một địa chỉ mạng con trong chính địa chỉ mạng đó; và
  • Một địa chỉ máy chủ trên mạng con.

Ví dụ, định dạng địa chỉ Lớp B phổ biến là N1.N2.SH, trong đó N1.N2 xác định mạng Lớp B, trường 8-bit S xác định mạng con và trường 8-bit H xác định máy chủ trên mạng con.

Tên vật chủ về số lượng rất khó nhớ và do đó chúng được gọi bằng những cái tên thông thường như Takshila hoặc Nalanda. Chúng tôi viết các ứng dụng phần mềm để tìm ra địa chỉ IP có dấu chấm tương ứng với một tên nhất định.

Quá trình tìm ra địa chỉ IP có dấu chấm dựa trên tên máy chủ chứa chữ và số đã cho được gọi là hostname resolution.

Việc phân giải tên máy chủ được thực hiện bằng phần mềm đặc biệt trên các hệ thống dung lượng cao. Các hệ thống này được gọi là Hệ thống tên miền (DNS), giữ ánh xạ địa chỉ IP và các tên thông thường tương ứng.

Tệp / etc / hosts

Sự tương ứng giữa tên máy chủ và địa chỉ IP được duy trì trong một tệp được gọi là máy chủ . Trên hầu hết các hệ thống, tệp này được tìm thấy trong/etc danh mục.

Các mục nhập trong tệp này trông giống như sau:

# This represents a comments in /etc/hosts file.
127.0.0.1       localhost
192.217.44.207  nalanda metro
153.110.31.18   netserve
153.110.31.19   mainserver centeral
153.110.31.20   samsonite
64.202.167.10   ns3.secureserver.net
64.202.167.97   ns4.secureserver.net
66.249.89.104   www.google.com
68.178.157.132  services.amrood.com

Lưu ý rằng nhiều tên có thể được liên kết với một địa chỉ IP nhất định. Tệp này được sử dụng trong khi chuyển đổi từ địa chỉ IP sang tên máy chủ và ngược lại.

Bạn sẽ không có quyền truy cập để chỉnh sửa tệp này, vì vậy nếu bạn muốn đặt bất kỳ tên máy chủ nào cùng với địa chỉ IP, thì bạn cần phải có quyền root.

Hầu hết các Ứng dụng Mạng sử dụng kiến ​​trúc Máy khách-Máy chủ, đề cập đến hai tiến trình hoặc hai ứng dụng giao tiếp với nhau để trao đổi một số thông tin. Một trong hai quy trình hoạt động như một quy trình khách và một quy trình khác hoạt động như một máy chủ.

Quy trình khách hàng

Đây là quá trình, thường đưa ra yêu cầu cung cấp thông tin. Sau khi nhận được phản hồi, quá trình này có thể kết thúc hoặc có thể thực hiện một số xử lý khác.

Example, Trình duyệt Internet hoạt động như một ứng dụng khách, ứng dụng này sẽ gửi yêu cầu đến Máy chủ Web để lấy một trang web HTML.

Quy trình máy chủ

Đây là quá trình nhận yêu cầu từ khách hàng. Sau khi nhận được yêu cầu từ máy khách, quy trình này sẽ thực hiện xử lý theo yêu cầu, thu thập thông tin được yêu cầu và gửi đến máy khách yêu cầu. Sau khi hoàn tất, nó sẽ sẵn sàng để phục vụ một khách hàng khác. Các tiến trình của máy chủ luôn cảnh báo và sẵn sàng phục vụ các yêu cầu đến.

Example - Máy chủ Web tiếp tục đợi các yêu cầu từ Trình duyệt Internet và ngay khi nhận được bất kỳ yêu cầu nào từ trình duyệt, nó sẽ chọn một trang HTML được yêu cầu và gửi lại cho Trình duyệt đó.

Lưu ý rằng máy khách cần biết địa chỉ của máy chủ, nhưng máy chủ không cần biết địa chỉ hoặc thậm chí sự tồn tại của máy khách trước khi kết nối được thiết lập. Khi kết nối được thiết lập, cả hai bên có thể gửi và nhận thông tin.

Kiến trúc 2 tầng và 3 tầng

Có hai loại kiến ​​trúc máy khách-máy chủ -

  • 2-tier architecture- Trong kiến ​​trúc này, máy khách tương tác trực tiếp với máy chủ. Loại kiến ​​trúc này có thể có một số lỗ hổng bảo mật và các vấn đề về hiệu suất. Internet Explorer và Máy chủ Web hoạt động trên kiến ​​trúc hai tầng. Ở đây các vấn đề bảo mật được giải quyết bằng cách sử dụng Lớp cổng bảo mật (SSL).

  • 3-tier architectures- Trong kiến ​​trúc này, một phần mềm nữa nằm giữa máy khách và máy chủ. Phần mềm trung gian này được gọi là 'phần mềm trung gian'. Phần mềm trung gian được sử dụng để thực hiện tất cả các kiểm tra bảo mật và cân bằng tải trong trường hợp tải nặng. Phần mềm trung gian nhận tất cả các yêu cầu từ máy khách và sau khi thực hiện xác thực được yêu cầu, nó sẽ chuyển yêu cầu đó đến máy chủ. Sau đó, máy chủ thực hiện xử lý theo yêu cầu và gửi phản hồi trở lại phần mềm trung gian và cuối cùng phần mềm trung gian chuyển phản hồi này trở lại máy khách. Nếu bạn muốn triển khai kiến ​​trúc 3 tầng, thì bạn có thể giữ bất kỳ phần mềm trung gian nào như phần mềm Web Logic hoặc WebSphere ở giữa Máy chủ Web và Trình duyệt Web của bạn.

Các loại máy chủ

Có hai loại máy chủ mà bạn có thể có -

  • Iterative Server- Đây là dạng máy chủ đơn giản nhất mà một tiến trình máy chủ phục vụ một máy khách và sau khi hoàn thành yêu cầu đầu tiên, nó sẽ nhận yêu cầu từ một máy khách khác. Trong khi đó, một khách hàng khác vẫn tiếp tục chờ đợi.

  • Concurrent Servers- Loại máy chủ này chạy nhiều tiến trình đồng thời để phục vụ nhiều yêu cầu cùng một lúc vì một quá trình này có thể lâu hơn và một máy khách khác không thể đợi quá lâu. Cách đơn giản nhất để viết một máy chủ đồng thời trong Unix là phân nhánh một quy trình con để xử lý từng máy khách riêng biệt.

Làm thế nào để tạo khách hàng

Hệ thống gọi thiết lập kết nối hơi khác nhau đối với máy khách và máy chủ, nhưng cả hai đều liên quan đến cấu trúc cơ bản của một ổ cắm. Cả hai quy trình đều thiết lập các ổ cắm riêng của chúng.

Các bước liên quan đến việc thiết lập một ổ cắm ở phía máy khách như sau:

  • Tạo một ổ cắm với socket() cuộc gọi hệ thống.

  • Kết nối ổ cắm với địa chỉ của máy chủ bằng cách sử dụng connect() cuộc gọi hệ thống.

  • Gửi và nhận dữ liệu. Có một số cách để làm điều này, nhưng cách đơn giản nhất là sử dụngread()write() các cuộc gọi hệ thống.

Cách tạo Máy chủ

Các bước liên quan đến việc thiết lập một ổ cắm ở phía máy chủ như sau:

  • Tạo một ổ cắm với socket() cuộc gọi hệ thống.

  • Liên kết ổ cắm với một địa chỉ bằng cách sử dụng bind()cuộc gọi hệ thống. Đối với ổ cắm máy chủ trên Internet, địa chỉ bao gồm số cổng trên máy chủ.

  • Lắng nghe kết nối với listen() cuộc gọi hệ thống.

  • Chấp nhận kết nối với accept()cuộc gọi hệ thống. Cuộc gọi này thường chặn kết nối cho đến khi một máy khách kết nối với máy chủ.

  • Gửi và nhận dữ liệu bằng cách sử dụng read()write() các cuộc gọi hệ thống.

Tương tác giữa máy khách và máy chủ

Sau đây là sơ đồ cho thấy toàn bộ sự tương tác giữa Máy khách và Máy chủ -

Các cấu trúc khác nhau được sử dụng trong Lập trình Unix Socket để giữ thông tin về địa chỉ và cổng cũng như các thông tin khác. Hầu hết các hàm socket yêu cầu một con trỏ đến cấu trúc địa chỉ socket làm đối số. Các cấu trúc được định nghĩa trong chương này có liên quan đến Họ Giao thức Internet.

sockaddr

Cấu trúc đầu tiên là sockaddr chứa thông tin ổ cắm -

struct sockaddr {
   unsigned short   sa_family;
   char             sa_data[14];
};

Đây là cấu trúc địa chỉ socket chung, sẽ được chuyển trong hầu hết các lệnh gọi hàm socket. Bảng sau cung cấp mô tả về các trường thành viên:

Thuộc tính Giá trị Sự miêu tả
sa_family

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

Nó đại diện cho một họ địa chỉ. Trong hầu hết các ứng dụng dựa trên Internet, chúng tôi sử dụng AF_INET.
sa_data Địa chỉ giao thức cụ thể Nội dung của 14 byte địa chỉ cụ thể của giao thức được diễn giải theo loại địa chỉ. Đối với họ Internet, chúng tôi sẽ sử dụng địa chỉ IP số cổng, được biểu thị bằng cấu trúc sockaddr_in được định nghĩa bên dưới.

sockaddr trong

Cấu trúc thứ hai giúp bạn tham chiếu đến các phần tử của ổ cắm như sau:

struct sockaddr_in {
   short int            sin_family;
   unsigned short int   sin_port;
   struct in_addr       sin_addr;
   unsigned char        sin_zero[8];
};

Đây là mô tả của các trường thành viên -

Thuộc tính Giá trị Sự miêu tả
sa_family

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

Nó đại diện cho một họ địa chỉ. Trong hầu hết các ứng dụng dựa trên Internet, chúng tôi sử dụng AF_INET.
sin_port Cổng dịch vụ Số cổng 16 bit trong Thứ tự byte mạng.
sin_addr Địa chỉ IP Một địa chỉ IP 32-bit trong Thứ tự Byte Mạng.
sin_zero Không được sử dụng Bạn chỉ cần đặt giá trị này thành NULL vì giá trị này không được sử dụng.

trong addr

Cấu trúc này chỉ được sử dụng trong cấu trúc trên như một trường cấu trúc và chứa netid / hostid 32 bit.

struct in_addr {
   unsigned long s_addr;
};

Đây là mô tả của các trường thành viên -

Thuộc tính Giá trị Sự miêu tả
s_addr cổng dịch vụ Một địa chỉ IP 32-bit trong Thứ tự Byte Mạng.

chủ nhà

Cấu trúc này được sử dụng để giữ thông tin liên quan đến máy chủ.

struct hostent {
   char *h_name; 
   char **h_aliases; 
   int h_addrtype;  
   int h_length;    
   char **h_addr_list
	
#define h_addr  h_addr_list[0]
};

Đây là mô tả của các trường thành viên -

Thuộc tính Giá trị Sự miêu tả
h_name ti.com, v.v. Đây là tên chính thức của máy chủ. Ví dụ: tutorialspoint.com, google.com, v.v.
h_aliases TI Nó chứa một danh sách các bí danh tên máy chủ.
h_addrtype AF_INET Nó chứa họ địa chỉ và trong trường hợp ứng dụng dựa trên Internet, nó sẽ luôn là AF_INET.
h_length 4 Nó chứa độ dài của địa chỉ IP, là 4 cho Địa chỉ Internet.
h_addr_list in_addr Đối với địa chỉ Internet, mảng con trỏ h_addr_list [0], h_addr_list [1], v.v., là các điểm đến cấu trúc in_addr.

NOTE - h_addr được định nghĩa là h_addr_list [0] để giữ khả năng tương thích ngược.

servent

Cấu trúc cụ thể này được sử dụng để giữ thông tin liên quan đến dịch vụ và các cổng liên quan.

struct servent {
   char  *s_name; 
   char  **s_aliases; 
   int   s_port;  
   char  *s_proto;
};

Đây là mô tả của các trường thành viên -

Thuộc tính Giá trị Sự miêu tả
tên của http Đây là tên chính thức của dịch vụ. Ví dụ: SMTP, FTP POP3, v.v.
s_aliases ALIAS Nó chứa danh sách các bí danh dịch vụ. Hầu hết thời gian điều này sẽ được đặt thành NULL.
s_port 80 Nó sẽ có số cổng liên quan. Ví dụ: đối với HTTP, giá trị này sẽ là 80.
s_proto

TCP

UDP

Nó được đặt thành giao thức được sử dụng. Dịch vụ Internet được cung cấp bằng TCP hoặc UDP.

Mẹo về cấu trúc ổ cắm

Cấu trúc địa chỉ socket là một phần không thể thiếu của mọi chương trình mạng. Chúng tôi phân bổ chúng, điền vào và chuyển các con trỏ tới chúng đến các hàm socket khác nhau. Đôi khi chúng ta chuyển một con trỏ tới một trong những cấu trúc này tới một hàm socket và nó sẽ điền vào nội dung.

Chúng tôi luôn truyền các cấu trúc này bằng tham chiếu (tức là chúng tôi truyền một con trỏ đến cấu trúc, không phải chính cấu trúc) và chúng tôi luôn chuyển kích thước của cấu trúc như một đối số khác.

Khi một hàm socket điền vào một cấu trúc, độ dài cũng được chuyển bằng tham chiếu, do đó giá trị của nó có thể được cập nhật bởi hàm. Chúng tôi gọi đây là các đối số giá trị-kết quả.

Luôn luôn đặt các biến cấu trúc thành NULL (tức là '\ 0') bằng cách sử dụng memset () cho các hàm bzero (), nếu không nó có thể nhận được các giá trị rác không mong muốn trong cấu trúc của bạn.

Khi một tiến trình máy khách muốn kết nối một máy chủ, máy khách phải có cách xác định máy chủ mà nó muốn kết nối. Nếu máy khách biết địa chỉ Internet 32-bit của máy chủ lưu trữ, máy khách có thể liên hệ với máy chủ đó. Nhưng làm thế nào để máy khách xác định tiến trình máy chủ cụ thể đang chạy trên máy chủ đó?

Để giải quyết vấn đề xác định một tiến trình máy chủ cụ thể đang chạy trên máy chủ, cả TCP và UDP đều đã xác định một nhóm các cổng nổi tiếng.

Theo mục đích của chúng tôi, một cổng sẽ được định nghĩa là một số nguyên trong khoảng từ 1024 đến 65535. Điều này là do tất cả các số cổng nhỏ hơn 1024 được coi là nổi tiếng - ví dụ: telnet sử dụng cổng 23, http sử dụng 80, ftp sử dụng 21, và như thế.

Các phép gán cổng cho các dịch vụ mạng có thể được tìm thấy trong tệp / etc / services. Nếu bạn đang viết máy chủ của riêng mình thì phải cẩn thận để gán một cổng cho máy chủ của bạn. Bạn nên đảm bảo rằng cổng này không được gán cho bất kỳ máy chủ nào khác.

Thông thường, một thông lệ là gán bất kỳ số cổng nào nhiều hơn 5000. Nhưng có nhiều tổ chức đã viết máy chủ có số cổng lớn hơn 5000. Ví dụ: Yahoo Messenger chạy trên 5050, SIP Server chạy trên 5060, v.v.

Cổng và dịch vụ mẫu

Đây là một danh sách nhỏ các dịch vụ và các cổng liên quan. Bạn có thể tìm thấy danh sách cập nhật nhất của các cổng internet và dịch vụ liên quan tại IANA - TCP / IP Port Assignments .

Service Port Number Service Description
tiếng vang 7 UDP / TCP gửi lại những gì nó nhận được.
bỏ đi 9 UDP / TCP loại bỏ đầu vào.
ban ngày 13 UDP / TCP trả về thời gian ASCII.
chargen 19 UDP / TCP trả về các ký tự.
ftp 21 Truyền tệp TCP.
telnet 23 Đăng nhập từ xa TCP.
smtp 25 Email TCP.
ban ngày 37 UDP / TCP trả về thời gian nhị phân.
tftp 69 Truyền tệp tầm thường UDP.
ngón tay 79 Thông tin TCP về người dùng.
http 80 TCP World Wide Web.
đăng nhập 513 Đăng nhập từ xa TCP.
WHO 513 UDP thông tin khác nhau về người dùng.
Xserver 6000 Cửa sổ TCP X (NB> 1023).

Chức năng cổng và dịch vụ

Unix cung cấp các chức năng sau để tìm nạp tên dịch vụ từ tệp / etc / services.

  • struct servent *getservbyname(char *name, char *proto) - Cuộc gọi này nhận tên dịch vụ và tên giao thức, và trả về số cổng tương ứng cho dịch vụ đó.

  • struct servent *getservbyport(int port, char *proto) - Cuộc gọi này nhận số cổng và tên giao thức, và trả về tên dịch vụ tương ứng.

Giá trị trả về cho mỗi hàm là một con trỏ đến một cấu trúc có dạng sau:

struct servent {
   char  *s_name;
   char  **s_aliases;
   int   s_port;
   char  *s_proto;
};

Đây là mô tả của các trường thành viên -

Thuộc tính Giá trị Sự miêu tả
tên của http Đây là tên chính thức của dịch vụ. Ví dụ: SMTP, FTP POP3, v.v.
s_aliases ALIAS Nó chứa danh sách các bí danh dịch vụ. Hầu hết thời gian, nó sẽ được đặt thành NULL.
s_port 80 Nó sẽ có số cổng liên quan. Ví dụ: đối với HTTP, nó sẽ là 80.
s_proto

TCP

UDP

Nó được đặt thành giao thức được sử dụng. Dịch vụ Internet được cung cấp bằng TCP hoặc UDP.

Thật không may, không phải tất cả các máy tính đều lưu trữ các byte bao gồm một giá trị multibyte theo cùng một thứ tự. Hãy xem xét một Internet 16 bit được tạo thành từ 2 byte. Có hai cách để lưu trữ giá trị này.

  • Little Endian - Trong lược đồ này, byte bậc thấp được lưu trên địa chỉ bắt đầu (A) và byte bậc cao được lưu trên địa chỉ tiếp theo (A + 1).

  • Big Endian - Trong lược đồ này, byte bậc cao được lưu trên địa chỉ bắt đầu (A) và byte bậc thấp được lưu trên địa chỉ tiếp theo (A + 1).

Để cho phép các máy có các quy ước thứ tự byte khác nhau giao tiếp với nhau, các giao thức Internet chỉ định quy ước thứ tự byte chuẩn cho dữ liệu được truyền qua mạng. Điều này được gọi là Thứ tự Byte Mạng.

Trong khi thiết lập kết nối ổ cắm Internet, bạn phải đảm bảo rằng dữ liệu trong các thành viên sin_port và sin_addr của cấu trúc sockaddr_in được thể hiện theo Thứ tự Mạng Byte.

Chức năng đặt hàng theo byte

Quy trình chuyển đổi dữ liệu giữa đại diện nội bộ của máy chủ lưu trữ và Thứ tự Byte Mạng như sau:

Chức năng Sự miêu tả
htons () Máy chủ lưu trữ vào mạng ngắn
htonl () Lưu trữ trên mạng dài
ntohl () Mạng để lưu trữ dài
ntohs () Network to Host Short

Dưới đây liệt kê một số chi tiết hơn về các chức năng này -

  • unsigned short htons(unsigned short hostshort) - Hàm này chuyển đổi các đại lượng 16 bit (2 byte) từ thứ tự byte chủ sang thứ tự byte mạng.

  • unsigned long htonl(unsigned long hostlong) - Hàm này chuyển đổi các đại lượng 32 bit (4 byte) từ thứ tự byte chủ sang thứ tự byte mạng.

  • unsigned short ntohs(unsigned short netshort) - Hàm này chuyển đổi các đại lượng 16 bit (2 byte) từ thứ tự byte mạng sang thứ tự byte máy chủ.

  • unsigned long ntohl(unsigned long netlong) - Hàm này chuyển đổi số lượng 32 bit từ thứ tự byte mạng sang thứ tự byte máy chủ.

Các hàm này là macro và dẫn đến việc chèn mã nguồn chuyển đổi vào chương trình gọi. Trên các máy endian nhỏ, mã sẽ thay đổi các giá trị xung quanh thành thứ tự byte mạng. Trên các máy big-end, không có mã nào được chèn vì không cần mã; các hàm được định nghĩa là null.

Chương trình xác định thứ tự Byte máy chủ

Giữ mã sau trong tệp byteorder.c , sau đó biên dịch nó và chạy nó trên máy của bạn.

Trong ví dụ này, chúng tôi lưu trữ giá trị hai byte 0x0102 trong số nguyên ngắn và sau đó xem xét hai byte liên tiếp, c [0] (địa chỉ A) và c [1] (địa chỉ A + 1) để xác định byte đặt hàng.

#include <stdio.h>

int main(int argc, char **argv) {

   union {
      short s;
      char c[sizeof(short)];
   }un;
	
   un.s = 0x0102;
   
   if (sizeof(short) == 2) {
      if (un.c[0] == 1 && un.c[1] == 2)
         printf("big-endian\n");
      
      else if (un.c[0] == 2 && un.c[1] == 1)
         printf("little-endian\n");
      
      else
         printf("unknown\n");
   }
   else {
      printf("sizeof(short) = %d\n", sizeof(short));
   }
	
   exit(0);
}

Đầu ra do chương trình này tạo ra trên máy Pentium như sau:

$> gcc byteorder.c $> ./a.out
little-endian
$>

Unix cung cấp các lệnh gọi hàm khác nhau để giúp bạn thao tác địa chỉ IP. Các hàm này chuyển đổi địa chỉ Internet giữa chuỗi ASCII (những gì con người thích sử dụng) và các giá trị nhị phân có thứ tự byte mạng (các giá trị được lưu trữ trong cấu trúc địa chỉ ổ cắm).

Ba lệnh gọi hàm sau được sử dụng để định địa chỉ IPv4:

  • int inet_aton (const char * strptr, struct in_addr * addrptr)
  • in_addr_t inet_addr (const char * strptr)
  • char * inet_ntoa (struct in_addr inaddr)

int inet_aton (const char * strptr, struct in_addr * addrptr)

Lệnh gọi hàm này chuyển đổi chuỗi được chỉ định trong ký hiệu chấm chuẩn Internet thành địa chỉ mạng và lưu trữ địa chỉ trong cấu trúc được cung cấp. Địa chỉ được chuyển đổi sẽ nằm trong Thứ tự Byte Mạng (các byte được sắp xếp theo thứ tự từ trái sang phải). Nó trả về 1 nếu chuỗi hợp lệ và 0 khi có lỗi.

Sau đây là ví dụ sử dụng -

#include <arpa/inet.h>

(...)

   int retval;
   struct in_addr addrptr
   
   memset(&addrptr, '\0', sizeof(addrptr));
   retval = inet_aton("68.178.157.132", &addrptr);

(...)

in_addr_t inet_addr (const char * strptr)

Lệnh gọi hàm này chuyển đổi chuỗi được chỉ định trong ký hiệu dấu chấm tiêu chuẩn Internet thành giá trị số nguyên phù hợp để sử dụng làm địa chỉ Internet. Địa chỉ được chuyển đổi sẽ nằm trong Thứ tự Byte Mạng (các byte được sắp xếp theo thứ tự từ trái sang phải). Nó trả về một địa chỉ IPv4 theo thứ tự byte mạng nhị phân 32 bit và INADDR_NONE do lỗi.

Sau đây là ví dụ sử dụng -

#include <arpa/inet.h>

(...)

   struct sockaddr_in dest;

   memset(&dest, '\0', sizeof(dest));
   dest.sin_addr.s_addr = inet_addr("68.178.157.132");
   
(...)

char * inet_ntoa (struct in_addr inaddr)

Lệnh gọi hàm này chuyển đổi địa chỉ máy chủ Internet được chỉ định thành một chuỗi trong ký hiệu dấu chấm chuẩn Internet.

Sau đây là ví dụ sử dụng -

#include <arpa/inet.h>

(...)

   char *ip;
   
   ip = inet_ntoa(dest.sin_addr);
   
   printf("IP Address is: %s\n",ip);
   
(...)

Chương này mô tả các chức năng ổ cắm lõi cần thiết để viết một máy khách và máy chủ TCP hoàn chỉnh.

Sơ đồ sau đây cho thấy toàn bộ tương tác giữa Máy khách và Máy chủ:

Chức năng ổ cắm

Để thực hiện I / O mạng, điều đầu tiên quy trình phải làm là gọi hàm socket, chỉ định loại giao thức truyền thông mong muốn và họ giao thức, v.v.

#include <sys/types.h>
#include <sys/socket.h>

int socket (int family, int type, int protocol);

Cuộc gọi này trả về một bộ mô tả ổ cắm mà bạn có thể sử dụng trong các cuộc gọi hệ thống sau này hoặc -1 khi bị lỗi.

Thông số

family - Nó chỉ định họ giao thức và là một trong những hằng số được hiển thị bên dưới -

gia đình Sự miêu tả
AF_INET Giao thức IPv4
AF_INET6 Giao thức IPv6
AF_LOCAL Giao thức miền Unix
AF_ROUTE Ổ cắm định tuyến
AF_KEY Ổ cắm Ket

Chương này không đề cập đến các giao thức khác ngoại trừ IPv4.

type- Nó chỉ định loại ổ cắm bạn muốn. Nó có thể nhận một trong các giá trị sau:

Kiểu Sự miêu tả
SOCK_STREAM Ổ cắm luồng
SOCK_DGRAM Datagram socket
SOCK_SEQPACKET Ổ cắm gói có trình tự
SOCK_RAW Ổ cắm thô

protocol - Đối số phải được đặt thành loại giao thức cụ thể được đưa ra bên dưới, hoặc 0 để chọn mặc định của hệ thống cho tổ hợp họ và loại đã cho -

Giao thức Sự miêu tả
IPPROTO_TCP Giao thức truyền tải TCP
IPPROTO_UDP Giao thức truyền tải UDP
IPPROTO_SCTP Giao thức vận chuyển SCTP

Các kết nối Chức năng

Các kết nối chức năng được sử dụng bởi một khách hàng TCP thiết lập kết nối với một máy chủ TCP.

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

Cuộc gọi này trả về 0 nếu nó kết nối thành công với máy chủ, nếu không nó sẽ trả về -1 khi bị lỗi.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • serv_addr - Nó là một con trỏ tới struct sockaddr chứa địa chỉ IP đích và cổng.

  • addrlen - Đặt nó thành sizeof (struct sockaddr).

Các ràng buộc Chức năng

Hàm ràng buộc chỉ định địa chỉ giao thức cục bộ cho một ổ cắm. Với các giao thức Internet, địa chỉ giao thức là sự kết hợp của địa chỉ IPv4 32 bit hoặc địa chỉ IPv6 128 bit, cùng với số cổng TCP hoặc UDP 16 bit. Chức năng này chỉ được gọi bởi máy chủ TCP.

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr,int addrlen);

Cuộc gọi này trả về 0 nếu nó liên kết thành công với địa chỉ, nếu không nó sẽ trả về -1 khi bị lỗi.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • my_addr - Nó là một con trỏ tới struct sockaddr chứa địa chỉ IP cục bộ và cổng.

  • addrlen - Đặt nó thành sizeof (struct sockaddr).

Bạn có thể tự động đặt địa chỉ IP và cổng của mình

Giá trị 0 cho số cổng có nghĩa là hệ thống sẽ chọn một cổng ngẫu nhiên và giá trị INADDR_ANY cho địa chỉ IP có nghĩa là địa chỉ IP của máy chủ sẽ được chỉ định tự động.

server.sin_port = 0;  		     
server.sin_addr.s_addr = INADDR_ANY;

NOTE- Tất cả các cổng dưới 1024 đều được bảo lưu. Bạn có thể đặt cổng trên 1024 và dưới 65535 trừ khi chúng là cổng được các chương trình khác sử dụng.

Các nghe Chức năng

Hàm lắng nghe chỉ được gọi bởi máy chủ TCP và nó thực hiện hai hành động:

  • Hàm lắng nghe chuyển đổi một ổ cắm chưa được kết nối thành một ổ cắm thụ động, cho biết rằng hạt nhân phải chấp nhận các yêu cầu kết nối đến trực tiếp đến ổ cắm này.

  • Đối số thứ hai của hàm này chỉ định số lượng kết nối tối đa mà hạt nhân phải xếp hàng cho ổ cắm này.

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd,int backlog);

Cuộc gọi này trả về 0 khi thành công, nếu không, nó trả về -1 khi bị lỗi.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • backlog - Đó là số lượng kết nối được phép.

Các chấp nhận Function

Hàm chấp nhận được gọi bởi máy chủ TCP để trả về kết nối đã hoàn thành tiếp theo từ phía trước của hàng đợi kết nối đã hoàn thành. Chữ ký của cuộc gọi như sau:

#include <sys/types.h>
#include <sys/socket.h>

int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

Lệnh gọi này trả về một bộ mô tả không âm khi thành công, nếu không nó sẽ trả về -1 khi bị lỗi. Bộ mô tả trả về được giả định là bộ mô tả ổ cắm máy khách và tất cả các hoạt động đọc-ghi sẽ được thực hiện trên bộ mô tả này để giao tiếp với máy khách.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • cliaddr - Nó là một con trỏ tới struct sockaddr chứa địa chỉ IP của máy khách và cổng.

  • addrlen - Đặt nó thành sizeof (struct sockaddr).

Các gửi Function

Các gửi chức năng được sử dụng để gửi dữ liệu qua ổ cắm dòng hoặc ổ cắm datagram được kết nối. Nếu bạn muốn gửi dữ liệu qua các ổ cắm dữ liệu KHÔNG KẾT NỐI, bạn phải sử dụng hàm sendto ().

Bạn có thể sử dụng lệnh gọi hệ thống write () để gửi dữ liệu. Chữ ký của nó như sau:

int send(int sockfd, const void *msg, int len, int flags);

Lệnh gọi này trả về số byte được gửi đi, nếu không, nó sẽ trả về -1 khi bị lỗi.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • msg - Nó là một con trỏ đến dữ liệu bạn muốn gửi.

  • len - Là độ dài của dữ liệu bạn muốn gửi (tính bằng byte).

  • flags - Nó được đặt thành 0.

Các recv Chức năng

Hàm recv được sử dụng để nhận dữ liệu qua ổ cắm luồng hoặc ổ cắm dữ liệu ĐÃ KẾT NỐI. Nếu bạn muốn nhận dữ liệu qua các ổ cắm dữ liệu KHÔNG KẾT NỐI, bạn phải sử dụng recvfrom ().

Bạn có thể sử dụng lệnh gọi hệ thống read () để đọc dữ liệu. Lời gọi này được giải thích trong chương hàm trợ giúp.

int recv(int sockfd, void *buf, int len, unsigned int flags);

Lệnh gọi này trả về số byte được đọc vào bộ đệm, nếu không, nó sẽ trả về -1 khi bị lỗi.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • buf - Nó là bộ đệm để đọc thông tin vào.

  • len - Là chiều dài tối đa của bộ đệm.

  • flags - Nó được đặt thành 0.

Các sendto Chức năng

Hàm sendto được sử dụng để gửi dữ liệu qua các ổ cắm dữ liệu KHÔNG KẾT NỐI. Chữ ký của nó như sau:

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

Cuộc gọi này trả về số byte được gửi, nếu không, nó trả về -1 khi bị lỗi.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • msg - Nó là một con trỏ đến dữ liệu bạn muốn gửi.

  • len - Là độ dài của dữ liệu bạn muốn gửi (tính bằng byte).

  • flags - Nó được đặt thành 0.

  • to - Nó là một con trỏ tới struct sockaddr cho máy chủ lưu trữ nơi dữ liệu phải được gửi đi.

  • tolen - Nó được đặt thành sizeof (struct sockaddr).

Các recvfrom Chức năng

Hàm recvfrom được sử dụng để nhận dữ liệu từ các ổ cắm dữ liệu KHÔNG KẾT NỐI.

int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);

Cuộc gọi này trả về số byte được đọc vào bộ đệm, nếu không, nó trả về -1 khi bị lỗi.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • buf - Nó là bộ đệm để đọc thông tin vào.

  • len - Là chiều dài tối đa của bộ đệm.

  • flags - Nó được đặt thành 0.

  • from - Nó là một con trỏ đến struct sockaddr cho máy chủ lưu trữ nơi dữ liệu phải được đọc.

  • fromlen - Nó được đặt thành sizeof (struct sockaddr).

Các gần Chức năng

Hàm đóng được sử dụng để đóng giao tiếp giữa máy khách và máy chủ. Cú pháp của nó như sau:

int close( int sockfd );

Cuộc gọi này trả về 0 khi thành công, nếu không, nó trả về -1 khi bị lỗi.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

Các shutdown Chức năng

Các shutdown chức năng được sử dụng để cách duyên dáng đóng giao tiếp giữa client và server. Chức năng này cho phép kiểm soát nhiều hơn so với chức năng đóng . Dưới đây là cú pháp tắt máy -

int shutdown(int sockfd, int how);

Cuộc gọi này trả về 0 khi thành công, nếu không, nó trả về -1 khi bị lỗi.

Thông số

  • sockfd - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • how - Đặt một trong các số -

    • 0 - chỉ ra rằng không được phép nhận,

    • 1 - chỉ ra rằng không được phép gửi và

    • 2- chỉ ra rằng cả gửi và nhận đều không được phép. Khi how được đặt thành 2, nó giống như close ().

Các chọn Chức năng

Hàm select cho biết bộ mô tả tệp được chỉ định nào đã sẵn sàng để đọc, sẵn sàng để ghi hoặc có tình trạng lỗi đang chờ xử lý.

Khi một ứng dụng gọi recv hoặc recvfrom , nó sẽ bị chặn cho đến khi dữ liệu đến cho socket đó. Một ứng dụng có thể đang thực hiện các xử lý hữu ích khác trong khi luồng dữ liệu đến trống. Một tình huống khác là khi một ứng dụng nhận dữ liệu từ nhiều ổ cắm.

Việc gọi recv hoặc recvfrom trên một ổ cắm không có dữ liệu trong hàng đợi đầu vào của nó ngăn chặn việc nhận dữ liệu ngay lập tức từ các ổ cắm khác. Lệnh gọi chức năng chọn giải quyết vấn đề này bằng cách cho phép chương trình thăm dò tất cả các tay cầm của ổ cắm để xem chúng có khả dụng cho các hoạt động đọc và ghi không bị chặn hay không.

Dưới đây là cú pháp của select -

int select(int  nfds, fd_set  *readfds, fd_set  *writefds, fd_set *errorfds, struct timeval *timeout);

Cuộc gọi này trả về 0 khi thành công, nếu không, nó trả về -1 khi bị lỗi.

Thông số

  • nfds- Nó chỉ định phạm vi của bộ mô tả tệp sẽ được kiểm tra. Hàm select () kiểm tra các bộ mô tả tệp trong phạm vi từ 0 đến nfds-1

  • readfds- Nó trỏ đến một đối tượng kiểu fd_set mà ở đầu vào, chỉ định các bộ mô tả tệp cần được kiểm tra để sẵn sàng đọc và trên đầu ra, cho biết bộ mô tả tệp nào đã sẵn sàng để đọc. Nó có thể là NULL để chỉ ra một tập hợp trống.

  • writefds- Nó trỏ đến một đối tượng kiểu fd_set mà ở đầu vào, chỉ định các bộ mô tả tệp cần được kiểm tra để sẵn sàng ghi và trên đầu ra, cho biết bộ mô tả tệp nào đã sẵn sàng để ghi. Nó có thể là NULL để chỉ ra một tập hợp trống.

  • exceptfds- Nó trỏ đến một đối tượng kiểu fd_set mà ở đầu vào, chỉ định các bộ mô tả tệp được kiểm tra các điều kiện lỗi đang chờ xử lý và trên đầu ra cho biết bộ mô tả tệp nào có các điều kiện lỗi đang chờ xử lý. Nó có thể là NULL để chỉ ra một tập hợp trống.

  • timeout- Nó trỏ đến một cấu trúc khoảng thời gian chỉ định thời gian cuộc gọi chọn sẽ thăm dò các bộ mô tả cho một hoạt động I / O khả dụng. Nếu giá trị thời gian chờ là 0, thì select sẽ trả về ngay lập tức. Nếu đối số thời gian chờ là NULL, thì select sẽ chặn cho đến khi ít nhất một tệp / ổ cắm xử lý sẵn sàng cho hoạt động I / O khả dụng. Nếu không, lựa chọn sẽ trở lại sau khi khoảng thời gian chờ đã trôi qua HOẶC khi ít nhất một bộ mô tả tệp / ổ cắm đã sẵn sàng cho một hoạt động I / O.

Giá trị trả về từ select là số lượng xử lý được chỉ định trong bộ mô tả tệp đã sẵn sàng cho I / O. Nếu đạt đến giới hạn thời gian được chỉ định bởi trường thời gian chờ, hãy chọn trả về 0. Các macro sau tồn tại để thao tác tập hợp bộ mô tả tệp:

  • FD_CLR(fd, &fdset)- Xóa bit cho bộ mô tả tệp fd trong bộ mô tả tệp fdset.

  • FD_ISSET(fd, &fdset)- Trả về giá trị khác 0 nếu bit cho bộ mô tả tệp fd được đặt trong bộ mô tả tệp được fdset trỏ tới và 0 nếu ngược lại.

  • FD_SET(fd, &fdset) - Đặt bit cho bộ mô tả tệp fd trong bộ mô tả tệp fdset.

  • FD_ZERO(&fdset) - Khởi tạo bộ mô tả tập tin fdset có bit 0 cho tất cả các bộ mô tả tập tin.

Hành vi của các macro này không được xác định nếu đối số fd nhỏ hơn 0 hoặc lớn hơn hoặc bằng FD_SETSIZE.

Thí dụ

fd_set fds;

struct timeval tv;

/* do socket initialization etc.
tv.tv_sec = 1;
tv.tv_usec = 500000;

/* tv now represents 1.5 seconds */
FD_ZERO(&fds);

/* adds sock to the file descriptor set */
FD_SET(sock, &fds); 

/* wait 1.5 seconds for any data to be read from any single socket */
select(sock+1, &fds, NULL, NULL, &tv);

if (FD_ISSET(sock, &fds)) {
   recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len);
   /* do something */
}
else {
   /* do something else */
}

Chương này mô tả tất cả các hàm trợ giúp, được sử dụng trong khi lập trình socket. Các chức năng trợ giúp khác được mô tả trong các chương -Ports and Servicesvà Mạng Byte Orders.

Các ghi Chức năng

Hàm ghi cố gắng ghi các byte nbyte từ bộ đệm được trỏ bởi buf vào tệp được liên kết với bộ mô tả tệp đang mở, các tệp .

Bạn cũng có thể sử dụng hàm send () để gửi dữ liệu đến một tiến trình khác.

#include <unistd.h>

int write(int fildes, const void *buf, int nbyte);

Sau khi hoàn thành thành công, write () trả về số byte thực sự được ghi vào tệp được liên kết với các bộ lọc. Con số này không bao giờ lớn hơn nbyte. Nếu không, -1 được trả về.

Thông số

  • fildes - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • buf - Nó là một con trỏ đến dữ liệu bạn muốn gửi.

  • nbyte- Là số byte được viết. Nếu nbyte bằng 0, write () sẽ trả về 0 và không có kết quả nào khác nếu tệp là tệp thông thường; nếu không, kết quả là không xác định.

Các đọc Chức năng

Hàm đọc cố gắng đọc các byte nbyte từ tệp được liên kết với bộ đệm, các bộ lọc, vào bộ đệm được trỏ đến bởi buf.

Bạn cũng có thể sử dụng hàm recv () để đọc dữ liệu cho quá trình khác.

#include <unistd.h>

int read(int fildes, const void *buf, int nbyte);

Sau khi hoàn thành thành công, write () trả về số byte thực sự được ghi vào tệp được liên kết với các bộ lọc. Con số này không bao giờ lớn hơn nbyte. Nếu không, -1 được trả về.

Thông số

  • fildes - Nó là một bộ mô tả socket được trả về bởi hàm socket.

  • buf - Nó là bộ đệm để đọc thông tin vào.

  • nbyte - Là số byte cần đọc.

Các ngã ba Hàm

Hàm fork tạo ra một quy trình mới. Tiến trình mới được gọi là tiến trình con sẽ là một bản sao chính xác của tiến trình gọi (tiến trình mẹ). Tiến trình con kế thừa nhiều thuộc tính từ tiến trình mẹ.

#include <sys/types.h>
#include <unistd.h>

int fork(void);

Sau khi hoàn thành thành công, fork () trả về 0 cho tiến trình con và ID tiến trình của tiến trình con cho tiến trình mẹ. Nếu không, -1 được trả về quy trình mẹ, không có quy trình con nào được tạo và errno được đặt để chỉ ra lỗi.

Thông số

  • void - Có nghĩa là không cần tham số.

Các bzero Chức năng

Hàm bzero đặt các byte rỗng nbyte trong chuỗi s . Hàm này được sử dụng để thiết lập tất cả các cấu trúc socket có giá trị null.

void bzero(void *s, int nbyte);

Hàm này không trả về bất cứ thứ gì.

Thông số

  • s- Nó chỉ định chuỗi phải được lấp đầy bởi các byte rỗng. Đây sẽ là một biến cấu trúc trỏ tới socket.

  • nbyte- Nó chỉ định số lượng byte được điền bằng giá trị null. Đây sẽ là kích thước của cấu trúc ổ cắm.

Các bcmp Chức năng

Hàm bcmp so sánh chuỗi byte s1 với chuỗi byte s2. Cả hai chuỗi đều được giả định là dài nbyte byte.

int bcmp(const void *s1, const void *s2, int nbyte);

Hàm này trả về 0 nếu cả hai chuỗi giống hệt nhau, 1 nếu ngược lại. Hàm bcmp () luôn trả về 0 khi nbyte bằng 0.

Thông số

  • s1 - Nó chỉ định chuỗi đầu tiên được so sánh.

  • s2 - Nó chỉ định chuỗi thứ hai được so sánh.

  • nbyte - Nó chỉ định số byte được so sánh.

Các bcopy Chức năng

Hàm bcopy sao chép nbyte byte từ chuỗi s1 sang chuỗi s2. Các chuỗi chồng chéo được xử lý chính xác.

void bcopy(const void *s1, void *s2, int nbyte);

Hàm này không trả về bất cứ thứ gì.

Thông số

  • s1 - Nó chỉ định chuỗi nguồn.

  • s2v - Nó chỉ định chuỗi đích.

  • nbyte - Nó chỉ định số byte sẽ được sao chép.

Các memset Chức năng

Hàm memset cũng được sử dụng để thiết lập các biến cấu trúc theo cách tương tự nhưbzero. Hãy xem cú pháp của nó, được đưa ra bên dưới.

void *memset(void *s, int c, int nbyte);

Hàm này trả về một con trỏ đến void; trên thực tế, một con trỏ đến bộ nhớ được thiết lập và bạn cần phân loại nó cho phù hợp.

Thông số

  • s - Nó chỉ định nguồn được thiết lập.

  • c - Nó chỉ định ký tự để đặt trên các địa điểm nbyte.

  • nbyte - Nó chỉ định số byte được thiết lập.

Để đặt một quy trình thành một máy chủ TCP, bạn cần làm theo các bước dưới đây:

  • Tạo một ổ cắm với lệnh gọi hệ thống socket () .

  • Liên kết socket với một địa chỉ bằng lệnh gọi hệ thống bind () . Đối với ổ cắm máy chủ trên Internet, địa chỉ bao gồm số cổng trên máy chủ.

  • Lắng nghe các kết nối với cuộc gọi hệ thống nghe () .

  • Chấp nhận kết nối với lệnh gọi hệ thống accept () . Cuộc gọi này thường chặn cho đến khi một máy khách kết nối với máy chủ.

  • Gửi và nhận dữ liệu bằng các lệnh gọi hệ thống read ()write () .

Bây giờ chúng ta hãy đặt các bước này dưới dạng mã nguồn. Đặt mã này vào tệp server.c và biên dịch nó bằng trình biên dịch gcc .

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int  n;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
      
   /* Now start listening for the clients, here process will
      * go in sleep mode and will wait for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   /* Accept actual connection from the client */
   newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
	
   if (newsockfd < 0) {
      perror("ERROR on accept");
      exit(1);
   }
   
   /* If connection is established then start communicating */
   bzero(buffer,256);
   n = read( newsockfd,buffer,255 );
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   
   /* Write a response to the client */
   n = write(newsockfd,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
      
   return 0;
}

Xử lý nhiều kết nối

Để cho phép máy chủ xử lý nhiều kết nối đồng thời, chúng tôi thực hiện các thay đổi sau trong đoạn mã trên:

  • Đặt câu lệnh accept và đoạn mã sau vào một vòng lặp vô hạn.

  • Sau khi kết nối được thiết lập, hãy gọi fork () để tạo một quy trình mới.

  • Tiến trình con sẽ đóng sockfd và gọi hàm doprocessing , chuyển bộ mô tả tệp socket mới làm đối số. Khi hai quá trình đã hoàn thành cuộc trò chuyện của chúng, như được chỉ ra bằng cách trả về doprocessing () , quá trình này chỉ cần thoát.

  • Tiến trình mẹ đóng newsockfd . Vì tất cả mã này nằm trong một vòng lặp vô hạn, nó sẽ quay trở lại câu lệnh accept để đợi kết nối tiếp theo.

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

void doprocessing (int sock);

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int n, pid;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
   
   /* Now start listening for the clients, here
      * process will go in sleep mode and will wait
      * for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   while (1) {
      newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
		
      if (newsockfd < 0) {
         perror("ERROR on accept");
         exit(1);
      }
      
      /* Create child process */
      pid = fork();
		
      if (pid < 0) {
         perror("ERROR on fork");
         exit(1);
      }
      
      if (pid == 0) {
         /* This is the client process */
         close(sockfd);
         doprocessing(newsockfd);
         exit(0);
      }
      else {
         close(newsockfd);
      }
		
   } /* end of while */
}

Đoạn mã sau đây cho thấy một cách triển khai đơn giản của hàm doprocessing .

void doprocessing (int sock) {
   int n;
   char buffer[256];
   bzero(buffer,256);
   n = read(sock,buffer,255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   n = write(sock,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
	
}

Để đặt một quy trình trở thành ứng dụng khách TCP, bạn cần thực hiện theo các bước được đưa ra bên dưới & trừ;

  • Tạo một ổ cắm với lệnh gọi hệ thống socket () .

  • Kết nối ổ cắm với địa chỉ của máy chủ bằng lệnh gọi hệ thống connect () .

  • Gửi và nhận dữ liệu. Có một số cách để thực hiện việc này, nhưng cách đơn giản nhất là sử dụng lệnh gọi hệ thống read ()write () .

Bây giờ chúng ta hãy đặt các bước này dưới dạng mã nguồn. Đặt mã này vào tệpclient.c và biên dịch nó với gcc trình biên dịch.

Chạy chương trình này và chuyển tên máy chủsố cổng của máy chủ để kết nối với máy chủ mà bạn phải chạy trong một cửa sổ Unix khác.

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main(int argc, char *argv[]) {
   int sockfd, portno, n;
   struct sockaddr_in serv_addr;
   struct hostent *server;
   
   char buffer[256];
   
   if (argc < 3) {
      fprintf(stderr,"usage %s hostname port\n", argv[0]);
      exit(0);
   }
	
   portno = atoi(argv[2]);
   
   /* Create a socket point */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
	
   server = gethostbyname(argv[1]);
   
   if (server == NULL) {
      fprintf(stderr,"ERROR, no such host\n");
      exit(0);
   }
   
   bzero((char *) &serv_addr, sizeof(serv_addr));
   serv_addr.sin_family = AF_INET;
   bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
   serv_addr.sin_port = htons(portno);
   
   /* Now connect to the server */
   if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR connecting");
      exit(1);
   }
   
   /* Now ask for a message from the user, this message
      * will be read by server
   */
	
   printf("Please enter the message: ");
   bzero(buffer,256);
   fgets(buffer,255,stdin);
   
   /* Send message to the server */
   n = write(sockfd, buffer, strlen(buffer));
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
   
   /* Now read server response */
   bzero(buffer,256);
   n = read(sockfd, buffer, 255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
	
   printf("%s\n",buffer);
   return 0;
}

Đây là danh sách tất cả các chức năng liên quan đến lập trình socket.

Chức năng cổng và dịch vụ

Unix cung cấp các chức năng sau để tìm nạp tên dịch vụ từ tệp / etc / services.

  • struct servent *getservbyname(char *name, char *proto) - Cuộc gọi này nhận tên dịch vụ và tên giao thức và trả về số cổng tương ứng cho dịch vụ đó.

  • struct servent *getservbyport(int port, char *proto) - Cuộc gọi này nhận một số cổng và một tên giao thức và trả về tên dịch vụ tương ứng.

Chức năng đặt hàng theo byte

  • unsigned short htons (unsigned short hostshort) - Hàm này chuyển đổi các đại lượng 16 bit (2 byte) từ thứ tự byte chủ sang thứ tự byte mạng.

  • unsigned long htonl (unsigned long hostlong) - Hàm này chuyển đổi các đại lượng 32 bit (4 byte) từ thứ tự byte chủ sang thứ tự byte mạng.

  • unsigned short ntohs (unsigned short netshort) - Hàm này chuyển đổi các đại lượng 16 bit (2 byte) từ thứ tự byte mạng sang thứ tự byte máy chủ.

  • unsigned long ntohl (unsigned long netlong) - Hàm này chuyển đổi số lượng 32 bit từ thứ tự byte mạng sang thứ tự byte máy chủ.

Chức năng địa chỉ IP

  • int inet_aton (const char *strptr, struct in_addr *addrptr)- Lệnh gọi hàm này chuyển đổi chuỗi được chỉ định, trong ký hiệu dấu chấm tiêu chuẩn Internet, thành địa chỉ mạng và lưu trữ địa chỉ trong cấu trúc được cung cấp. Địa chỉ được chuyển đổi sẽ nằm trong Thứ tự Byte Mạng (các byte được sắp xếp theo thứ tự từ trái sang phải). Nó trả về 1 nếu chuỗi hợp lệ và 0 khi có lỗi.

  • in_addr_t inet_addr (const char *strptr)- Lệnh gọi hàm này chuyển đổi chuỗi được chỉ định, trong ký hiệu dấu chấm tiêu chuẩn Internet, thành một giá trị số nguyên phù hợp để sử dụng làm địa chỉ Internet. Địa chỉ được chuyển đổi sẽ nằm trong Thứ tự Byte Mạng (các byte được sắp xếp theo thứ tự từ trái sang phải). Nó trả về một địa chỉ IPv4 theo thứ tự byte mạng nhị phân 32 bit và INADDR_NONE do lỗi.

  • char *inet_ntoa (struct in_addr inaddr) - Lệnh gọi hàm này chuyển đổi địa chỉ máy chủ Internet được chỉ định thành một chuỗi trong ký hiệu dấu chấm chuẩn Internet.

Chức năng cốt lõi của ổ cắm

  • int socket (int family, int type, int protocol) - Cuộc gọi này trả về một bộ mô tả ổ cắm mà bạn có thể sử dụng trong các cuộc gọi hệ thống sau này hoặc nó cho bạn -1 khi bị lỗi.

  • int connect (int sockfd, struct sockaddr *serv_addr, int addrlen)- Chức năng kết nối được sử dụng bởi máy khách TCP để thiết lập kết nối với máy chủ TCP. Cuộc gọi này trả về 0 nếu nó kết nối thành công với máy chủ, ngược lại nó trả về -1.

  • int bind(int sockfd, struct sockaddr *my_addr,int addrlen)- Hàm ràng buộc gán một địa chỉ giao thức cục bộ cho một ổ cắm. Cuộc gọi này trả về 0 nếu nó liên kết thành công với địa chỉ, ngược lại nó trả về -1.

  • int listen(int sockfd, int backlog)- Chức năng lắng nghe chỉ được gọi bởi một máy chủ TCP để lắng nghe yêu cầu của máy khách. Cuộc gọi này trả về 0 khi thành công, nếu không nó trả về -1.

  • int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)- Chức năng chấp nhận được gọi bởi máy chủ TCP để chấp nhận các yêu cầu của khách hàng và thiết lập kết nối thực tế. Cuộc gọi này trả về một bộ mô tả không âm khi thành công, nếu không nó trả về -1.

  • int send(int sockfd, const void *msg, int len, int flags)- Chức năng gửi được sử dụng để gửi dữ liệu qua ổ cắm luồng hoặc ổ cắm dữ liệu ĐÃ KẾT NỐI. Lời gọi này trả về số byte được gửi đi, nếu không, nó trả về -1.

  • int recv (int sockfd, void *buf, int len, unsigned int flags)- Hàm recv được sử dụng để nhận dữ liệu qua ổ cắm luồng hoặc ổ cắm dữ liệu ĐÃ KẾT NỐI. Cuộc gọi này trả về số byte được đọc vào bộ đệm, nếu không, nó trả về -1 khi bị lỗi.

  • int sendto (int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)- Hàm sendto được sử dụng để gửi dữ liệu qua các ổ cắm dữ liệu KHÔNG KẾT NỐI. Cuộc gọi này trả về số byte được gửi, nếu không, nó trả về -1 khi bị lỗi.

  • int recvfrom (int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen)- Hàm recvfrom được sử dụng để nhận dữ liệu từ các ổ cắm dữ liệu KHÔNG KẾT NỐI. Cuộc gọi này trả về số byte được đọc vào bộ đệm, nếu không, nó trả về -1 khi bị lỗi.

  • int close (int sockfd)- Chức năng đóng được sử dụng để đóng giao tiếp giữa máy khách và máy chủ. Cuộc gọi này trả về 0 khi thành công, nếu không nó trả về -1.

  • int shutdown (int sockfd, int how)- Chức năng tắt máy được sử dụng để đóng giao tiếp giữa máy khách và máy chủ một cách duyên dáng. Chức năng này cho phép kiểm soát nhiều hơn so với chức năng đóng. Nó trả về 0 khi thành công, -1 nếu ngược lại.

  • int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) - Chức năng này được sử dụng để đọc hoặc ghi nhiều socket.

Các chức năng của Socket Helper

  • int write (int fildes, const void *buf, int nbyte)- Hàm ghi cố gắng ghi các byte nbyte từ bộ đệm được buf trỏ đến vào tệp được liên kết với bộ mô tả tệp đang mở, các tệp. Sau khi hoàn thành thành công, write () trả về số byte thực sự được ghi vào tệp được liên kết với các bộ lọc. Con số này không bao giờ lớn hơn nbyte. Nếu không, -1 được trả về.

  • int read (int fildes, const void *buf, int nbyte)- Hàm đọc cố gắng đọc các byte nbyte từ tệp được liên kết với bộ mô tả tệp đang mở, các bộ lọc, vào bộ đệm được trỏ đến bởi buf. Sau khi hoàn thành thành công, write () trả về số byte thực sự được ghi vào tệp được liên kết với các bộ lọc. Con số này không bao giờ lớn hơn nbyte. Nếu không, -1 được trả về.

  • int fork (void)- Chức năng fork tạo ra một quy trình mới. Tiến trình mới, được gọi là tiến trình con, sẽ là một bản sao chính xác của tiến trình gọi (tiến trình mẹ).

  • void bzero (void *s, int nbyte)- Hàm bzero đặt nbyte null byte trong chuỗi s. Hàm này sẽ được sử dụng để thiết lập tất cả các cấu trúc socket có giá trị null.

  • int bcmp (const void *s1, const void *s2, int nbyte)- Hàm bcmp so sánh chuỗi byte s1 với chuỗi byte s2. Cả hai chuỗi đều được giả định là dài nbyte byte.

  • void bcopy (const void *s1, void *s2, int nbyte)- Hàm bcopy sao chép nbyte byte từ chuỗi s1 sang chuỗi s2. Các chuỗi chồng chéo được xử lý chính xác.

  • void *memset(void *s, int c, int nbyte) - Hàm memset cũng được sử dụng để thiết lập các biến cấu trúc theo cách tương tự như bzero.