Vì sao app VPN iOS của bạn hiển thị mất kết nối khi đường hầm vẫn chạy
App iOS của bạn điều khiển đường hầm nhưng không sở hữu nó, và trạng thái của trình quản lý được lưu trong bộ nhớ đệm. Hãy tải tuỳ chọn trước, rồi đọc trạng thái và bộ đếm giao diện.
App của bạn báo mất kết nối, thanh menu báo đã kết nối, và lưu lượng rõ ràng đang chảy. Khoảng cách đó không phải là điều kiện đua mà bạn sửa bằng cách thử lại: app và đường hầm là hai tiến trình riêng, và đối tượng mà mã của bạn đọc là một khung nhìn được lưu đệm của cấu hình do hệ thống lưu giữ. Bề mặt Personal VPN của Apple là API điều khiển, không phải đường dẫn dữ liệu, và phần lớn hành vi kỳ lạ của nó bắt nguồn từ đúng một sự thật đó. Đây là những gì thực sự xảy ra ở mỗi lời gọi.
Đối tượng trình quản lý là khung nhìn lưu đệm, không phải đường hầm
NETunnelProviderManager được điền từ kho tuỳ chọn do hệ thống sở hữu, nên các thuộc tính của nó không có ý nghĩa gì cho đến khi loadFromPreferences hoàn tất. Đọc connection.status trước đó và bạn nhận được giá trị mặc định, và đó là lý do lần làm mới trạng thái đầu tiên sau khi khởi động lại báo mất kết nối trong khi đường hầm vẫn chạy. Hãy tải trước, rồi đọc trạng thái và cấu hình giao thức từ cùng một trình xử lý hoàn tất. Theo dõi các thay đổi về sau qua NEVPNStatusDidChange thay vì thăm dò, và lưu ý rằng thông báo cũng kích hoạt cho các kết nối do On Demand khởi tạo, nơi không hề có lời gọi app nào để trả về thứ gì. Điều tương tự áp dụng cho cài đặt giao thức: chúng được đọc từ cấu hình đã tải, không phải từ thứ app truyền vào.
Bản thân đường hầm sống trong tiến trình nhà cung cấp Network Extension, nên app có thể mất trạng thái của chính nó mà không mất kết nối. Mọi thứ giao diện hiển thị phải được dựng lại từ trình quản lý, không phải ghi nhớ từ phiên trước.
Thứ tự khởi động: lưu, tải, rồi khởi chạy
Kết nối dựa trên nhà cung cấp cần ba bước theo đúng thứ tự. Ghi cấu hình nhà cung cấp và gọi saveToPreferences, tải trình quản lý để cấu hình đã lưu chính là cấu hình bạn đang giữ, rồi khởi chạy qua NETunnelProviderSession.startVPNTunnel(). Bỏ qua bước lưu thì khởi chạy không có gì để khởi chạy; tải trước khi lưu thì trình quản lý vẫn phản ánh cấu hình cũ.
Giá trị boolean mà startVPNTunnel trả về chỉ nói rằng yêu cầu đã được chấp nhận. Lỗi ở cấp nhà cung cấp đến sau dưới dạng chuyển trạng thái sang mất kết nối, mà bạn quan sát qua thông báo NEVPNStatusDidChange, và trình xử lý thông báo phải đọc lại connection.status sau loadFromPreferences vì giá trị lưu đệm có thể vẫn là giá trị cũ. Lỗi ở cấp yêu cầu trả về ngay từ dạng ném ngoại lệ của lời gọi, hoặc qua tham số đầu ra lỗi của startVPNTunnelAndReturnError:. Mã coi giá trị trả về là kết quả sẽ báo một kết nối chưa bao giờ được thiết lập. Lỗi âm thầm còn lại là định danh gói của nhà cung cấp không khớp với tiện ích đã cài: yêu cầu bị từ chối, và không có gì trong thông điệp chỉ ra định danh đó. Nếu định danh sai, yêu cầu thường thất bại ngay, trước khi bất kỳ dòng nhật ký nào của nhà cung cấp xuất hiện.
Tải lại tuỳ chọn sau khi khởi chạy là điều làm cho các màn hình trạng thái đúng. Trình quản lý không đẩy cho bạn cấu hình mới khi hệ thống thay đổi nó.
Vật liệu tin cậy đến qua hồ sơ, không bao giờ từ mã app
App không có cách được hỗ trợ để thêm chứng chỉ gốc vào kho tin cậy của iOS, nên cấu hình Personal VPN chỉ có thể tham chiếu vật liệu mà thiết bị đã tin cậy: chuỗi do hồ sơ đã cài, do quản lý thiết bị hoặc do CA mà hệ thống tin cậy vì lý do khác thiết lập. Một app tạo CA riêng ở lần chạy đầu và mong hệ thống công nhận nó sẽ thất bại ở bước xác thực bên trong nhà cung cấp, nơi giao diện app không thể thấy lý do.
Hãy coi ranh giới đó là ràng buộc thiết kế đúng như nó vốn có và gửi neo tin cậy cùng với hồ sơ ghi danh. Giữ xác thực VPN tách khỏi quyết định tin cậy, vì chúng thất bại ở những nơi khác nhau và những người khác nhau gỡ lỗi chúng.
Bộ đếm đến từ giao diện, không phải từ API VPN
Trình quản lý phơi ra trạng thái và cấu hình, và không có gì đếm byte cho mỗi đường hầm. Nếu bạn cần số liệu sử dụng, gọi getifaddrs() và đọc if_data cho giao diện tương ứng: en0 cho Wi-Fi, các giao diện pdp_ip cho di động và thiết bị utun cho chính đường hầm. Các bộ đếm của utun mô tả lưu lượng bên trong và bao gồm phần phụ trội của giao thức.
Những con số này là ước lượng, không phải kế toán theo từng app, và chúng được đặt lại khi giao diện được tạo lại. Hãy dán nhãn như vậy trong giao diện, nếu không ai đó sẽ dùng chúng để xuất hoá đơn cho khách. Nếu bạn cần số liệu theo từng app, bạn cần một cơ chế hoàn toàn khác, không phải cách đọc tốt hơn các trường này.
Việc dọn dẹp thuộc về stopVPNTunnel, không phải lúc kết thúc
applicationWillTerminate cho bạn vài giây trước khi hệ thống có thể giết tiến trình, và đó không phải là nơi đáng tin để tháo một kết nối. Gửi lệnh dừng tới stopVPNTunnel() hoặc xử lý nó trong callback dừng của nhà cung cấp, và giữ lại định danh trình quản lý hoặc cấu hình mà bạn đã khởi chạy, để lệnh dừng nhắm đúng kết nối đó thay vì mọi cấu hình trên thiết bị.
Lưu định danh đó bên cạnh hồ sơ mà nó thuộc về, vì đối tượng trình quản lý không ghi lại cấu hình nào người dùng đã chọn. Và hãy nhớ rằng kết nối do On Demand dựng lên không có lời gọi app nào phía sau, nên màn hình trạng thái vẫn phải tải tuỳ chọn trước khi vẽ bất cứ thứ gì.