Bài giảng Lập trình mạng - Nguyễn Hoài Sơn - Lập trình máy chủ
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
lý từng yêu cầu đến từ một kết nối tới một máy khách Network programming TCP client TCP server socket() socket() bind() listen() accept() read() write() close() read() connect() write() close() Connection request EOF Wait next request Process request read() data (request) data (reply) Send next request Network programming Má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 Server Client1 Client2 Receiving requests from Client 1 Receiving requests from Client 2 Network programming Cá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 yêu cầu Network programming Xử 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ý Network programming Máy chủ xử lý đồng thời, không kết nối, đa tiến trình Network programming Má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 Network programming Má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 Network programming Máy chủ xử lý đồng thời, hướng kết nối, đa tiến trình Network programming Thự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 Network programming Socket for socket for individualconnection connectionsrequests master slave1 slave2 slaven Server application processes Operating system Network programming Cách thực thi máy chủ xử lý đồng thời, hướ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 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 Network programming Cách thực thi máy chủ xử lý đồng thời, hướng kết nối, đa tiến trình (2) Bước 1 tiến trình con: Nhận socket kết nối với máy khách Bước 2 tiến trình con: Đọc yêu cầu của máy khách, xử lý yêu cầu và gửi thông báo trả lời cho máy khách Bước 3 tiến trình con: Đóng kết nối và thoát sau khi xử lý xong yêu cầu của máy khách Network programming fork(): 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 Gọi một lần nhưng trả về 2 lần Trả về trên tiến trình mẹ với giá trị trả về là PID của tiến trình con Trả về trên tiến trình con, với giá trị trả về là 0 Lấy PID của tiến trình mẹ bằng hàm getppid() #include pid_t fork(void); Returns: 0 in child, process ID of child in parent, -1 on error Network programming 2 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 Network programming Ví dụ về máy chủ ECHO xử lý đồng thời, đa tiến trình tcpserv02.c Network programming Chi 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 Network programming Xử lý đồng bộ với fork() Network programming Vấ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) linux % ps -o pid,ppid,tty,stat,args,wchan PID PPID TT STAT COMMAND WCHAN 3674 3453 pts/3 S+ ./daytime_server - 3676 3674 pts/3 Z+ [daytime_server] exit Tiến trình zombie sẽ làm tốn tài nguyên máy tính chiếm chỗ trên bộ nhớ Network programming Giả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 kết thúc của tiến trình con Network programming /* sig_chld - clean up zombie child */ void sig_chld(int signo){ int status; while (wait3(&status, WNOHANG, (struct rusage *) 0) >= 0) /* empty */; } Giá trị status sau khi hàm wait3 trả về sẽ cho biết trạng thái kết thúc của tiến trình con 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 Network programming Cách viết khác của hàm sig_chld /* reaper - clean up zombie child */ void sig_chld (int sig) { pid_t pid; int status while ((pid = waitpid (-1, &status, WNOHANG) )> 0) /* empty */; return; } waitpid trả về giá trị là cấu trúc status để có thể kiểm tra thông tin về tiến trình con đã kết thúc Tùy chọn –1 để chỉ đợi tiến trình con thứ nhất Network programming Xử lý cuộc gọi hệ thống bị ngắt như thế nào? Vấn đề: Tín hiệu SIGCHLD được tạo ra khi tiến trình mẹ đang ngừng thực thi để chờ chấp nhận kết nối mới. Điều gì xảy ra khi hàm xử lý tín hiệu trả về giá trị? 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(….)) #include 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 Network programming Khi 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 gán giá trị tùy thuộc vào lỗi xảy ra Network programming Khi 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 Network programming Macros /* 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*/ Int FD_ISSET(inf fd, fd_set *fdset); Network programming Cách thực thi máy chủ 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 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 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. Gán socket kết nối vào tập mô tả file xuất nhập dữ liệu Network programming Cách thực thi máy chủ (2) 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 Tiếp tục xử lý với bước 2 ở trên Network programming Ví 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); Network programming /* 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*/ Network programming msock = 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) Denial Of Service
File đính kèm:
- Bài giảng Lập trình mạng - Nguyễn Hoài Sơn - Lập trình máy chủ.ppt