
















Preview text:
lOMoAR cPSD| 61549570
ĐẠI HỌC BÁCH KHOA HÀ NỘI
Trường Công nghệ thông tin và Truyền thông
------------------🙡🕮🙣------------------
BÁO CÁO BÀI TẬP LỚN MÔN LẬP TRÌNH MẠNG
Đề tài: Xây dựng trò chơi theo lượt Battleship Online Sinh viên thực hiện:
Đỗ Mạnh Phương 20225660 Lê Quang Khải 20225638 Giảng viên hướng dẫn: TS Đặng Tuấn Linh
Hà Nội, ngày 31 tháng 12 năm 2024 lOMoAR cPSD| 61549570 MỤC LỤC
I. Chương I: Giới thiệu đề tài
1. Sơ lược về đề tài 2. Bố cục báo cáo
II. Chương II: Cơ sở lý thuyết
1. Kiến thức cơ bản về lập trình socket TCP
2. Kiến thức về quản lý vào ra và hàm select()
3. Kiến thức về QT trong C++
4. Sử dụng SQLite3 quản lý dữ liệu người chơi
III. Chương III: Kiến trúc mạng 1. Server
IV. Chương IV: Cơ chế game
1. Cơ chế đặt tàu và bắn tàu
2. Chức năng nâng cao : Chat trong game
V. Chương V: Phân chia công việc VI. Chương VI: Kết luận
CHƯƠNG I: Giới thiệu đề tài
1. Sơ lược về đề tài
Đã từ lâu, Battleship là một trò chơi chiến thuật nổi tiếng, trong đó hai người
chơi cạnh tranh để đánh bại đối phương bằng cách tìm và đánh chìm các tàu chiến
của họ. Mỗi người chơi sẽ đặt các tàu chiến của mình trên một bảng lưới (grid)
10x10 và lần lượt phát tín hiệu bằng cách "bắn" vào các tọa độ trên bảng đối
phương. Mục tiêu của trò chơi là xác định vị trí các tàu đối thủ và bắn trúng chúng
trước khi đối thủ làm điều tương tự. Trò chơi không chỉ đòi hỏi sự tính toán chiến
lược mà còn yêu cầu khả năng phán đoán và sự may mắn. Với sự hấp dẫn và thử
thách trong việc đối kháng qua các lượt đi, Battleship đã trở thành một trò chơi
kinh điển trong làng boardgame, mang lại những phút giây thư giãn và kịch tính cho người chơi.
Vậy nên bọn em đã quyết định tái hiện lại trò chơi này trên máy tính bằng
giao diện đồ họa kết hợp cùng các kiến thức đã học được trong bộ môn Thực hành Lập trình mạng. lOMoAR cPSD| 61549570 2. Bố cục báo cáo
Chương 1: giới thiệu đề tài
Ở chương này nhóm em sẽ tóm tắt chủ đề mà nhóm thực hiện và nêu tóm tắt
mục đích của từng chương để thầy có thể khái quát được báo cáo gồm những gì và nói về gì.
Chương 2: Cơ sở lý thuyết
Nhóm em sẽ nêu ra các lý thuyết, công nghệ mà nhóm em đã tìm hiểu và sử
dụng để xây dựng project này. Nhóm em sẽ nêu tóm lược khái niệm và lý do sử
dụng, phần implementation sẽ được đề cập trong các chương sau.
Chương 3: Kiến trúc mạng
Trong chương này, nhóm em sẽ khái quát tổng quan về kiến trúc mạng, cách
thức xử lý truyền dòng cũng như là cơ chế vào ra socket theo mô hình clientserver
được thực hiện trong ứng dụng.
Chương 4: Cơ chế của game
Trong chương này em sẽ giới thiệu các tính năng mà game có thể cho thầy
một cái nhìn khái quát về độ phức tạp của game mà nhóm em đã phát triển. Về
chi tiết hình ảnh và thao tác chúng em sẽ nêu đầy đủ trong video demo.
Chương 5: Nội dung phân chia công việc
Chương này sẽ bao gồm bảng chứa danh sách các công việc, lượng điểm ước
chừng và người được phân công. Ngoài ra ở mỗi công việc, nhóm em cũng sẽ nêu
rõ nội dung những công việc đó là gì để thầy có thể đánh giá xem lượng điểm
dành cho đầu việc đó có hợp lý hay không. Chương 6: Kết luận
Bao gồm các tài liệu tham khảo và lời kết. lOMoAR cPSD| 61549570
CHƯƠNG II: Cơ sở lý thuyết
Dưới đây là 1 số cơ sở lý thuyết và nền tảng chúng em đã áp dụng vào ứng dụng của mình:
1. Kiến thức cơ bản về lập trình socket và TCP
- Socket là giao diện lập trình ứng dụng mạng được dùng để truyền
và nhận dữ liệu trên internet. Giữa hai chương trình chạy trên mạng
cần có một liên kết giao tiếp hai chiều, hay còn gọi là two-way
communication để kết nối 2 process trò chuyện với nhau. Điểm cuối
(endpoint) của liên kết này được gọi là socket.
o Socket định danh ứng dụng mà dữ liệu sẽ được gửi tới thông
qua sự ràng buộc với 1 cổng port để tiến hành kết nối giữa client và server.
o Ưu điểm của socket: tương thích với hầu hết các hệ điều
hành, từ Window cho đến Linux và Mac OS X... Ngoài ra,
socket còn có thể kết hợp được với rất nhiều ngôn ngữ lập
trình như C, C++, C#, Java. Người dùng cũng có thể chạy
nhiều socket liên tục cùng một lúc được, giúp nâng cao hiệu
suất làm việc cũng như tiết kiệm thời gian hơn.
- TCP là 1 giao thức truyền thông trong bộ giao thức TCP/IP, được
sử dụng để đảm bảo việc truyền tải dữ liệu giữa các thiết bị trên
mạng một cách tin cậy và có trật tự. TCP đảm bảo rằng dữ liệu được
truyền từ nguồn đến đích mà không bị mất mát, bị sai thứ tự, hoặc
bị hỏng. Giao thức này là kết nối hướng, có nghĩa là trước khi truyền
tải dữ liệu, một kết nối phải được thiết lập giữa hai thiết bị thông qua
quá trình gọi là 3-way handshake. Dưới đây là 1 số đặc trưng của
việc truyền thông bằng TCP:
o Tạo luồng dữ liệu hai chiều, đáng tin cậy, có trình tự và không
trùng lặp, dữ liệu chỉ được gửi/nhận khi có đã có liên kết.
Trong C++ sử dụng hàm socket() để tạo và sử dụng socket.
o Cần có liên kết 2 chiều trước khi server và client có thể trao
đổi thông điệp với nhau.
o Ban đầu, phía server tạo socket được ràng buộc với một cổng
(port number) để chờ nhận yêu cầu từ phía client.
o Tiếp đến phía client yêu cầu server bằng cách tạo một Socket
TCP trên máy kèm với địa chỉ IP và port number của tiến trình lOMoAR cPSD| 61549570
tương ứng trên máy server. Khi client tạo Socket, client TCP
tạo liên kết với server TCP và chờ chấp nhận kết nối từ server.
o TCP cung cấp dịch vụ truyền dòng tin cậy và có thứ tự giữa
client và server, giữa máy chủ và máy nhận chỉ có 1 địa chỉ IP
duy nhất. Thêm vào đó, mỗi thông điệp truyền đi đều có xác nhận trả về.
2. Kiến thức về quản lý vào ra và hàm select()
- Trong lập trình nói chung và lập trình mạng nói riêng, quản lý vào
ra (I/O) là một yếu tố quan trọng để đảm bảo hiệu suất và tính khả
dụng của các ứng dụng. Khi một chương trình cần phải giao tiếp với
nhiều nguồn dữ liệu (như nhiều kết nối mạng hoặc tệp tin), việc chờ
đợi các thao tác I/O hoàn tất có thể làm giảm hiệu suất nếu không
được quản lý đúng cách.
- Một trong những công cụ mạnh mẽ để giải quyết vấn đề này trong
lập trình mạng là hàm select(). Hàm select() cho phép kiểm tra
nhiều file descriptor (bao gồm các socket) để xác định xem có sự
kiện I/O nào cần xử lý, chẳng hạn như dữ liệu có sẵn để đọc hoặc
socket có thể ghi dữ liệu. Điều này giúp chương trình có thể xử lý
nhiều kết nối đồng thời mà không cần phải sử dụng nhiều luồng, tối
ưu hóa tài nguyên và tăng cường khả năng đáp ứng trong các ứng
dụng mạng lớn. o Hàm select() yêu cầu kernel kiểm tra đồng thời
nhiều socket để xem chúng có dữ liệu chờ được recv(), hoặc nếu bạn
có thể send() dữ liệu đến chúng mà không bị chặn, hoặc nếu có
ngoại lệ nào đó xảy ra. o Kernel sẽ đánh thức quá trình chỉ khi một
hoặc nhiều sự kiện xảy ra hoặc khi một khoảng thời gian xác định đã trôi qua.
3. Kiến thức về QT trong C++
- QT là một framework phát triển phần mềm đa nền tảng phổ biến,
nó được sử dụng để xây dựng các ứng dụng GUI, các chương trình
command line và còn có các ứng dụng của hệ thống nhúng. QT được
rất nhiều lập trình viên ưa thích và sử dụng bởi độ mạnh mẽ
và linh hoạt của nó. Đồng thời, QT cũng hỗ trợ nhiều nền tảng như
Windows, macOS, Linux, iOS, Android, v.v...
- Trong bài tập lớn bộ môn Thực hành Lập trình mạng này, chúng em
chọn QT vì QT cung cấp rất nhiều các công cụ và lớp hỗ trợ giúp
việc phát triển của trò chơi trở nên dễ dàng hơn như QTcpSocket.
4. Sử dụng SQLite3 trong quản lý dữ liệu trò chơi lOMoAR cPSD| 61549570
- SQLite3 là một hệ quản trị cơ sở dữ liệu quan hệ (RDBMS) nhẹ, dễ
sử dụng, và phổ biến, đặc biệt là trong các ứng dụng di động, máy
tính cá nhân và các ứng dụng nhúng. Sau đây là 1 số lí do chúng em
chọn SQLite3 để làm quản lý cơ sở dữ liệu cho ứng dụng Battleship Online:
o SQLite3 không yêu cầu một máy chủ riêng biệt, tức là nó hoạt
động như một thư viện trong ứng dụng. Cơ sở dữ liệu được
lưu trữ trong một tệp đơn, giúp giảm chi phí tài nguyên và dễ
dàng tích hợp vào các ứng dụng nhúng hoặc di động mà không
cần thiết lập phức tạp.
o Không giống như các hệ quản trị cơ sở dữ liệu như MySQL
hay PostgreSQL, SQLite3 không cần phải cài đặt và cấu hình
một máy chủ cơ sở dữ liệu. Chỉ cần tích hợp thư viện SQLite
vào ứng dụng là có thể sử dụng cơ sở dữ liệu ngay lập tức.
o Dữ liệu của SQLite được lưu trữ trong một tệp duy nhất.
Điều này khiến SQLite rất dễ di chuyển giữa các hệ thống
hoặc các nền tảng khác nhau mà không gặp phải vấn đề tương
thích. Dữ liệu có thể sao chép hoặc di chuyển mà không gặp
phải sự phụ thuộc vào cấu hình phần cứng hay phần mềm.
- Từ các lí do trên, chúng em đã chọn SQLite3 vì hệ quản trị này rất
phù hợp với yêu cầu của trò chơi Battleship.
CHƯƠNG III: Kiến trúc mạng
Ứng dụng nhóm em thực hiện theo mô hình Multi Client-Server với 1 server
tổng. Server tổng để thực hiện truyền thông các chức năng cơ bản của ứng dụng
như đăng nhập, đăng ký, chơi nhanh hoặc thách đấu 1 người chơi khác.
Sơ đồ state machine của game Battleship
Dưới đây là chi tiết cụ thể về kiến trúc mạng cho server game: lOMoAR cPSD| 61549570
Server tổng thực hiện gửi và nhận thông điệp với các client (Multiclient)
thông qua giao thức truyền thông TCP để đảm bảo thông tin được truyền đi một
cách đầy đủ và chính xác nhất. Sẽ có 2 dạng thông điệp là Response (thông điệp
từ server trả về cho một hoặc nhiều client) và Request (thông điệp từ một client
gửi lên server). Mỗi dạng thông điệp này sẽ có các loại khác nhau (sử dụng enum
trong C++ để phân loại) tương ứng với việc thực hiện các chức năng khác nhau.
Dưới đây là bảng liệt kê các loại thông điệp: Thông điệp Mô tả Tham số Tham số Response Request REQUEST_SIGN_IN Đăng -request_type
Nếu đăng nhập thành công: nhập -user.username -response.status:STATUS_OK - -user.password
response.message thông báo đăng nhập thành công
-payload(chứa thông tin user)
Nếu đăng nhập thất bại: - response.status:STATUS_ERROR
-response.message thông báo loại lỗi REQUEST_SIGN_UP Đăng -request_type
Nếu đăng ký thành công: ký -user.username -response.status:STATUS_OK - -user.password
response.message thông báo đăng ký thành công
Nếu đăng ký thất bại: tương tự như đăng nhập thất bại. REQUEST_SIGN_OUT Đăng -request_type -response.status: STATUS_OK - xuất -user
response.message thông báo đăng xuất thành công REQUEST_GET_USERS Tìm tất -request_type -response.status: STATUS_OK - cả user -user response.message: thông báo thành công -List các user lOMoAR cPSD| 61549570 REQUEST_GET_MATCHES Tìm -request_type
Trong trường hợp tìm trận thành trận -user công: -response.status: STATUS_OK - response.message: thông báo thành công
-khởi tạo match mới Trong
trường hợp thất bại: - response.status: STATUS_ERROR
-response.message: Thông báo loại lỗi REQUEST_GET_MOVES Lấy -request_type
Trong trường hợp tìm thành công: thông -user -response.status: STATUS_OK -
tin về -move.matchID response.message: thông báo nước đi thành công
-response.move: thông báo về
nước đi được người dùng yêu cầu
Trong trường hợp thất bại: - response.status: STATUS_ERROR
-response.message: Thông báo loại lỗi REQUEST_CHALLENGE
Chuyển -request_type - Trong trường hợp chuyển lời mời lời mời user1:
người thách đấu thành công: thách thách đấu -response.status: STATUS_OK đấu -user2:
người -response.message: thông báo được thách đấu thành công
Trong trường hợp thất bại: - response.status: STATUS_ERROR
-response.message: Thông báo loại lỗi REQUEST_ACCEPT Chấp
-request_type - -response.status: STATUS_OK - /REQUEST_DECLINE nhận
user: người được response.message: thông báo chấp
hoặc từ thách đấu -user2: nhận hoặc từ chối
chối lời người thách đấu mời thách đấu lOMoAR cPSD| 61549570 REQUEST_READY
Thông -request_type - -response.status: STATUS_OK
báo với user: người click server nút ok đã chuẩn bị xong REQUEST_SHOT Yêu -request_type Thành công:
cầu bắn -user: người bắn -response.status: STATUS_OK
1 ô cờ -move: thông tin -response.message: Thông báo - của đối nước đi
move.cell: trạng thái của được phương chọn
-match: thông tin về lượt và bàn cờ của người chơi
Nếu có người chơi thắng cuộc: - response.status: STATUS_OK - response.type: REQUEST_GAME_OVER
-response.message: Thông báo
-response.user: người thắng cuộc
-response.user2: người thua Nếu
người chơi thoát giữa chừng hoặc đầu hàng:
-Tương tự trường hợp thắng cuộc,
khác biệt ở response.type Thất bại: -response.status: STATUS_ERROR
-response.message: Thông báo REQUEST_CHAT Gửi -request_type -response.status: STATUS_OK đoạn
-user: người gửi -response.message: thông báo chat - thành công đến đối request.message thủ : Thông điệp chat REQUEST_QUICK_MATC Tìm -request_type -response.status: STATUS_OK - H trận -user response.message: thông báo nhanh thành công lOMoAR cPSD| 61549570 REQUEST_CANCEL Hủy -request_type -response.status: STATUS_OK thao tác -user như chờ tìm trận, đầu hàng
Để thuận tiện trong việc tạo và gửi thông điệp, nhóm bọn em đã tạo ra các struct sau:
- Struct User: Lưu trữ thông tin người dùng - struct User {
- char username[255]{};
- char password[255]{}; - int elo{0}; - - User() = default; -
- User(char* username, char* password, int elo) : elo(elo) {
- strncpy(this->username, username, sizeof(this- >username));
- strncpy(this->password, password, sizeof(this- >password)); - } -
- bool operator==(const User& other) const {
- return strcmp(username, other.username) == 0; - } - };
- Struct Message: lưu trữ tin nhắn server phản hồi đến client - struct Message {
- char message[2048]{}; - - Message() = default; - };
- Struct Move: chứa các thông tin về nước đi, trận đấu và trạng thái của ô cờ - enum CellType { - CELL_EMPTY, - CELL_MISS, - CELL_HIT, - CELL_SHIP, - }; lOMoAR cPSD| 61549570 - - struct Move { - int matchID{}; - int row{}; - int col{}; - int player{}; - CellType type{}; - - Move() = default; - };
- Struct Match: chứa thông tin về 1 ván đấu đang diễn ra - struct Match { - uint64_t id{}; - User player1{}; - User player2{}; - int status1{}; - int status2{}; - int turn{}; - int winner{}; - int point1{}; - int point2{};
- CellType board1[BOARD_SIZE][BOARD_SIZE]{};
- CellType board2[BOARD_SIZE][BOARD_SIZE]{};
- CellType init1[BOARD_SIZE][BOARD_SIZE]{};
- CellType init2[BOARD_SIZE][BOARD_SIZE]{}; - - Match() = default; - };
- Struct Request: chứa thông tin về request - struct Request { - RequestType type{}; - User user{}; - User user2{}; - Move move{}; - Message message{}; - int rShips[5]{}; - int cShips[5]{}; - int oriShips[5]{}; - }; -
- Struct Response: chứa các thông tin về phản hồi của server - struct Response { - RequestType type{}; - Status status{}; - char message[255]{}; lOMoAR cPSD| 61549570 - User user{}; - User user2{}; - }; CHƯƠNG IV: Cơ chế game
Trong chương này, bọn em sẽ giới thiệu về cơ chế của game cũng như chức
năng nâng cao đã làm được:
- Cơ chế đặt và bắn tàu:
o Người chơi có thể đặt tàu theo các hướng ngang hoặc dọc
tùy theo ý muốn của mình, miễn là hợp lệ.
o Khi người chơi bắn trúng tàu địch, ô bị bắn sẽ hiển thị là
màu đỏ, nếu trượt là màu xanh. o 2 người chơi luân phiên
với nhau đến khi có người thắng cuộc.
- Một số hình ảnh demo:
- Chức năng nâng cao: chat o 2 người chơi khi chơi với nhau có thể
chat tùy vào mục đích của mình như cà khịa hay cổ vũ, v.v...
- Một số hình ảnh demo: lOMoAR cPSD| 61549570
CHƯƠNG V: Phân chia công việc
Dưới đây là bảng phân công công việc và ý tưởng thực hiện của nhóm chúng em: Chức năng Người thực hiện
Ý tưởng và cách thực hiện Điểm
Xử lý truyền dòng Đỗ Mạnh Phương Em thực hiện xử lý truyền 1
dòng tương tự như các bài
tập tuần, kết hợp thêm các
protocol đã liệt kê ở chương
3 nhằm mục đích chặt chẽ và
dễ hiểu khi code và sử dụng
Cài đặt cơ chế Đỗ Mạnh Phương Lúc đầu em định chọn dùng 2 vào/ra socket trên
pthread, nhưng do phức tạp server
và khó xử lý hơn đồng thời
nhóm em đã loại bỏ chức
năng thời gian thực nên em
quyết định chọn select do số
client trong dự án này rất nhỏ, chỉ tầm 200 client lOMoAR cPSD| 61549570
Đăng ký và quản Đỗ Mạnh Phương Em thực hiện tương tự như 2 lý tài khoản
các bài tập tuần, đồng thời
thêm các cơ chế quản lý như
tránh đăng nhập trùng tài
khoản hoặc tài khoản rỗng mật khẩu
Đăng nhập và Đỗ Mạnh Phương Em thực hiện đăng nhập 2 quản lý phiên
tương tự như các bài tập tuần,
còn về quản lý phiên, nhóm chúng em đã thêm 1 map
client nhằm quản lý cách
client hiện tại đang kết nối
với server nên khi đăng nhập
trùng tài khoản sẽ trả về thông báo lỗi
Cung cấp danh Đỗ Mạnh Phương Đối với chức năng này, em 2 sách người chơi
dùng map client trên để lấy sẵn sàng
thông tin và truyền về client
hiển thị ra danh sách người chơi đang sẵn sàng
Chuyển lời mời Đỗ Mạnh Phương Em đã thực hiện chức năng 2 thách đấu này theo cách sau:
+Khi người chơi chuyển lời
mời thách đấu, client sẽ tạo 1 request có header là REQUEST_CHALLENGE,
trong request này User sẽ là
người thách đấu và User2 sẽ lOMoAR cPSD| 61549570
là người được thách đấu.
+Server sẽ kiểm tra bằng
cách check các client xem có
client nào tồn tại người chơi
được thách đấu không. +Nếu
có, kiểm tra xem người chơi đó có trong match nào không. Nếu đang trong
match sẽ trả về thông báo lỗi,
nếu không sẽ tạo lời mời
thách đấu và gửi đến người chơi kia. Chấp nhận, từ
Đỗ Mạnh Phương Em đã thực hiện chức năng 1 chối lời mời thách
này theo cách sau: +Người đấu chơi nhận được
request sẽ có 2 lựa chọn là Yes hoặc No
+Nếu chọn Yes, Server sẽ
khởi tạo 1 match mới với 2
người chơi đã thách đấu nhau.
+Nếu chọn No, Server sẽ hủy
lời mời và gửi về client thách đấu response REQUEST_DECLINE lOMoAR cPSD| 61549570
Xây dựng hệ thống Đỗ Mạnh Phương Em đã thực hiện chức năng 1 tính điểm này như sau:
+Khi 2 người chơi chơi với
nhau, người chơi thắng sẽ
được cộng nhiều elo hơn nếu
đối thủ rank cao hơn mình,
người thua sẽ bị trừ ít elo hơn
nếu đối thủ elo cao hơn mình,
nếu 2 người chơi bằng rank
nhau sẽ được cộng hoặc trừ
10 điểm. Tất nhiên chúng em
đã ngăn việc để elo giảm
xuống dưới 0 điểm, vì mức
elo nhỏ hơn 0 là không hợp lệ.
+Khi quickmatch, người chơi
sẽ được ghép cặp với player
chênh mình tối đa 20 elo,
điều này có thể gây ra việc 2
người chơi chênh điểm nhau
sẽ bị chờ đợi vô thời hạn, em
chưa xử lý được lỗi này.
Xác định kết quả Đỗ Mạnh Phương Khi người chơi đang trong 1 ván cờ
game, sẽ có 3 trường hợp sau:
+1 người chơi bắn trúng hết
tàu của đối phương, khi đó
server thông báo kết thúc
game và tuyên bố người chơi thắng cuộc.
+Khi 1 người chơi thoát đột
ngột hoặc đầu hàng, server sẽ
xử lý tương tự, người bị xử
thua là người đầu hàng hoặc thoát game. lOMoAR cPSD| 61549570
Lưu thông tin ván Đỗ Mạnh Phương Khi xác định kết quả ván cờ 2 đấu và replay
xong, server sẽ lưu kết quả
ván đấu vào cơ sở dữ liệu
SQLite3, đồng thời tạo ra 1
response hỏi người chơi bị
thua có muốn tái đấu không,
nếu người chơi đồng ý, server
sẽ thực hiện phần còn lại
giống như cách xử lý chuyển lời mời thách đấu
Tổng điểm: 31 điểm Đóng góp:
- Đỗ Mạnh Phương: 50%(16 điểm)
- Lê Quang Khải: 50%(15 điểm)