Khi bạn đã tạo một socket, bạn có thể dùng nó để thiết lập một kết nối đến server . Ta dùng hàm connect() để thực hiện việc này: int connect (SOCKET s, const struct sockaddr *name, int namelen ). Tham số đầu tiên, s, xác định socket descriptor được trả về bởi hàm socket() .
Kết nối đến Server ( từ Client ) : Khi bạn tạo socket, bạn dùng để thiết lập kết nối đến server Ta dùng hàm connect() để thực việc này: int connect (SOCKET s, const struct sockaddr *name, int namelen ) Tham số đầu tiên, s, xác định socket descriptor trả hàm socket() Tham số name socket address structure , SOCKADDR_IN , xác định server mà ta định kết nối Tham số namelen chiều dài đệm dành cho tham số name Nếu ta thành công việc thiết lập kết nối đến Server xác định tham số name , hàm trả giá trị 0, ngược lại lỗi SOCKET_ERROER xảy Để tìm xem thơng tin không thiết lập kết nối ta gọi hàm WSAGetLastError () Nên nhớ ta gọi hàm connect() socket kết nối Một kết nối thiết lập, socket sẵn sàng gửi nhận liệu Chú ý rằng: Nếu kết nối bị đứt (broken) lúc Client Server truyền tin với ứng dụng ta cần phải bỏ socket cũ tạo socket để thiết lập lại việc truyền tin Client Server Ví dụ sau trình bày cách kết nối đến Server : // First, get the host information HOSTENT *hostServer = gethostbyname("www.microsoft.com"); if(hostServer == NULL) { int iSocketError = WSAGetLastError(); return FALSE; } // Set up the target device address structure SOCKADDR_IN sinServer; memset(&sinServer, 0, sizeof(SOCKADDR_IN)); sinServer.sin_family = AF_INET; sinServer.sin_port = htons(80); sinServer.sin_addr = *((IN_ADDR *)hostServer->h_addr_list[0]); // Connect with a valid socket if(connect(s, (SOCKADDR *)&sinServer, sizeof(sinServer)) == SOCKET_ERROR) { int iSocketError = WSAGetLastError(); return FALSE; } // Do something with the socket closesocket(s); Gửi nhận liệu : Chúng ta thiết lập kết nối đến server, sẵn sàng cho việc gửi nhận liệu hai máy mạng Trên kết nối socket, liệu truyền hai hướng, Server Client dùng phương thức để truyền tin với Để truyền liệu kết nối socket ta dùng phương thức send(), xác định sau: int send (SOCKET s, const char *buf, int len, int flags) ; Tham số s socket handle mà ta dùng với hàm connect () trên, tạo lúc đầu hàm socket() Tham số buf trỏ, trỏ đến đệm chứa liệu mà ta muốn gửi, chiều dài xác định tham số len Tham số cuối cùng, flags, dùng để xác định cách liệu gửi, có giá trị MSG_DONTROUTE Thông thường, tham số thiết lập 0, MSG_DONTROUTE dùng cho việc kiểm tra định đường cho thông điệp Giá trị trả hàm send() số byte thực gửi mạng SOCKET_ERROR có lỗi việc truyền liệu Để nhận liệu socket, ta dùng hàm recv(): int recv ( SOCKET s , char *buf , int len , int flags ) ; Tương tự trên, tham số s socket mà thiết lập để nhận liệu Tham số thứ hai, buf, đệm để nhận liệu, kích thước xác định tham số len Cuối cùng, tham số flags, thường thiết lập Giá trị trả hàm recv() số byte nhận kết nối bị đóng Hàm trả SOCKET_ERROR có lỗi xảy Chú ý rằng: hai hàm send() recv() khơng ln ln đọc ghi xác số lượng liệu mà ta yêu cầu Điều TCP/IP cấp phát số lượng không gian đệm có hạn cho hàng đợi liệu vào, thường đệm đầy nhanh Ví dụ, yêu cầu file 10 MB từ trang web, hàng đợi liệu vào ta khóa ta đọc liệu từ hàng đợi (dùng hàm receive()) Việc truyền liệu tương tự thế, cần đảm bảo rằng: tất liệu gửi Ví dụ sau thể việc gửi liệu đệm TCP: // Send a request to the server char cBuffer[1024] = ""; int nBytesSent = 0; int nBytesIndex = 0; // Set up the buffer to send sprintf(cBuffer, "GET / HTTP/1.0\r\n\r\n"); int nBytesLeft = strlen(cBuffer); // Send the entire buffer while(nBytesLeft > 0) { nBytesSent = send(s, &cBuffer[nBytesIndex], nBytesLeft, 0); if(nBytesSent == SOCKET_ERROR) break; // See how many bytes are left If we still need to send, loop nBytesLeft -= nBytesSent; nBytesIndex += nBytesSent; } Ví dụ: Ví dụ sau trình bày cách dùng socket TCP để tạo client để kết nối với trang web, gửi yêu cầu, nhận trang web HTML mặc định Khi thực thành cơng, nội dung Message Box Bộ đệm thực trả từ yêu cầu trình bày hình sau: Hình 3.40 // Initialize Winsock WSADATA wsaData; memset(&wsaData, 0, sizeof(WSADATA)); if(WSAStartup(MAKEWORD(1,1), &wsaData) != 0) return FALSE; // Create a connection-oriented socket SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Check to see if we have a valid socket if(s == INVALID_SOCKET) { int iSocketError = WSAGetLastError(); return FALSE; } // Get the host information HOSTENT *hostServer = gethostbyname("www.microsoft.com"); if(hostServer == NULL) { int iSocketError = WSAGetLastError(); return FALSE; } // Set up the target device address structure SOCKADDR_IN sinServer; memset(&sinServer, 0, sizeof(SOCKADDR_IN)); sinServer.sin_family = AF_INET; sinServer.sin_port = htons(80); sinServer.sin_addr = *((IN_ADDR *)hostServer->h_addr_list[0]); // Connect if(connect(s, (SOCKADDR *)&sinServer, sizeof(sinServer)) == SOCKET_ERROR) { int iSocketError = WSAGetLastError(); return FALSE; } // Send a request to the server char cBuffer[1024] = ""; int nBytesSent = 0; int nBytesIndex = 0; // Set up the buffer to send sprintf(cBuffer, "GET / HTTP/1.0\r\n\r\n"); int nBytesLeft = strlen(cBuffer); // Send the entire buffer while(nBytesLeft > 0) { nBytesSent = send(s, &cBuffer[nBytesIndex], nBytesLeft, 0); if(nBytesSent == SOCKET_ERROR) break; // See how many bytes are left If we still need to send, loop nBytesLeft -= nBytesSent; nBytesIndex += nBytesSent; } // Get the response TCHAR tchResponseBuffer[1024] = TEXT("\0"); char cResponseBuffer[1024] = ""; BOOL fBreak = FALSE; int nBytesReceived = 0; while(!fBreak) { nBytesReceived = recv(s, &cResponseBuffer[0], 1024, 0); if(nBytesReceived == SOCKET_ERROR) break; // Convert the data from ANSI to Unicode mbstowcs(tchResponseBuffer, cResponseBuffer, nBytesReceived); // Show the MessageBox MessageBox(NULL, tchResponseBuffer, TEXT("Web Output"), MB_OK); // Check to see if this is the end of the HTTP response by // looking for \r\n\r\n if(_tcsstr(tchResponseBuffer, TEXT("\r\n\r\n"))) fBreak = TRUE; // Clear the buffers memset(tchResponseBuffer, 0, 1024); memset(cResponseBuffer, 0, 1024); } closesocket(s); WSACleanup(); Server: Nhận kết nối vào ( Server ) : Sự khác việc truyền liệu luồng kết nối Server Client cách kết nối thiết lập (Client tạo kết nối, cịn Server lắng nghe kết nối) Mặt khác, hai sử dùng phương thức send() recv() để trao đổi liệu hai máy Chúng ta tìm hiểu phía Client, ta bắt đầu tìm hiểu cách tạo ứng dụng theo yêu cầu dịch vụ kết nối vào (hình thành gọi hàm connect()) Điều mà cần làm tạo socket; giống ta làm phía Client cách gọi hàm socket() Sau tạo socket rồi, thay kết nối đến Server, ta để socket trạng thái lắng nghe kết nối vào Để làm việc đó, cần kết buộc (bind) socket tạo với địa cục Để tạo kết buộc ta dùng hàm bind() int bind( SOCKET s, const struct sockaddr *addr, int namelen ) ; Tham số đầu tiên, s, handle socket tạo hàm socket(), ta dùng socket để chở kết nối Tham số addr trỏ, trỏ đến address buffer, xác định giao thức mà ta muốn sử dụng Nếu ta muốn dùng giao thức TCP/IP chuẩn, dùng đệm SOCKADDR_IN Nếu dùng giao thức hồng ngoại sử dùng SOCKADDR_IRDA Tham số cuối cùng, namelen, kích thước cấu trúc địa (address structure) mà tham số addr dùng Nếu khơng có lỗi, hàm bind() trả 0, ngược lại, SOCKET_ERROR xuất Ví dụ sau kết buộc kết nối TCP cổng 80 đến socket cho tất địa IP thiết bị SOCKADDR_IN sListener; memset(&sListener, 0, sizeof(SOCKADDR_IN)); // Set up the port to bind on sListener.sin_family = AF_INET; sListener.sin_port = htons(80); sListener.sin_addr.s_addr = htonl(INADDR_ANY); // Create a TCP socket SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(s == INVALID_SOCKET) return FALSE; // Bind to the socket if(bind(s, (SOCKADDR *)&sListener, sizeof(sListener)) == SOCKET_ERROR) { int iSocketError = WSAGetLastError(); return FALSE; } Chú ý rằng: Ta dùng địa IP INADDR_ANY thay địa IP Adapter Dùng INADDR_ANY làm cho kết buộc socket vào tất địa IP có sẵn thiết bị chúng ta, kết nối vào giao diện chấp nhận socket Khi socket kết buộc vào địa (hoặc nhiều địa chỉ), cần đặt socket chế độ lắng nghe Điều làm cho socket chờ kết nối vào int listen( SOCKET s , int backlog) ; Tham số s socket kết buộc Tham số backlog xác định kích thước hàng đợi cho kết nối vào, thường thiết lập SOMAXCONN (trên Pocket PC giới hạn cho hai kết nối) Hàng đợi backlog dùng lúc có nhiều kết nối vào Khi hàng đợi đầy, tất yêu cầu khác bị từ chối yêu cầu kết nối lấy khỏi hàng đợi hàm accept() Nếu có lỗi hàm listen() trả giá trị SOCKET_ERROR, ngược lại giá trị Cuối để nhận socket kết nối vào, cần gọi hàm accept(), xác định sau SOCKET accept( SOCKET s, struct sockaddr *addr, int * addrlen); Tham số s socket mà đặt chế độ lắng nghe Tham số addr đệm dùng để nhận cấu trúc SOCKADDR_IN SOCKADDR_IRDA tùy thuộc vào giao thức mà socket dùng, chứa thông tin kết nối vào Tham số cuối cùng, addrlen, kích thước cấu trúc addr Chú ý : phương thức accept() không trả giá trị Điều accept() hàm khóa, nghĩa khơng trả giá trị có kết nối từ client socket lắng nghe bị hủy (ta thiết lập tùy chọn socket để đặt chế độ khơng khóa) Khi hàm accept() trả về, giá trị socket handle cho client kết nối vào, lỗi SOCKET_ERROR Tất thông tin client kết nối vào thể socket handle này, socket ban đầu tiếp tục lắng nghe nhiều kết nối khác Ví dụ: Ví dụ sau thể việc Server lắng nghe kết nối vào Client yêu cầu trang Web dùng giao thức HTTP, trả cho Client hồi đáp // Initialize Winsock WSADATA wsaData; memset(&wsaData, 0, sizeof(WSADATA)); if(WSAStartup(MAKEWORD(1,1), &wsaData) != 0) return FALSE; // Create a connection-oriented socket SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Check to see if we have a valid socket if(s == INVALID_SOCKET) { int iSocketError = WSAGetLastError(); return FALSE; } SOCKADDR_IN sListener; memset(&sListener, 0, sizeof(SOCKADDR_IN)); // Setup the port to bind on sListener.sin_family = AF_INET; sListener.sin_port = htons(80); sListener.sin_addr.s_addr = htonl(INADDR_ANY); // Bind to the socket if(bind(s, (SOCKADDR *)&sListener, sizeof(sListener)) == SOCKET_ERROR) { int iSocketError = WSAGetLastError(); return FALSE; } // Listen for incoming connections if(listen(s, SOMAXCONN) == SOCKET_ERROR) { int iSocketError = WSAGetLastError(); return FALSE; } // Wait for a connection SOCKADDR_IN sIncomingAddr; memset(&sIncomingAddr, 0, sizeof(SOCKADDR_IN)); int iAddrLen = sizeof(SOCKADDR_IN); SOCKET sIncomingSocket = accept(s, (SOCKADDR *) &sIncomingAddr, &iAddrLen); if(sIncomingSocket == SOCKET_ERROR) { int iSocketError = WSAGetLastError(); return FALSE; } // We have an incoming socket request char cResponseBuffer[1024] = ""; int nBytesReceived = 0; // Get a basic request In reality, we would want to check // the HTTP request to see if it's valid, but let's just // send a simple response nBytesReceived = recv(sIncomingSocket, &cResponseBuffer[0], 1024, 0); if(nBytesReceived == SOCKET_ERROR) { int iSocketError = WSAGetLastError(); return FALSE; } // Send out a response char cBuffer[1024] = ""; int nBytesSent = 0; int nBytesIndex = 0; // Setup the buffer to send sprintf(cBuffer, &"HTTP/1.0 200 OK\r\n\r\nTest Response\r\n\r\n"); int nBytesLeft = strlen(cBuffer); // Send the entire buffer while(nBytesLeft > 0) { nBytesSent = send(sIncomingSocket, &cBuffer[nBytesIndex], nBytesLeft, 0); if(nBytesSent == SOCKET_ERROR) break; // See how many bytes are left If we still need to send, loop nBytesLeft -= nBytesSent; nBytesIndex += nBytesSent; } // Close the sockets closesocket(sIncomingSocket); closesocket(s); WSACleanup(); ... with the socket closesocket(s); Gửi nhận liệu : Chúng ta thiết lập kết nối đến server, sẵn sàng cho việc gửi nhận liệu hai máy mạng Trên kết nối socket, liệu truyền hai hướng, Server Client dùng... MSG_DONTROUTE Thơng thường, tham số thiết lập 0, MSG_DONTROUTE dùng cho việc kiểm tra định đường cho thông điệp Giá trị trả hàm send() số byte thực gửi mạng SOCKET_ERROR có lỗi việc truyền liệu... trên, tham số s socket mà thiết lập để nhận liệu Tham số thứ hai, buf, đệm để nhận liệu, kích thước xác định tham số len Cuối cùng, tham số flags, thường thiết lập Giá trị trả hàm recv() số byte