Được rồi, trước hết, nếu bạn có một cái gì đó và nó đang hoạt động, thường thì nên để nó như thế. Tại sao phải sửa những gì không bị hỏng?
Nhưng nếu bạn gặp sự cố và thực sự muốn viết lại mã mạng của mình, tôi nghĩ bạn có bốn lựa chọn chính:
- Mã chặn đa luồng (những gì bạn đang làm ngay bây giờ)
- Ổ cắm không chặn với thông báo kích hoạt cấp
- Ổ cắm không chặn với thông báo thay đổi sẵn sàng
- Ổ cắm không đồng bộ
Đã viết rất nhiều máy khách và máy chủ nhiều người chơi (từ ngang hàng đến nhiều người chơi), tôi muốn nghĩ rằng tùy chọn 2 dẫn đến độ phức tạp ít nhất, với hiệu suất khá tốt, cho cả phần máy chủ và máy khách của trò chơi. Gần một giây, tôi sẽ chọn tùy chọn 4, nhưng điều đó thường đòi hỏi bạn phải suy nghĩ lại về toàn bộ chương trình của mình và tôi chủ yếu thấy nó hữu ích cho các máy chủ chứ không phải máy khách.
Đặc biệt, tôi muốn khuyên bạn không nên chặn các socket trong môi trường đa luồng, vì làm như vậy thường dẫn đến khóa và các tính năng đồng bộ hóa khác không chỉ làm tăng đáng kể độ phức tạp của mã, mà còn có thể làm giảm hiệu suất của nó, vì một số luồng chờ khác.
Nhưng trên hết, hầu hết các triển khai ổ cắm (và hầu hết các triển khai I / O tại đó) đều không chặn ở mức thấp nhất. Các hoạt động chặn được cung cấp đơn giản để đơn giản hóa việc phát triển các chương trình tầm thường. Khi một ổ cắm bị chặn, CPU trong luồng đó hoàn toàn không hoạt động, vậy tại sao lại xây dựng một sự trừu tượng hóa không chặn đối với sự trừu tượng hóa chặn đối với một nhiệm vụ không chặn?
Lập trình ổ cắm không chặn là một chút khó khăn nếu bạn chưa thử nó, nhưng hóa ra nó khá đơn giản và nếu bạn đang thực hiện bỏ phiếu đầu vào, bạn đã có suy nghĩ để thực hiện các ổ cắm không chặn.
Điều đầu tiên bạn muốn làm là đặt ổ cắm thành không chặn. Bạn làm điều đó với fcntl()
.
Sau đó, trước khi bạn làm send()
, recv()
, sendto()
, recvfrom()
, accept()
( connect()
là một khác nhau bit) hoặc cuộc gọi khác mà có thể ngăn chặn các chủ đề, bạn gọi select()
vào ổ cắm. select()
cho bạn biết liệu một thao tác đọc hoặc ghi tiếp theo có thể được thực hiện trên ổ cắm mà không bị chặn hay không. Nếu đó là trường hợp, bạn có thể thực hiện thao tác bạn muốn một cách an toàn và ổ cắm sẽ không chặn.
Bao gồm điều này trong một trò chơi khá đơn giản. Nếu bạn đã có một vòng lặp trò chơi, ví dụ như thế này:
while game_is_running do
poll_input()
update_world()
do_sounds()
draw_world()
end
bạn có thể thay đổi nó thành như thế này:
while game_is_running do
poll_input()
read_network()
update_world()
do_sounds()
write_network()
draw_world()
end
Ở đâu
function read_network()
while select(socket, READ) do
game.net_input.enqueue(recv(socket))
end
end
và
function write_network()
while not game.net_output.empty and select(socket, WRITE) do
send(socket, game.net_output.dequeue())
end
end
Về tài nguyên, cuốn sách duy nhất tôi nghĩ mọi người phải có trong giá sách của mình, ngay cả khi đó là cuốn sách duy nhất họ có, là " Lập trình mạng Unix, Tập 1. " của cố Richard Stevens. Không thành vấn đề nếu bạn làm Windows hoặc lập trình ổ cắm ngôn ngữ hoặc hệ điều hành khác. Đừng nghĩ rằng bạn hiểu ổ cắm cho đến khi bạn đọc cuốn sách này.
Một tài nguyên khác mà bạn có thể tìm thấy tổng quan chung về các giải pháp khả dụng về mặt lập trình nhiều ổ cắm (chủ yếu liên quan đến lập trình máy chủ) là trang này .