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

  • Số trang: 40 |
  • Loại file: PDF |
  • Lượt xem: 261 |
  • Lượt tải: 3
tranvantruong

Đã đăng 3224 tài liệu

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 -