Đăng ký Đăng nhập
Trang chủ Công nghệ thông tin Kỹ thuật lập trình Bài giảng môn lập trình mạng...

Tài liệu Bài giảng môn lập trình mạng

.PDF
40
526
138

Mô tả:

bài giảng môn lập trình mạng
Chương 1: TỔNG QUAN VỀ LẬP TRÌNH MẠNG I. Họ giao thức TCP/IP 1. Mục tiêu _ Cung cấp dịch vụ truyền thông liên mạng, che dấu chi tiết kiến trúc liên mạng, che dấu chi tiết phần cứng. _ Kiến trúc phân lớp. 2. Địa chỉ IP a) Khái niệm _ Địa chỉ luận lý (địa chỉ cấp phát động hoặc tĩnh): xác định duy nhất một máy trên mạng, khác với địa chỉ card mạng. _ Địa chỉ vật lý (địa chỉ card mạng): do nhà sản xuất cấp phát. _ Phiên bản: • Ipv4: 32 bit, dạng biểu diễn số chấm thập phân (ví dụ: 192.168.10.1). • Ipv6: 128 bit. b) Phân lớp địa chỉ _ Xác định bởi những bit nhận dạng (Class ID): – Sự tương quan giữa lớp và kích thước mạng: _ Các địa chỉ IP đặc biệt: _ Các vùng địa chỉ IP dành riêng (Private Network): • 10.0.0.0 – 10.255.255.255.255 • 172.16.0.0 – 172.31.255.255 • 192.168.0.0 – 192.168.255.255 3. Một số giao thức a) Lớp Internet (Internet Layer) _ Giao thức Internet (IP – Internet Protocol). _ Giao thức Kiềm soát Thông điệp Internet (Internet Control Message Protocol – ICMP). _ Giao thức Nhóm Thông điệp Internet (Internet Group Message Protocol – IGMP). _ Giao thức Phân giải Địa chỉ (Address Resolution Protocol – ARP), giao thức Chuyển đổi Địa chỉ (Address Reverse Protocol – ARP): chuyển đổi địa chỉ vật lý, luận lý. b) Lớp Giao vận (Transport Layer) _ Cung cấp dịch vụ truyền thông giữa các tiến trình. _ Thông tin xác định tiến trình: • Địa chỉ IP. • Cổng (16 bit):  0 – 1023: well-know port, IANA (Internet Assigned Numbers Authority).  1024 – 49151: registered port.  49152 – 65535: dynamic port. _ Giao thức Kiểm soát Truyền thông (Transmission Control Protocol – TCP): • Có thiết lập cầu nối: full duplex. • Tin cậy: đúng trình tự, không thất thoát, không trùng lấp. • Byte stream: đệm dữ liệu (nơi lưu trữ dữ liệu trước khi gửi; ví dụ: nếu đệm là 1 KB, gói dữ liệu là 2 KB thì chỉ có 1KB được chuyển phải gửi lại thêm 1 KB nữa). _ Giao thức Dữ liệu Người dùng (User Datagram Protocol – UDP): • Không thiết lập cầu nối. • Không tin cậy. • Dạng truyền thông (broadcast). • Dữ liệu Người dùng (datagram). c) Lớp Ứng dụng (Application Layer) _ Cung cấp việc vận chuyển dữ liệu trong suốt giữa các hệ thống đầu cuối (end systems). II. Ứng dụng mạng _ Truyền tải tập tin (File Transfer). _ Trình duyệt web / server (Web Browser / Server). _ Thư điện tử (Electric Mail). _ Truyền tải giọng nói (Voice over IP). _ Xem phim, nghe nhạc trực tuyến (Audio / Video Online). _ Hội họp từ xa (Remote Conferencing). _ Trò chơi trực tuyến (Game Online). _… III. Mô hình ứng dụng _ Mô hình 2 tầng (2-tiers): Client / Server. _ Mô hình đa tầng (N-tiers). _ Mô hình hàng ngang (Peer to Peer). IV. Giao diện lập trình socket 1. Window Socket API _ Khái quát: • Phát triển theo đặc tả giao diện Phân phối Phần mềm Berkeley (Berkeley Soft ware Distribution – BSD Socket). • Bổ sung các tính năng hoạt động của môi trường Windows. • Hiện thực ở dạng thư viện liên kết động: wsock32.dll (winsock.h), ws2_32.del (winsock.h). • Thư viện lập trình giao tiếp với giao thức mạng. _ Dữ liệu: socket, địa chỉ IP, thông tin máy. _ Các hàm: liên kết thư viện truy xuất thông tin, chuyển đổi dữ liệu, làm việc với socket. 2. Tiếp cận hướng đối tượng a) MFC _ Thư viện hỗ trợ của Microsoft: • Che giấu chi tiết sử dụng các hàm Winsock API. • Hỗ trợ xây dựng ứng dụng Internet. _ Windows Sockets: CasyncSocket, Csocket, CsocketFile. _ Mở rộng: Win32 Internet Extensions Winlnet: • ElnternetSession. • ElnternetConnection: CftpConnection, CgopherConection, ChttConnection. • CgopherLocation. • ElnternetFile. • EfileFmd. b) .NET _ System.Net: • DNS. • IPAddress. • EndPoint. • IPHostEntry. • Socket Address. • WebRequest, WebResponse. • WebClient. •… _ System.Net.Socket: • Socket. • SocketException. • TcpClient, TcpListener. • UdpClient. •… _ System.Net.Mail: • Enet. •… c) Java _ Java.Net: • InetAddress. • Socket, ServerSocket. • SocketException. • URI+, URL. _ Javamail. Chương 2: CĂN BẢN LẬP TRÌNH WINSOCK I. Socket 1. Khái niệm _ Cơ chế trừu tượng dùng cho quá trình truyền thông giữa các tiến trình. _ Tương ứng với cấu trúc chứa các thông tin cần cho quá trình truyền thông giữa các tiến trình (IP, port). 2. Quản lý socket _ Cấu trúc dữ liệu do hệ điều hành quản lý. _ Ứng dụng sử dụng thông qua handle. 3. Phân loại _ Stream Socket: TCP Socket. _ Datagram Socket: UDP Socket. _ Raw Socket. II. Phân nhóm hàm thư viện _ Liên kết thư viện, kết thúc. _ Truy xuất thông tin. _ Chuyển đổi dạng dữ liệu. _ Các hàm thao tác trên socket: • Tạo socket, đóng socket. • Thiết lập cầu nối. • Gửi, nhận dữ liệu. III. Liên kết thư viện _ Liên kết thư viện: int WSAStartup (WORD wVersionRequested, LPWSADATA lpwaData); _ Kết thúc: int WSACleanup(); _ Truy xuất mã lỗi sai: int WSAGetLastError(); _ Lưu ý: • File StdAfx.h: #include • Lớp ứng dụng: bool CDDemoApp :: InitInstance() { ... if (!AfxSocketInit()) { AfxMessageBox(IDP_SOCKETS_INIT_FAILED); return false; } ... return false; } IV. Truy xuất thông tin 1. Thông tin máy _ Các phương thức: • int gethost name (char FAR* name, int len); • PHOSTENT gethostbyname (const char FAR* hostname); • PHOSTENT gethostbyaddr (const char FAR* addr, int len, int af); _ Ví dụ: char sethostName [MAX_LEN]; if (gethostname (sethostName, MAX_LEN) != SOCKET_ERROR) { //… } else { //… } _ Cấu trúc thông tin máy: • struct hostent { char FAR* h_name; char FAR* FAR* h_aliases; short h_addtype; short h_lenght; char FAR* FAR* h_addr_list; #define h_addr h_addr_list[0]; }; • PHOSTENT pHostEnt = gethostbyname (set); if (pHostEnt != NULL) { //… } 2. Thông tin dịch vụ _ Cú pháp: PSERVENT getservbyname (constchar char FAR* name, const FAR* proto); V. Chuyển đổi thông tin dữ liệu 1. Chuyển đổi trật tự byte _ Trật tự byte: • Lưu trữ số nguyên trên máy tính: Host Byte Order Little-Endian Big-Endian short 12AB AB12 12AB long 12AB34CD CS34AB12 12AB34CD • Quy ước lưu trữ số nguyên trên mạng (Network Byte Order): Big-Endian. _ Các hàm chuyển đổi: • u_short ntohs (u_short); • u_long ntohl (u_long); • u_short htons (u_short); • u_long htonl (u_long); 2. Chuyển đổi dạng địa chỉ _ Dạng biểu diễn địa chỉ IPv4: • Số nguyên 4 byte. • Chuỗi dấu chấm thập phân (Dotted Decimal). _ Các hàm chuyển đổi: • unsigned long inet_addr (const char FAR* CD); • char FAR* inet_ntoa (struct in_addr in); _ Cấu trúc địa chỉ: struct in_addr { union { struct { u_char s_b1, s_b2, s_b3, s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; #define #define #define #define #define #define s_addr S_un.S_addr // can be used for most tcp & ip code s_host S_un.S_un_b.s_b2 // host on imp s_net S_un.S_un_b.s_b1 // network s_imp S_un.S_un_w.s_w2 // imp s_impno S_un.S_un_b.s_b4 // imp # s_lh S_un.S_un_b.s_b3 // logical host }; _ Ví dụ: //… PHOSTENT pHost = gethostbyname (“…”); if (pHost != NULL) { IN_ADDR inAddr; memcpy (&inAddr, pHost->h_addr, 4); // inAddr.s_addr = pHost->h_addr; Cstring sAddress = inet_ntoa (inAddr); //… } memcpy (&inAddr, pHost->h_addr, 4); VI. Các hàm socket 1. Quy trình sử dụng a) Có thiết lập cầu nối b) Không thiết lập cầu nối 2. Chi tiết sử dụng a) Tạo socket _ Cú pháp: SOCKET socket (int af, int type, int protocol); _ Thông số: • af: họ địa chỉ AF_INET. • type: loại địa chỉ – SOCK_STREAM (có thiết lập cầu nối – TCP), SOCK_DGRAM (không thiết lập cầu nối – UDP). • protocol: loại giao thức – 0. _ Kết quả trả về: • Thành công: handle của socket vừa tạo. • Thất bại: INVALID_SOCKET. _ Ví dụ: SOCKET s; // socket descriptor char lpszMessage[100]; // informational message s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) wsprintf (lpszMessage, “socket() generated error %d”, WSAGetLastError()); else lstrcpy(lpszMessage, “socket() succeeded”); MessageBox(NULL, lpszMessage, “Info”, MB_OK); b) Đóng socket _ Cú pháp: int closesocket(SOCKET s); _ Thông số: s: handle máy muốn đóng. _ Kết quả trả về: Thất bại: SOCKET_ERROR. c) Gán thông tin socket _ Cú pháp: int bind (SOCKET s, const struct sockaddr FAR* addr, int addrlen); _ Thông số: • s: handle của socket chờ gán thông tin. • addr: địa chỉ cấu trúc dùng để chứa thông tin socket phía kết nối đến. • addrlen: địa chỉ biến chứa kích thước cấu trúc bởi addr. _ Cấu trúc thông tin socket: struct sockaddr { u_short sa_family; // address family char sa_data[14]; // up to 14 bytes of direct address }; struct sockaddr_in { short sin_family; // address family u_short sin_port; // service port struct in_addr sin_addr; // Internet address char sin_zero[8]; // filler }; _ Ví dụ: SOCKET s; // socket descriptor char lpszMessage[100]; // informational message SOCKADDR_IN addr; // Internet address // create a stream socket s = socket(AF_INET, SOCK_STREAM, 0); if (s != INVALID_SOCKET) { // fill out the socket’s address information addr.sin_family = AF_INET; addr.sin_port = htons(1050); addr.sin_addr.s_addr = htonl(INADDR_ANY); // bind the socket to its address if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) { wsprintf(lpszMessage, “ bind() generated error %d”, WSAGetLastError()); MessageBox(NULL, lpszMessage, “Info”, MB_OK); } else { //... } } • Trường hợp không chỉ định port: sin_port = 0; • Lấy thông tin socket: int getsockname (SOCKET s, struct sockAddr* addr, int* addrlen); d) Lắng nghe _ Cú pháp: int listen (SOCKET s, int backlog); _ Thông số: • s: handle máy muốn đóng. • backlog: kích thước hàng đợi kết nối. _ Kết quả trả về: Thất bại: SOCKET_ERROR. _ Ví dụ: SOCKET s; // socket descriptor char lpszMessage[100]; // informational message SOCKADDR_IN addr; // Internet address // create a stream socket s = socket(AF_INET, SOCK_STREAM, 0); if (s != INVALID_SOCKET) { // fill out the socket’s address information addr.sin_family = AF_INET; addr.sin_port = htons(1050); addr.sin_addr.s_addr = htonl(INADDR_ANY); // bind the socket to its address if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) != SOCKET_ERROR) { // listen for connections (queueing up to three) if (listen(s, 3) == SOCKET_ERROR) { wsprintf(lpszMessage, “listen() generated error %d”, WSAGetLastError()); MessageBox(lpszMessage, “Info”); } else { //... } } } e) Tiếp nhận _ Cú pháp: SOCKET socket (SOCKET s, struct sockAddr FAR* addr, int FAR* addrlen); _ Thông số: • s: handle của socket chờ tiếp nhận “nói” (“lắng nghe”). • addr: địa chỉ cấu trúc dùng để chứa thông tin socket phía kết nối đến. • addrlen: địa chỉ biến chứa kích thước cấu trúc bởi addr. _ Kết quả trả về: • Thành công: handle của socket giao tiếp với phía kết nối đến • Thất bại: INVALID_SOCKET. _ Ví dụ: SOCKET s; // socket descriptor SOCKET clientS; // client socket descriptor char lpszMessage[100]; // informational message SOCKADDR_IN addr; // Internet address SOCKADDR_IN clientAddr; // Internet address IN_ADDR clientIn; // IP address int nClientAddrLen; // create a stream socket s = socket(AF_INET, SOCK_STREAM, 0); if (s != INVALID_SOCKET) { // fill out the socket’s address information addr.sin_family = AF_INET; addr.sin_port = htons(1050); addr.sin_addr.s_addr = htonl(INADDR_ANY); // bind the socket to its address if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) != SOCKET_ERROR) { // listen for connections (queueing up to three) if (listen(s, 3) != SOCKET_ERROR) { // set the size of the client address structure nClientAddrLen = sizeof(clientAddr); // accept a connection clientS = accept(s, (LPSOCKADDR)&clientAddr, &nClientAddrLen); if (clientS == INVALID_SOCKET) { wsprintf(lpszMessage, “ accept() generated error %d”, WSAGetLastError()); MessageBox(lpszMessage, “Info”); } else { // copy the four byte IP address into an IP address structure memcpy(&clientIn, &clientAddr.sin_addr.s_addr, 4); // print an informational message wsprintf(lpszMessage, “accept() ok: client IP address is %s, port is %d”, inet_ntoa(clientIn), ntohs(clientAddr.sin_port)); 114 Part II n Basics of WinSock Programming MessageBox(lpszMessage, “Info”); ... } } } } _ Trường hợp lấy thông tin phía kết nối sau khi tiếp nhận kết nối: int getpeername (SOCKET s, struct sockAddr* addr, int* addrlen); _ Ví dụ: // accept a connection clientS = accept(s, NULL, NULL); if (clientS == INVALID_SOCKET) { wsprintf(lpszMessage, “accept() generated error %d”, WSAGetLastError()); MessageBox(lpszMessage, “Info”); } else { if (getpeername(clientS, (LPSOCKADDR)&clientAddr, &nClientAddrLen)) == SOCKET_ERROR) { wsprintf(lpszMessage, “getpeername() generated error %d”, WSAGetLastError()); MessageBox(lpszMessage, “Info”); } else { // copy the four byte IP address into an IP address structure memcpy(&clientIn, &clientAddr.sin_addr.s_addr, 4); // print an informational message wsprintf(lpszMessage, “client IP address is %s, port is %d”, inet_ntoa(clientIn), ntohs(clientAddr.sin_port)); MessageBox(lpszMessage, “Info”); //... } } f) Kết nối _ Cú pháp: int connect (SOCKET s, const struct sockAddr FAR* addr, int addrlen); _ Thông số: • s: handle của socket thực hiện kết nối. • addr: địa chỉ cấu trúc dùng để chứa thông tin socket phía chờ kết nối. • addrlen: địa chỉ biến chứa kích thước cấu trúc bởi addr. _ Kết quả trả về: Thất bại: SOCKET_ERROR. _ Ví dụ: bool ConnectToServer (const Cstring &sServerAddress, short nServerPort) { bool bSuccess = true; m_hSocket = socket (AP_INET, SOCK_STREAM, 0); if (m_hSocket == INVALID_SOCKET) bSuccess = false; else { SOCKADD_IN sockAddr; addrServer.sin_family = AF_INET; addrServer.sin_port = htons(nServerPort); addrServer.sin_addr.s_addr = inet_addr(sServerAddress); if (connect(s, (LPSOCKADDR) &addrServer, sizeof(addrServer)) == SOCKET_ERROR) { bSuccess = false; closesock(m_hSocket); } return bSuccess; } } g) Gửi nhận _ Có thiết lập cầu nối: • Cú pháp: int send (SOCKET s, const char FAR* buf, int len, int flags); int recv (SOCKET s, const char FAR* buf, int len, int flags); • Thông số:  s: handle của socket bên gửi.  buf: địa chỉ vùng đệm chứa dữ liệu cần gửi.  len: kích thước dữ liệu gửi (tính theo byte).  flags: 0, MSG_DONTROUTR, MSG, OOB. • Kết quả trả về:  Thành công: số byte dữ liệu đã gửi.  Thất bại: SOCKET_ERROR. • Ví dụ:  Gửi: SOCKET s; // socket to communicate over char pszBuf[100]; // buffer to send int nBufLen; // number of bytes in buffer to send int nBytesSent; // bytes sent int nError; // error status // create, bind, and connect socket s ... lstrcpy(pszBuf, “Hello, World!”); nBufLen = lstrlen(pszBuf); nBytesSent = send(s, pszBuf, nBufLen, 0); if (nBytesSent == SOCKET_ERROR) r = WSAGetLastError(); else { //... }  Nhận: SOCKET s; // socket to communicate over #define BUFSIZE (100) // receive buffer size char pszBuf[BUFSIZE]; // buffer to receive data int nBytesRecv; // number of bytes received int nError; // error status // create, bind, and connect socket s ... nBytesRecv = recv(s, pszBuf, BUFSIZE, 0); if (nBytesRecv == SOCKET_ERROR) nError = WSAGetLastError(); else { //... } _ Không thiết lập cầu nối: • Cú pháp: int sendto (SOCKET s, const char * buf, int size, int flags, const struct sockAddr FAR* addrTo); int recvfrom (SOCKET s, char* buf, int size, int flags, struct sockAddr FAR* addrFrom); • Thông số:  s: handle của socket bên gửi.  buf: địa chỉ vùng đệm chứa dữ liệu cần gửi.  len: kích thước dữ liệu gửi (tính theo byte).  flags: 0, MSG_DONTROUTR, MSG, OOB. • Kết quả trả về:  Thành công: số byte dữ liệu đã gửi.  Thất bại: SOCKET_ERROR. • Ví dụ:  Gửi: SOCKET s; SOCKADDR_IN addr; #define BUFSIZE (100) char pszBuf[BUFSIZE]; int nBufLen; int nBytesSent; int nError; s = socket(AF_INET, SOCK_DGRAM, 0); if (s == INVALID_SOCKET) { nError = WSAGetLastError(); // ... } else { // fill out the address of the recipient addr.sin_family = AF_INET; addr.sin_port = htons(2050); addr.sin_addr.s_addr = inet_addr(“166.78.16.150”); // assign some data to send lstrcpy(pszBuf, “Hello, World!”); nBufLen = lstrlen(pszBuf); // send the datagram nBytesSent = sendto(s, pszBuf, nBufLen, (LPSOCKADDR)&addr, sizeof(addr)); if (nBytesSent == SOCKET_ERROR) { //... } else { //... } closesocket(s); } 0,  Nhận: char pszMessage[100]; // informational message SOCKET s; // socket to receive data on SOCKADDR_IN addr; // address of the socket #define BUFSIZE (100) // receive buffer size char pszBuf[BUFSIZE]; // receive buffer int nBytesRecv; // number of bytes received int nError; // error code SOCKADDR_IN addrFrom; // address of sender int nAddrFromLen = sizeof(addrFrom); // lengh of sender structure IN_ADDR inFrom; // IP address of sender s = socket(AF_INET, SOCK_DGRAM, 0); if (s == INVALID_SOCKET) { nError = WSAGetLastError(); // ... } else { // fill out the name this server will read data from addr.sin_family = AF_INET; addr.sin_port = htons(2050); addr.sin_addr.s_addr = htonl(INADDR_ANY); // bind the name to the socket if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) { nError = WSAGetLastError(); // ... } else { nBytesRecv = recvfrom(s, pszBuf, 100, 0, (LPSOCKADDR)&addrFrom, &nAddrFromLen); if (nBytesRecv == SOCKET_ERROR) { nError = WSAGetLastError(); // ... } else { // got some data ... // copy the four byte IP address into an IP address structure memcpy(&inFrom, &addrFrom.sin_addr.s_addr, 4); // print an informational message wsprintf(pszMessage, “server received %d bytes from %s, port is %d”, nBytesRecv, inet_ntoa(inFrom), ntohs(addrFrom.sin_port)); } closesocket(s); } } 3. Hai chế độ hoạt động của socket a) Blocking _ Các hàm thực hiện hoạt động nhập / xuất trên socket chỉ trả về khi tác vụ hoàn tất → Tiến trình bị chặn nếu tác vụ chưa hoàn tất (sự kiện mong đợi chưa xảy ra). b) Non-blocking _ Các hàm thực hiện hoạt động nhập / xuất trên socket trở vền gay sau khi được gởi. _ Phát sinh lỗi WSAEWOULDBLOCK nếu tác vụ yêu cầu chưa hoàn tất → Tiến trình không bị chặn. 4. Mô hình xử lý a) Blocking _ Sử dụng các hàm socket theo chế độ hoạt động mặc định (blocking). _ Thường sử dụng giải pháp xử lý đa luồng (multithread). b) WSAAsyncSelect _ Mô hình xử lý bất đồng bộ: • Ứng dụng đăng ký sự kiện mong đợi xảy ra trên socket. • Hệ thống giám sát và gửi thông điệp báo hiệu đến ứng dụng khi sự kiện xảy ra. • Ứng dụng xử lý khi nhận được thông điệp. _ Giải pháp phù hợp với mô hình hoạt động hướng thông điệp của Windows. _ Chọn chế độ xử lý bất đồng bộ: int WSAAsyncSelect (SOCKET s, HWND hWnd, UINT message, long IEvent), trong đó sự kiện mong đợi (IEvent) bao gồm: • FD_ACCEPT, FD_CONNECT. • FD_READ, FD_WRITE. • FD_CLOSE. • FD_OOB. _ Đăng ký nhiều sự kiện: • Đúng: WSAAsyncSelect (s, hWnd, USER_MESSAGE, FD_READ | FD_CLOSE); • Sai: Thông điệp báo hiệu do người lập trình định nghĩa #define (WM_USERtn) const UINT = WM_USERtn; c) Tạo trình xử lý thông điệp (MFC) _ Hàm thành phần của lớp cửa sổ nhận thông điệp: • Khai báo: LRESULT (WPARAM wParam, LPARAM lParam); trong đó:  wParam: handle socket xảy ra sự kiện mong đợi.  lParam: mã sự kiện xảy ra và lỗi sai. • Định nghĩa: LRESULT ::(WPARAM wParam, LPARAM lParam) { if (WSAGETSELECTERROR (lParam == 0) { switch (WSAGETSELECTEVENT (lParam)) { case FD_ACCEPT: //...; break; case FD_READ: //...; break; case FD_CLOSE: //...; break; //... } } else { //... } return LRESULT(); } • Ví dụ: const UINT MSG_ASYNC = WM_USER + 1; bool ::ConnectToServer (const Cstring &sIpAddr, short nServerPort) { bool bSuccess = true; m_hSocket = ...; if (m_hSocket != INVALID_SOCKET) { SOCKADD_IN sockAddr; if (connect (m_hSocket, ..., ... ) == SOCKET_ERROR || WSAAsynSelect (m_hSocket, m_h Wnd, MSG_ASYNC, FD_READ | FD_CLOSE) == SOCKET_ERROR) { closesocket (m_hSocket); bSuccess = false; } } else bSuccess = false; return bSuccess; } LRESULT ::(WPARAM wParam, LPARAM lParam) { if (WSAGETSELECTERROR (lParam) == 0) { switch (WSAGETSELECTEVENT) { case FD_READ: ReceiveData(); break; case FD_CLOSE: closesocket (m_hSocket); break; } } else { //... } return 0L; } _ Đăng ký trình xử lý: BEGIN_MESSAGE_MAP(, ) //... ON_MESSAGE (, ) END_MESSAGE_MAP trong đó: • MSG_ASYNC: thông điệm do người lập trình định nghĩa. • OnAsyncSelect tên trình xử lý thông điệp. VII. Ứng dụng MiniChat 1. Mô tả _ Hai thành phần: • Server:  Quản lý người dùng theo nhóm.  Tổ chức đăng nhập theo nhóm.  Tổ chức trao đổi trong nhóm. • Client:  Giao tiếp với người dùng.  Cho phép đăng nhập theo nhóm.  Cho phép “trò chuyện” trong nhóm. _ Giao thức ứng dụng: • Server port: 2013: có thiết lập cầu nối. • Tập lệnh (Client  Server):  GLIST: yêu cầu danh sách nhóm : LOGIN ,  ULIST: yêu cầu danh sách người dùng trong nhóm:  Gửi nội dung trò chuyện: TALKS  LGOUT: đăng xuất.  Ký hiệu đặc biệt để ngăn cắt: CRLF. • Dạng hồi đáp (Server  Client):  Khi thiết lập cầu nối: xuất lời chào.  Khi nhận các lệnh: trong đó:  : 0 là thành công, 1 là thất bại.  :  Lệnh GLIST (trường hợp thành công): message = , …,  Lệnh ULIST (trường hợp thành công): message = , …,  Lệnh LOGIN, LGOUT, TALKS (hoặc trường hợp thất bại): message = 2. Client a) Giao diện _ Hộp thoại chính. _ Hộp thoại phụ trợ. b) Trạng thái hoạt động c) Cài đặt _ Ứng dụng dạng hộp thoại. _ Lớp hộp thoại ứng dụng: • Dữ liệu:  socket handle: SOCKET m_hSocket.  Trạng thái hiện tại: enum FSTATE {FS_BEGIN, FS_CONNECT, FS_ULIST, FS_TALKS, FS_LOGOUT}; FSTATE m_fState  Tên đăng nhập: Cstring m_sUserName FS_GLIST, FS_LOGIN,  Thành phần liên kết với điều khiển: CString m_sMessage CListBox m_lbGroups CListBox m_lbUsers CListBox m_lbContent • Thao tác:  Trình xử lý sự kiện:  Tác động trên các nút:  Kết nối (Connect):  Kiểm tra trạng thái chưa kết nối.  Mở hộp thoại kết nối (Connect Dialog).  Thực hiện kết nối – gọi thao tác phụ trợ (ConnectToServer).  Đặt trạng thái: đang kết nối.  Hoạt động bất đồng bộ:  Lấy Danh sách Nhóm (Get Groups):  Kiểm tra trạng thái kết nối.  Gởi lệnh GLIST.  Đặt trạng thái: đang lấy danh sách nhóm.  Đăng nhập (Login):  Kiểm tra trạng thái đã kết nối và chưa đăng nhập.  Mở hộp thoại đăng nhập (Login Dialog).  Gởi lệnh LOGIN.  Đặt trạng thái: đang đăng nhập.  Lấy Danh sách Người dùng (Get Users):  Kiểm tra trạng thái đã đăng nhập.  Gởi lệnh ULIST.  Đặt trạng thái: đang lấy danh sách người dùng.  Gửi (Send):  Kiểm tra trạng thái đã đăng nhập.  Gởi lệnh TALKS.  Đặt trạng thái: đang trao đổi.  Đăng xuất (Logout):  Kiểm tra trạng thái đã đăng nhập.  Gởi lệnh LGOUT.  Đặt trạng thái: đang thoát ra khỏi nhóm.  Hoạt động bất đồng bộ:  Kiểm tra mã báo lỗi.  Thực hiện / gọi tác vụ xử lý sự kiện tương ứng:  FD_CONNECT: báo kết nối thành công.  FD_READ: nhận và xử lý hồi đáp từ server.  FD_CLOSE: đóng socket  mfState = FS_BEGIN  Thông báo đã đóng kết nối  Xử lý hồi đáp:  Kết nối (Connect):  Hiển thị lời chào nhận từ server.  Đặt trạng thái: đã kết nối.  Lấy Danh sách Nhóm (Get Groups):  Kiểm tra mã hồi đáp: nếu thành công thì hiển thị danh sách nhóm  tách các tên nhóm và đưa vào listbox; nếu thất bại thì hiển thị thông điệp báo lỗi nhận từ server.  Hiển thị thông báo nhận từ server.  Đăng nhập (Login):  Kiểm tra mã hồi đáp: nếu thành công thì đặt trạng thái đã đăng nhập (sẵn sàng đón nhận thông điệp); nếu thất bại thì đặt trạng thái: đã kết nối (chưa đăng nhập).  Hiển thị thông báo nhận từ server.  Lấy Danh sách Người dùng (Get Users):  Kiểm tra mã hồi đáp: nếu thành công thì hiển thị danh sách người dùng; nếu thất bại thì hiển thị thông điệp báo lỗi nhận từ server.  Đặt trạng thái: sẵn sàng nhận thông điệp.  Gửi (Send):  Kiểm tra mã hồi đáp: Thành công: đặt trạng thái: đã kết nối (chưa đăng nhập).  Hiển thị thông báo nhận từ server.  Thao tác phụ trợ:  Tạo cầu nối đến server (ConnectToServer):  Thông số: [vào] Địa chỉ server (dạng chuỗi), [vào] số hiệu cổng.  Kết quả trả về: nếu thành công là true; nếu thất bại là false.  Thực hiện: Kết nối (hoạt động theo chế độ blocking), chọn chế độ hoạt động bất đồng bộ: FD_READ | FD_CLOSE.  Nhận dữ liệu (ReceiveData):  Xử lý nhận dữ liệu: nội dung hồi đáp từ server.  Gọi thao tác xử lý hồi đáp tương ứng, tùy thuộc trạng thái hiện tại của client.  Gởi dữ liệu (SendData). 3. Server a) Giao diện _ Hộp thoại chính. _ Hộp thoại phụ trợ. b) Cài đặt _ Ứng dụng dạng hộp thoại. _ Các lớp phụ trợ: • CUserSocket:  Kế thừa từ CObject.  Dữ liệu:  socket handle: SOCKET m_hSocket.  Tên đăng nhập: CString m_sName.  Địa chỉ, cổng phía kết nối: int m_nPeerPort. CString m_sPeerAddress.  Thao tác:  Truy xuất thông tin.  Cập nhật thông tin. • CUserGroup:  Kế thừa từ CObject.  Dữ liệu:  Tên nhóm: CString m_sName.  Danh sách các usersocket: CTypedPtrList m_userList.  Thao tác:  Thêm người dùng.  Lấy người dùng ra khỏi nhóm.  Tìm người dùng.  Truy xuất tên nhóm.  Lấy danh sách tên nhóm. • CGroupList:  Dữ liệu: Danh sách các nhóm: CArray m_groupList.  Thao tác:  Tìm nhóm.  Theo tên nhóm.  Theo socket handle.  Tìm nhóm và người dùng: theo socket handle.  Lấy danh sách tên nhóm. _ Lớp hộp thoại ứng dụng: • Dữ liệu:  socket handle – “lắng nghe”.  Danh sách nhóm: CGroupList m_groupList.  Danh sách người dùng mới kết nối: CUserList m_groupNewUsers.  Thành phần liên kết với các điều khiển. • Thao tác:  Khởi tạo:  Thông số: port lắng nghe.  Kết quả trả về: nếu thành công là socket handle; nếu thất bại là INVALID_SOCKET.  Dữ liệu nhóm: gồm tập tin dữ liệu hoặc cơ sở dữ liệu.  Socket “lắng nghe” – InitListenSocket: Socket() Bind() Listen() WSAAsyncSelect() - FD_ACCEPT  Trình xử lý sự kiện:  Tác động trên phần tử giao diện: ListBox: Nhóm. Button: Xóa, ẩn.  Hoạt động bất đồng bộ.  Xử lý đáp ứng yêu cầu từ client:  GLIST:  Tạo nội dung hồi đáp theo quy định: sử dụng thao tác lấy danh sách tên nhóm của CGroupList.  Gởi hồi đáp.  LOGIN:  Kiểm tra người dùng chưa đăng nhập. Kiểm tra thông tin đăng nhập:  Tên nhóm có thật:  Tên người dùng không trùng lắp.  Chuyển người dùng sang nhóm đăng nhập (trường hợp thành công):  Xóa khỏi danh sách người dùng mới kết nối.  Thêm vào danh sách người dùng của nhóm đăng nhập.  Cập nhật tên đăng nhập.  Tạo hồi đáp theo quy định gửi hồi đáp.  ULIST:  Kiểm tra là người dùng đã đăng nhập (hiện diện trong danh sách của một nhóm).  Tạo nội dung hồi đáp theo quy định (sử dụng thao tác lấy danh sách tên người dùng của CUserGroup).  Gửi hồi đáp.  LGOUT:  Kiểm tra là người dùng đã đăng nhập (hiện diện trong danh sách của một nhóm).  Chuyển người dùng sang danh sách mới kết nối (trường hợp thành công).  Xóa khỏi danh sách người dùng mới của nhóm.  Thêm vào danh sách người dùng mới kết nối.  Tạo hồi đáp theo quy định.  Gửi hồi đáp.  “Broadcast”:  Kiểm tra là người dùng đã đăng nhập (hiện diện trong danh sách của một nhóm).  Sử dụng thao tác tìm nhóm và người dùng theo socket handle của lớp CGroupList.  Tạo thông điệp “broadcast” (tên người dùng gửi + “:” + nội dung trò chuyện).  Gửi thông điệp “broadcast” cho các người dùng còn lại trong nhóm.  Thao tác phụ trợ:  Tiếp nhận kết nối (AcceptConnection):  Nhận dữ liệu (ReceiveData).  Phân tích yêu cầu (ParseRequest):  Xác định mã lệnh, gọi tác vụ.  Xừ lý đáp ứng yêu cầu.  Gửi dữ liệu (SendData). 4. Minh họa _ Thiết lập kết nối: bool CChatClDlg::ConnectToServer (const CString &sServerAddress, short nServerPort) { bSuccess = true; m_hSocket = socket (…, …, 0); if (m_hSocket != INVALID_SOCKET) { SOCKETADD_IN sockAddr; sockAddr.sin_family = … sockAddr.sin_port = htons (nServerPort); if (isalpha (sServerAddress[0]) { PHOSTENT pHost = gethostbyname (sServerAddress); if (pHost) memcpy (&sockAddr.sin_addr, pHost->h_addr, 4); } else sockAddr.sin_addr = inet_addr (sServerAddress); if (connect (m_hSocket, (LPSOCKADDR) & sockAddr, sizeof(sockAddr)) == SOCKET_ERROR || WSAAsyncSelect, FDREAD | FDCLOSE) == SOCKET_ERROR) { closesocket (m_hSocket); bSuccess = false; } else bSuccess = false; return bSuccess; } } void CChatClDlg::OnBnClickedConnect() { if (m_fState < FSCONNECT) // chưa kết nối { ConnectDlg dlg; // hộp thoại để nhập thông tin server if (dlg_DoModal() == IDOK) { if (ConnectToServer (dlg.m_nServerAddress) dlg.m_nServerPort) { mlbContent.AddString (_T(“Đã kết nối”)); m_fState = FS_CONNECT; } else mlbContent.AddString (_T(“Kết nối thất bại”)); } } } == _ Trình xử lý cho hoạt động xử lý bất đồng bộ: • Hàm thành phần của C…Dlg (lớp hộp thoại chính của ứng dụng). LRESULT C…Dlg::OnAsyncSelect (WPARAM wParam, LPARAM lParam) { if (WSAGETSELECTERROR (lParam) == 0) { switch (WSAGETSELECT (lParam)) { case FD_READ: ReceiveData(); break; case FD_CLOSE: closesocket (m_hSocket); m_fState = FS_BEGIN; m_lbContent.AddString(_T(“...”); break; } } else { //…} return LRESULT(0); } • Đăng ký trình xử lý: BEGIN_MESSAGE_MAP (C…Dlg:Cdialog) ... ON_MESSAGE (MSG_ASYNC, OnAsyncSelect); // tự định nghĩa END_MESSAGE_MAP const UNIT MSG_ASYNC = WM_USER + 1; • Hàm thành phần thực hiện nhận dữ liệu: const int BUF_LEN = 255; void C…Dlg::ReceiveData() { char szBuffer[BUF_LEN + 1]; int nRecvBytes = recv (m_hSocket, szBuffer, BUF_LEN , 0); if (nRecvBytes > 0) { szBuffer[nRecvBytes] = ‘\0’; switch (m_fState): { case FS_CONNECT: m_lbContent.AddString (szBuffer); break; case FS_GLIST: ProcessGListReply(nRecvBytes); break; } }
- Xem thêm -

Tài liệu liên quan