Chapter 3 cac phuong phap cap nhat thong tin tren web
Bai 4 lap trình phia client
1. Bài 4- Giải thuật và các kỹ thuật cơ
bản trong lập trình Client
2. Chủ đề
Nắm được các bước (giải thuật) lập trình chương
trình Client
Biết cách sử dụng các hàm Socket API trong lập
trình Client
Thực hành lập trình một chương trình Client đơn
giản
3. Nội dung
4.1) Cấu trúc tổng quát chương trình Client
4.2) Giải thuật tổng quát chương trình Client
4.3) Cách gọi hàm Socket API trong từng bước
4. 4.1 Cấu trúc chương trình Client
Về cơ bản, client không yêu cầu mức ưu tiên cao
trong hệ thống
Không yêu cầu nhiều tài nguyên và không cần
hoạt động liên tục
5. Cấu trúc chương trình gồm 3
module
Module giao diện để tương tác với user
Module kết nối với server
Module thực thi giao thức trao đổi dữ liệu với
server
Gửi bản tin yêu cầu đến Server
Nhận bản tin phản hồi từ Server và xử lý
6. Các chức năng hỗ trợ thực thi
giao thức
Đọc và phân tích cú pháp bản tin giao thức
Client/Server
Xử lý dữ liệu trước khi gửi và sau khi nhận
Thao tác với bộ đệm buffer chứa dữ liệu
Hiển thị trạng thái phiên làm việc
Thông báo và xử lý lỗi gặp phải
Đọc/ghi dữ liệu từ tệp tin
7. 4.2) Giải thuật chương trình client có
5 bước
Một Client nói chung cần thực hiện 5 bước sau
Bước 1) Xác định địa chỉ remote socket phía
server
Bước 2) Tạo local socket tại client
Bước 3) Kết nối local socket với remote
socket
Bước 4) Gửi/nhận/xử lý dữ liệu qua kết nối
socket
Bước 5) Đóng kết nối
8. Phân rã chức năng theo module
Bước 1 -> 3 được thực hiện bởi module kết nối
socket
Bước 4 ->5 được thực hiện bởi module thực thi
giao thức (module chính)
Bước 1 3 là thao tác chung đối với mọi Client
Bước 4 và 5 tùy theo từng giao thức ứng dụng
như HTTP, FTP, POP3 hay SMTP
9. Ví dụ minh họa: Chương trình
EchoClient
Chức năng: kiểm tra phản hồi của server qua
TCP/IP
Các công đoạn xây dựng chương trình gồm:
Các chức năng bổ trợ: truy vấn thông tin, thông báo
lỗi
Lập trình module kết nối
Lập trình module tương tác với Server theo giao
thức ứng dụng Echo
Lập chương trình chính
10. Giao thức ECHO
Echo dùng để kiểm tra hoạt động của mạng và của
server
Client gửi đến Server 1 bản tin là một xâu kí tự bất
kỳ
Server gửi trả lại một bản tin với nội dung hoàn
toàn trùng với bản tin mà client gửi đến
Ví dụ:
Client gửi bản tin ”HELLO”
Server nhận được và gửi lại “HELLO”
Client gửi “How are you?”
Server gửi lại “How are you?”
11. Cách thức hoạt động
User chạy chương trình TCPEchoClient với tham
số hàm main là tên server
TCPEchoClient.exe <tên server>
Nếu kết nối thành công, nhập bản tin là xâu kí tự
từ bàn phím và gửi bản tin đến server
Nhận bản tin phản hồi và in ra màn hình
Đóng kết nối và kết thúc nếu bản tin nhập vào là
xâu rỗng
12. Các tệp mã nguồn của project
socketbasic.cpp: Module chức năng khởi tạo và
giải phóng WS, truy vấn thông tin
errorhandler.cpp: Module chức năng báo lỗi
connect.cpp : chức năng kết nối đến server
echoclient.cpp: chức năng trao đổi các bản tin
theo giao thức Echo
13. Bước 1: Xác định địa chỉ socket
phía Server
Client
Server
transport
network
interface
transport
network
interface
Internet
Xác định địa chỉ Socket của
server ntn ?
UDP
TCP
POP3
Địa chỉ Socket của tiến trình Server
- Kiểu Socket (TCP hay UDP)
- Cổng
- Địa chỉ IP
14. Địa chỉ của remote socket phía
Server có 3 tham số
Loại socket: chọn TCP hoặc UDP tùy vào ứng
dụng
FTP :
TCP socket
POP3: TCP socket
DNS: UDP socket
Số cổng: chọn cổng của dịch vụ tương ứng
FTP: 21
POP3: 110
Địa chỉ IP: lấy từ hostname của server hoặc địa
chỉ IP dạng thập phân
hostname:
Địa chỉ IP:
ftp.nuce.edu.vn
220.231.122.114
15. Có 2 cách lấy địa chỉ IP
Nếu biết hostname, ví dụ: ftp.nuce.edu.vn
Sử dụng hàm API gethostbyname để lấy thông tin
về trạm server qua hostname
Thông tin hàm trả về là cấu trúc hostent
Thông qua trường h_addr_list có thể lấy địa chỉ IP
dạng 4-byte
Nếu biết địa chỉ IP dạng thập phân, ví dụ :
“220.231.122.114”
Sử dụng hàm API inet_addr để chuyển đổi thành
địa chỉ IP dạng số 32 bit
16. Cách lấy số cổng
Nếu biết số cổng trực tiếp, ví dụ 80, 7 hoặc 21
Sử dụng hàm htons để biến đổi số cổng sang dạng
NBO
Nếu chỉ nhớ tên dịch vụ, ví dụ HTTP, ECHO, FTP
Sử dụng hàm API getservbyname để truy vấn
thông tin dịch vụ từ tên dịch vụ
17. Sử dụng cấu trúc địa chỉ socket
Địa chỉ socket được lưu trong cấu trúc dữ liệu
struct sockaddr_in
Chương trình cần khai báo một biến kiểu struct
sockaddr_in và gán giá trị cổng, địa chỉ IP vừa lấy
được cho các trường dữ liệu tương ứng
struct sockaddr_in sin; // biến cấu trúc địa chỉ socket
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = server_addr.s_addr; //gán địa chỉ IP
sin.sin_port = server_port; // gán số cổng
18. Bước 2: Tạo local socket tại
Client
Mỗi socket được tham chiếu bởi một biến kiểu
SOCKET
Sử dụng hàm API socket để tạo 1 socket mới
SOCKET s = socket(PF_INET, sock_type, 0);
PF_INET: họ giao thức TCP/IP
sock_type = SOCK_STREAM nếu tạo TCP socket
sock_type = SOCK_DGRAM nếu tạo UDP socket
19. Tiếp…
Nếu thành công, hàm socket trả về một giá trị
kiểu SOCKET
Nếu không thành công, hàm trả về giá trị hằng
INVALID_SOCKET
20. Bước 3: Kết nối đến remote
socket tại Server
Gọi hàm API connect và truyền tham số cần thiết
để kết nối với socket tại server
int ret = connect(s, (struct sockaddr*) &sin, sizeof(sin));
if(ret==SOCKET_ERROR)
errexit(GetLastError(), "Khong ket noi duoc voi server: %s,
%s", host, service);
s: biến local socket vừa tạo
sin : biến cấu trúc địa chỉ socket của server
Nếu giá trị trả về ret == SOCKET_ERROR
không kết nối được, ngược lại là kết nối thành
công
21. Chú ý
Có nhiều khả năng xảy ra lỗi khi kết nối
Server không trả lời
Server từ chối kết nối
Time out do trễ truyền dữ liệu
…
Khi lập trình mạng, cần chú ý kiểm tra kết quả gọi
hàm SOCKET API để biết thao tác thực hiện có
thành công hay không
Nếu không kiểm tra mà vẫn tiếp tục các thao tác
tiếp theo sẽ dẫn đến lỗi chương trình
22. Bước 4: Client tương tác với Server
Client gửi bản tin yêu cầu bằng cách
Tạo nội dung bản tin và đưa bản tin vào vùng đệm
buffer (bộ nhớ)
Gọi hàm API send để gửi bản tin qua socket
int byte_send = send(s, buffer, msg_length, 0);
Client nhận bản tin phản hồi từ server
Chuẩn bị vùng đệm buffer sẽ nhận nội dung bản tin
Gọi hàm API recv để đọc dữ liệu từ socket vào
buffer
int byte_recv = recv(s, buffer, sizeof(buffer), 0);
23. Hàm send và recv
Nếu gửi dữ liệu thành công, hàm trả về số byte
dữ liệu đã gửi
Ngược lại, hàm trả về giá trị SOCKET_ERROR
Để tìm nguyên nhân gây lỗi, gọi hàm
GetLastError()
Tương tự, hàm recv trả về số byte nhận được,
nếu thành công, trả về SOCKET_ERROR nếu
gặp lỗi
24. Chuẩn bị vùng đệm buffer
Vùng đệm có thể khai báo dưới dạng mảng kiểu
char
char buffer[512]; // vùng đêm 512 bytes
Chú ý tránh tràn vùng đệm gây lỗi chương trình
khi gọi hàm recv nhiều lần
25. Bước 5: Đóng kết nối
Sau khi kết thúc gửi/nhận các bản, client có thể
đóng kết nối
Gọi hàm API closesocket
closesocket(s)
26. Bài tập
Bài 1: Biết tên web server của ĐHXD là
www.nuce.edu.vn, hãy viết một chương trình truy
vấn địa chỉ IP của server trên
Bài 2: Viết 1 chương trình Client kết nối đến
cổng 80 của server www.nuce.edu.vn sau đó
Gửi bản tin “GET / HTTP/1.1rnrn”, nhận phản
hồi và in ra màn hình
Gửi bản tin “GET /administrator HTTP/1.1rn”,
nhận bản tin phản hồi và in ra màn hình
27. Tiếp
Bài 3: Tạo giao diện command line cho chương
trình EchoTCPClient, sử dụng các chức năng
qua lệnh:
Kết nối đến server: lệnh connect, tham số là tên
hoặc địa chỉ IP của server
Gửi bản tin: lệnh msg, tham số là nội dung bản tin
trên 1 dòng
Đóng kết nối: lệnh close
Thoát khỏi chương trình: lệnh quit
28. Đọc thêm: Client sử dụng UDP
socket
Một chương trình client sử dụng UDP socket
cũng bao gồm các bước tương tự
Điểm khác biệt: không có liên kết giữa local
socket và remote socket phía server vì UDP là
giao thức không hướng kết nối
29. Kết nối UDP socket
Ý nghĩa của việc gọi hàm connect với UDP
socket
Xác định địa chỉ socket phía server một lần duy
nhất.
Sau đó có thể dùng hàm send() và recv()để
gửi nhận dữ liệu giống như với TCP socket
Ưu điểm: thuận tiện khi client chỉ tương tác
với một server
Nếu muốn thay đổi địa chỉ socket mỗi lần gửi
hoặc nhận dữ liệu, sử dụng hàm sendto và
recvfrom
30. Hàm sendto và recvfrom
sendto là hàm gửi dữ liệu dành riêng cho
UDP socket
int sendto( SOCKET s, const char *buf, int len, int flags,
const struct sockaddr *to, int tolen );
recvfrom là hàm nhận dữ liệu
int recvfrom( SOCKET s, char * buf, int len, int flags, struct
sockaddr *from, int *fromlen );
Ưu điểm: giúp chương trình client linh hoạt
hơn trong gửi/ nhận dữ liệu với nhiều Server
Client có thể dùng một local socket để gửi dữ liệu
cho nhiều remote socket
31. Chú ý
Khi gọi hàm connect() với UDP socket, hàm
không báo lỗi nhưng không bảo đảm chắc chắn
sẽ liên lạc được với server.
32. Cách truyền và nhận
Khác với TCP, UDP socket truyền trọn vẹn
một bản tin (datagram) trong một lần gọi hàm
send hoặc sendto
Nếu vùng đệm (buffer) đủ lớn thì phía nhận
cũng sẽ nhận trọn vẹn bản tin đó trong 1 lần
gọi hàm recv hoặc recvfrom
Chú ý : nếu kích thước vùng đệm < kích
thước bản tin thi các byte dữ liệu còn lại sẽ bị
bỏ qua (cách làm việc của UDP không tin cậy)
33. Đóng socket
Đóng socket UDP là giải phóng tài nguyên
gán cho socket đó
Do tính chất không liên kết của UDP, nếu một
phía (ví dụ là Client) đóng socket thì phía bên
kia không nhận biết được sự kiện đó
UDP thích hợp cho các ứng dụng cần tốc độ
mà không cần tin cậy và liên kết chặt chẽ
giữa Client và Server
34. Bài tập: Chương trình Client với
socket UDP
Viết chương trình EchoUDPClient
Sử dụng socket UDP
Tương tác với server: gửi bản tin qua socket và
nhận bản tin phản hồi