Bài giảng này trang bị cho người học những hiểu biết về lập trình máy chủ với những nội dung cơ bản như: Khái niệm các loại máy chủ; máy chủ xứ lý đồng thời, đa tiến trình, hướng kết nối; ví dụ về Máy chủ xử lý đồng thời, đa tiến trình, hướng kết nối; máy chủ xử lý đồng thời, đơn tiến trình, hướng kết nối;... Mời các bạn cùng tham khảo.
Trang 1Lập trình máy chủ
Giáo viên: Nguyễn Hoài Sơn
Bộ môn Mạng và Truyền thông máy tính Trường Đại học Công nghệ
Trang 2Nội dung bài học
Khái niệm các loại máy chủ
Máy chủ xứ lý đồng thời, đa tiến trình, hướng kết nối
Ví dụ về Máy chủ xử lý đồng thời, đa tiến trình, hướng kết nối
Máy chủ xử lý đồng thời, đơn tiến trình, hướng kết nối
Ví dụ về Máy chủ xử lý đồng thời, đơn tiến trình, hướng kết nối
Trang 3 Dung lượng bộ nhớ sử dụng, tài nguyên tính toán
Dễ thiết kế, xây dựng và bảo trì
Trang 4Phân loại máy chủ theo cách thức
Tuần tự hướng
kết nối
Đồng thời hướng kết nối
Trang 5Máy chủ không kết nối
Máy chủ sử dụng giao thức UDP
Không có kết nối giữa máy khách và máy chủ
Ưu điểm
Chỉ cần một socket
Thích hợp với kiểu truyền tin quảng bá/quảng phát
Người lập trình được tự do lựa chọn cách thức thực thi tính tin cậy trong truyền tin
Nhược điểm
Cung cấp tính tin cậy đòi hỏi chi phí cao với người lập trình
Trang 6Máy chủ hướng kết nối
Trang 7Khái niệm xử lý tuần tự/xử lý đồng
Trang 8Máy chủ xử lý tuần tự
Tại mỗi thời điểm, xử lý một yêu cầu
Đưa các yêu cầu mới vào một hàng đợi
Yêu cầu của một máy khách chỉ được xử lý khi yêu cầu trước đó được xử lý xong
Dễ thực thi nhưng kém hiệu quả
Nhất là trường hợp yêu cầu xử lý ít phải chờ yêu cầu xử lý nhiều kết thúc
Server
Client1
Receiving requests from Client 1 Receiving requests from Client 2
Trang 9Cách thực hiện máy chủ xử lý tuần tự
Một máy chủ xử lý tuần tự thực hiện theo các bước đơn giản sau
Tạo socket và gán thông tin cổng cho socket
Lặp lại
Chấp nhận yêu cầu từ máy khách
Xử lý yêu cầu
Tạo thông báo kết quả và gửi trả lại máy khách
Trong trường hợp của máy chủ xử lý tuần tự
hướng kết nối
Tại mỗi thời điểm, máy chủ xử lý từng yêu cầu đến từ một kết nối tới một máy khách
Trang 10TCP client
TCP server
socket()
socket() bind() listen() accept()
read()
write() read()
connect()
write()
Connection request
Wait next request
Trang 11Máy chủ xử lý đồng thời
Có thể xử lý nhiều yêu cầu đến cùng một lúc
Máy khách không cần đợi yêu cầu trước đó kết thúc
Hiệu quả cao hơn máy chủ xử lý tuần tự trong hầu hết các trường hợp nhưng khó thực hiện hơn
Trang 12Cách thực thi máy chủ xử lý đồng
thời
Đa tiến trình
Tạo ra nhiều tiến trình
Mỗi tiến trình xử lý một yêu cầu của máy khách
Đa luồng
Tạo ra nhiều luồng
Mỗi luồng xử lý một yêu cầu của máy khách
Đa xuất nhập (I/O multiplex)
Đơn tiến trình
Xuất nhập dữ liệu tại nhiều socket cùng một lúc
Nhận dữ liệu đồng thời từ nhiều kết nối, xử lý tuần tự các
Trang 13Xử lý đồng thời tốt trong trường hợp nào?
Trả lời một yêu cầu cần nhiều thời gian
xuất/nhập dữ liệu
E.g File transfer, Telnet, Web server,…
Thời gian xử lý các yêu cầu của máy khách là khác nhau lớn
Máy chủ chạy trên một máy tính có nhiều bộ
vi xử lý
Trang 14Máy chủ xử lý đồng thời,
không kết nối, đa tiến trình
Trang 15Máy chủ xử lý đồng thời, không kết nối, đa tiến trình
Bước 1 tiến trình mẹ: Khởi tạo socket, gán
thông tin cho socket
Bước 2 tiến trình mẹ: Lặp lại lệnh recvfrom( )
để nhận yêu cầu từ máy khách và khởi tạo
tiến trình con để xử lý yêu cầu của máy
khách
Trang 16Máy chủ xử lý đồng thời, không kết nối (2)
Bước 1 tiến trình con: Xử lý yêu cầu của máy khách
Bước 2 tiến trình con: Tạo thông báo trả lời và gửi trả lại máy khách bởi hàm sendto( )
Bước 3 tiến trình con: Kết thúc sau khi xử lý xong yêu cầu
Do chi phí cao khi khởi tạo một tiến trình con nên ít máy chủ không kết nối sử dụng cách xử lý đồng thời
đa tiến trình
Trang 17Máy chủ xử lý đồng thời,
hướng kết nối, đa tiến trình
Trang 18Thực thi máy chủ xử lý đồng thời, hướng kết nối đa tiến trình như thế nào?
Lệnh accept dừng thực thi của máy chủ để đợi kết nối đến cổng chờ
Khi có kết nối từ máy khách, tiến trình mẹ
sẽ khởi tạo một tiến trình con để xử lý kết
nối
Tiến trình mẹ sẽ chờ để chấp nhận một kết nối khác
Trang 19Socket for socket for individual
Operating system
Trang 20Cách thực thi máy chủ xử lý đồng
thời, hướng kết nối, đa tiến trình
thông tin cho socket và chuyển socket sang
trạng thái thụ động, chờ kết nối
B ướ c 2 ti ế n trình m ẹ : Lặp lại:
Gọi accept() chờ kết nối máy khách
Khi có kết nối từ máy khách, khởi tạo một tiến trình
con để xử lý dữ liệu qua kết nối với máy khách
Trang 21Cách thực thi máy chủ xử lý đồng
thời, hướng kết nối, đa tiến trình (2)
máy khách
khách, xử lý yêu cầu và gửi thông báo trả lời cho
máy khách
khi xử lý xong yêu cầu của máy khách
Trang 22fork(): Tạo tiến trình mới
Khởi tạo một tiến trình con chỉ khác tiến trình mẹ ở PID và PPID
Trang 232 cách sử dụng điển hình với fork
Một tiến trình muốn thực hiện nhiều công việc cùng một lúc
Tiến trình sẽ tạo ra một bản copy của nó Mỗi bản copy sẽ thực hiện một công việc
Một tiến trình muốn chạy một chương trình
khác
Tiến trình sẽ tạo ra một bản copy của nó và bản copy đó sẽ chạy chương trình mới
Trang 24Ví dụ về máy chủ ECHO xử lý đồng thời, đa tiến trình
tcpserv02.c
Trang 25Chi tiết về xử lý đồng bộ
Tiến trình mẹ
đóng socket kết nối connfd của kết nối mới
sử dụng socket chờ listenfd để tiếp tục chờ kết nối mới
Tiến trình con
đóng socket chờ istenfd
cung cấp dịch vụ echo ( str_echo (connfd)) thông qua kết nối mới
tiết tục sử dụng socket kết nối connfd cho đến khi thoát
ra (exit)
Tiến trình con phải thoát ra (exit) sau khi đã thực hiện xong dịch vụ
Lệnh Exit được sử dụng để kết thúc tiến trình
Khi tiến trình con kết thúc, hệ thống sẽ tự động đóng các socket kết nối lại
Trang 27Vấn đề tiến trình không kết thúc hoàn toàn
Một tín hiệu (SIGCHLD) sẽ được gửi đến tiến trình
mẹ khi một tiến trình con kết thúc
Tiến trình con sau khi kết thúc sẽ tồn tại ở trạng
thái zombie cho đến khi tiến trình mẹ thực hiện
lệnh gọi wait3 (wait, waitpid)
Trang 28Giải quyết vấn đề tiến trình không kết
thúc hoàn toàn như thế nào ?
Tiến trình mẹ sẽ bắt tín hiệu kết thúc của tiến
trình con và gọi hàm xử lý tín hiệu
signal(SIGCHLD, sig_chld)
Hàm signal biểu thị rằng tiến trình mẹ cần gọi
hàm sig_chld mỗi khi nó nhận được tín hiệu
SIGCHLD báo hiệu một tiến trình con đã kết
thúc
Hàm sig_chld gọi hàm wait3 để hoàn thành việc
Trang 29/* sig_chld - clean up zombie child */
void sig_chld(int signo){
Tùy chọn WNOHANG cho phép tiến trình mẹ
không bị dừng thực thi nếu không có tiến trình
con nào kết thúc
Trang 30Cách viết khác của hàm sig_chld
/* reaper - clean up zombie child */
void sig_chld (int sig)
Trang 31Xử lý cuộc gọi hệ thống bị ngắt như thế nào?
Hàm accept sẽ trả về giá trị lỗi là EINTR
(interrupted signal call) Tiến trình mẹ cần phải
bỏ qua lỗi này
If ((ssock=accept(….)) <0)
if (errno = EINTR) continue;
else err_sys(“accept error”);
Trang 32Ví dụ về máy chủ ECHO xử lý đồng thời, đa tiến trình
tcpserv03.c
Trang 33Máy chủ hướng kết nối, xử
lý đồng thời, đơn tiến trình
Trang 34Tại sao lại xử lý đồng thời đơn tiến trình?
Chi phí khi tạo tiến trình
Chia sẻ thông tin giữa các kết nối được dễ dàng
Máy chủ xử lý đồng thời sẽ xử lý một cách tuần tự nếu thời gian xử lý một yêu cầu của máy khách là
ngắn
Sử dụng đa tiến trình chỉ có hiệu quả khi CPU không thể
xử lý các yêu cấu một cách tuần tự
request1 request 2 request 3
CPU time
Trang 35Thực hiện xử lý đồng thời đơn tiến trình như thế nào?
Một máy chủ đồng thời, đơn tiến trình sẽ phải thực hiện chức năng của cả tiến trình mẹ và tiến trình con trong mô hình máy chủ đa tiến trình
Trang 36Server
application process
< -Operating
Socket lắng
Trang 37Thực hiện xử lý đồng thời đơn tiến trình như thế nào? (2)
Nếu socket lắng nghe sẵn sàng, tiến trình sẽ gọi
hàm accept() để chấp nhận kết nối mới đến
Nếu socket kết nối sẵn sàng, tiến trình sẽ gọi hàm
read() để nhận yêu cầu đến từ máy khách, xử lý yêu cầu và gửi trả lại kết quả xử lý
Sau đó các bước trên được lặp lại
Làm thế nào để đợi xuất nhập dữ liệu trên tất cả các sockets và biết socket nào đã sẵn sàng?
Trang 38Các mô hình xuất nhập dữ liệu
Xuất nhập chặn (Blocking I/O)
Xuất nhập không chặn (Nonblocking I/O)
Đa xuất nhập (I/O multiplexing)
Xuất nhập dựa tín hiệu (signal driven I/O)
(SIGIO)
Xuất nhập không đồng bộ (asynchronous I/O)
Trang 39Blocking I/O Model
recvfrom
system call no datagram ready
datagram ready copy datagram
copy complete
wait for data
copy data from kernel
to user
process datagram
Trang 40copy complete
wait for data
copy data from kernel
Trang 41I/O Multiplexing Model
copy complete
wait for data
copy data from kernel
to user
process datagram
Trang 42Signal-driven I/O model
recvfrom
sigaction system call
datagram ready copy datagram
copy complete
wait for data
copy data from kernel
to user process
deliver SIGIO system call
Trang 43Asynchronous I/O Model
aio_read
system call no datagram ready
datagram ready copy datagram
copy complete
wait for data
copy data from kernel
to user signal
handler process datagram
process
continues
executing
deliver signal return
specified in aio_read
Trang 44Hàm gọi select
Hàm Select cung cấp xuất nhật dữ liệu không đồng
bộ bằng cách cho phép một tiến trình có thể đợi trạng thái sẵn sàng đầu tiên của nhiều sockets trong một tập mô tả file đã được định sẵn
Không chỉ có socket
Có thể thiết lập thời gian timeout tối đa
Ví dụ, hàm select sẽ trả về giá trị khi:
Một trong các mô tả file của tập {1,4,5} sẵn sàng để đọc
Một trong các mô tả file của tập {2,7} sẵn sàng để ghi
Một trong các mô tả file của tập {1,4,5} nhận ngoại lệ
Trang 45Select(): dùng cho xuất nhập
timeout : thời gian timeout đợi một trong các mô tả file trở nên sẵn sàng
Kiểu dữ liệu fd_set data :
Biểu diễn tập các miêu tả file dưới dạng chuỗi integer, mỗi bit trong chuỗi ứng với một miêu tả file
#include <sys/select.h>#include <sys/time.h>
int select(int maxfdp1, fd_set *readset, fd_set *writeset,
fd_set *exceptset, const struct timeval *timeout);
Returns: positive count of ready descriptors,
0 on timeout, –1 on error
Trang 46Khi nào một socket ở trạng thái sẵn
sàng đọc
Khi số byte dữ liệu trong buffer của socket lớn hơn hoặc bằng
kích thước tối thiểu (mặc định là 1)
Chúng ta có thể thiết lập giá trị kích thước tối thiểu bằng tùy
chọn SO_RCVLOWAT
Khi một nửa đọc của kết nối bị đóng
Kết nối TCP nhận được gói tin FIN
Một thao tác đọc socket sẽ trả về giá trị 0 (i.e EOF)
Nếu socket là socket lắng nghe thì số lượng kết nối đã hoàn
thành bắt tay ba bước trong hàng đợi lớn hơn 0
Một lỗi socket đang chờ
Một thao tác đọc socket sẽ trả về giá trị -1 với tập errno được
Trang 47Khi nào một socket ở trạng thái sẵn
sàng ghi?
Khi số byte trống trong buffer gửi của socket lớn hơn hoặc bằng độ lớn tối thiểu (mặc định là 2048) và :
socket đang được kết nối
socket không cần kết nối(e.g., UDP)
Một nửa đọc của kết nối bị đóng
Một thao tác viết trên socket sẽ sinh ra tín hiệu SIGPIPE
Một socket sử dụng kết nối non-blocking vừa hoàn thành bắt tay 3 bước hay kết nối đó bị thất bại
Một lỗi socket đang chờ
Một thao tác viết trên socket đó sẽ trả về giá trị -1 cùng với tập errno set được gán giá trị của lỗi
Trang 48 /* Xóa tất cả các bits trong fdset */
void FD_ZERO(fd_set *fdset);
/* Bật một bit ứng với fd trong fdset */
void FD_SET(inf fd, fd_set *fdset);
/* Tắt một bit ứng với fd trong fdset */
void FD_CLR(inf fd, fd_set *fdset);
/* Kiểm tra xem bit ứng với fd trong fdset có
được bật lên hay không*/
Trang 49Cách thực thi máy chủ
1. Tạo ra socket lắng nghe tại một cổng dịch vụ
Gán socket này vào tập mô tả file xuất nhập dữ liệu
2. Sử dụng lệnh gọi select để chờ xuất nhập dữ
liệu trên tập mô tả file của các sockets
3. Nếu socket lắng nghe sẵn sàng, gọi hàm
accept để chấp nhận kết nối mới và nhận về
một socket kết nối
4. Gán socket kết nối vào tập mô tả file xuất nhập
dữ liệu
Trang 50Cách thực thi máy chủ (2)
5. Nếu có socket kết nối sẵn sàng, gọi hàm
đọc để đọc yêu cầu từ máy khách, xử lý yêu cầu và tạo thông báo trả lới Gọi hàm ghi để gửi thông báo trả lời cho máy khách
6. Tiếp tục xử lý với bước 2 ở trên
Trang 51Ví dụ - Máy chủ ECHO đơn tiến trình
/* TCPmechod.c - main, TCPechod */
/* include header files here */
#define QLEN 5 /*max connection queue length */
#define BUFSIZE 4096
extern int errno;
int echo (int fd) /*echo data until end of file */
int errexit (const char *format, …);
int passiveTCP (const char *service, int qlen);
Trang 52/* main- concurrent TCP server for ECHO service */
int main(int argc, *argv[])
{
char *service = “echo”; /*service name or port number */
struct sockaddr_in fsin; /* the from address of a client */
int alen; /* length of a client’s address */
int msock; /* master server socket */
fd_set rfds; /* read file descriptor set */
fd_set afsd; /* active file descriptor set */
int fd;
/* check arguments - not detailed here*/
Trang 53msock = passiveTCP (service, QLEN);
FD_ZERO (&afds);
FD_SET (msock, &afds);
while(1) {
memcpy(&rfds, &afds, sizeof(rfds));
if ( select (FD_SETSIZE, &rfds, (fd_set *) 0,
(fd_set *) 0, (struct timeval *) 0) < 0)
errexit (“select: %s\n”, strerror(errno));
Trang 54if ( FD_ISSET (msock, &rfds)) {
int ssock;
alen = sizeof (fsin);
ssock = accept(msock,(struct sockaddr *)&fsin,
&alen);
if ( ssock < 0)
errexit (“accept: %s\n, strerror (errno)); FD_SET (ssock, &afds);
Trang 55}
Trang 56/* Echo - echo one buffer of data, returning byte count */
int echo (int fd)
errexit(“echo read: %s\n”, strerror(errno));
if (cc && write(fd, buf, cc) < 0 )
errexit (“echo write: %s\n”, strerror(errno));
return cc;
}
Trang 57Vấn đề của xử lý đồng thời, đơn tiến trình
Xử lý đồng thời nhiều kết nối …
nhưng xử lý tuần tự các yêu cầu
=> Denial Of Service