- vừa được xem lúc

[MFC] Http request with winsock2.h

0 0 22

Người đăng: Hoang Van Hoa

Theo Viblo Asia

Giới thiệu

Xin chào, trong bài này mình sẽ giới thiệu 1 số lưu ý khi sử dụng thư viện winsock2.h (thư viện window socket) sử dụng trong window app.

Cơ bản

Đầu tiên, bạn sẽ dễ dàng search được 1 ví dụ cụ thể trên document của winsock2.

#ifndef UNICODE
#define UNICODE
#endif #define WIN32_LEAN_AND_MEAN #include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h> // Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib") int wmain()
{ //---------------------- // Initialize Winsock WSADATA wsaData; int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != NO_ERROR) { wprintf(L"WSAStartup function failed with error: %d\n", iResult); return 1; } //---------------------- // Create a SOCKET for connecting to server SOCKET ConnectSocket; ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (ConnectSocket == INVALID_SOCKET) { wprintf(L"socket function failed with error: %ld\n", WSAGetLastError()); WSACleanup(); return 1; } //---------------------- // The sockaddr_in structure specifies the address family, // IP address, and port of the server to be connected to. sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr("127.0.0.1"); clientService.sin_port = htons(27015); //---------------------- // Connect to server. iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService)); if (iResult == SOCKET_ERROR) { wprintf(L"connect function failed with error: %ld\n", WSAGetLastError()); iResult = closesocket(ConnectSocket); if (iResult == SOCKET_ERROR) wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError()); WSACleanup(); return 1; } wprintf(L"Connected to server.\n"); iResult = closesocket(ConnectSocket); if (iResult == SOCKET_ERROR) { wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError()); WSACleanup(); return 1; } WSACleanup(); return 0;
}

Ví dụ này khá cụ thể và chi tiết, tuy nhiên, mình có 1 số lưu ý khi sử dụng example này như bên dưới.

WSAStartup

int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

Đây là hàm bắt buộc được gọi khi muốn thao tác với window socket bằn 1 ứng dụng hoặc DLL. Ngoài việc khới tạo môi trường win socket, lệnh này còn chỉ định phiên bản winsock được sử dụng cho app. Phổ biến nhất, ta sử dụng phiên bản 2.2 cho hầu hết các ứng dụng, vì các phiên bản trước đã quá cũ rồi, ai cần dùng đâu. Chẳng hạn: win socket phiên bản 1.1 dành cho window 95 và window NT 3.51, toàn phiên bản từ đời nào rồi ?.

WSACleanup()

int WSACleanup();

Tất nhiên, có khởi tạo phải có kết thúc. Hàm WSACleanup sẽ làm điều này. Ở ví dụ trên, mỗi khi gặp lỗi ta sẽ Cleanup ngay trong đó rồi return. Tuy nhiên, làm cách này rất dễ khiến ta bị quên hoặc nhầm lẫn khi gọi. Gọi Cleanup khi socket chưa được close sẽ sinh ra lỗi WSA_OPERATION_ABORTED. Để khắc phục điều này, ta sử dụng:

int atexit( void (__cdecl *func )( void )
);

Bằng cách sử dụng atexit((void (*)(void))(WSACleanup)) Cleanup sẽ luôn được gọi khi kết thúc hàm. Quá tiện lợi. ✌️

inet_addr();

unsigned long inet_addr( const char *cp
);

Hàm convert ip từ char* về long. Tuy nhiên hàm này đã không còn được sử dụng nữa. Nếu bạn sử dụng phiên bản VS thấp hơn, warning sẽ xuất hiện. Ở phiên bản mới hơn, ta sử dụng inet_pton thay thế:

INT WSAAPI inet_pton( INT Family, PCSTR pszAddrString, PVOID pAddrBuf
);

Inet_pton hoặc InetPton được ưu tiên sử dụng hơn vì sự rõ ràng trong param truyền vào và giá trị trả về. Tuy nhiên, hàm này lại nằm ở thư viện ws2tcpip.h nên hãy nhớ include thư viện này nhé. Ngoài ra, có thể sử dụng InetPton để check valid ip. Cụ thể, với ip không đúng chuẩn format, hàm này sẽ return 0, không có lỗi gì sẽ return 1(hơi lạ chút). Vậy hãy sử dụng hàm này để không phải viết đoạn code check valid ip theo text dài loằng ngoằng.

WSAGetLastError()

WSAGetLastError() là công cụ hữu hiệu nhất để check error khi thao tác với winsocket. Bất cứ khi nào có lỗi xảy ra, hảm này sẽ trả về lỗi trong list: Windows Sockets Error Codes. Ngoài ra, ta có thể tự set lỗi thông qua hàm WSASetLastError. Việc set lỗi này sẽ có ý nghĩa trong 1 số trường hợp nhất định, set timeout cho server chẳng hạn.

Send() và Receiv()

Tiến trình gửi và nhận data khi đã connect thành công.

 iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 ); if (iResult == SOCKET_ERROR) { wprintf(L"send failed with error: %d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; }

Hàm send sẽ gửi lên server 1 gói tin bao gồm ip, port, path, param, ... tương đương với đường dẫn. Trong package này còn chưa header và HTTP version. Về http header, nếu không có option gì đặc biệt cho server khi request, giá trị này sẽ để trống. tham khảo HTTP headers Về http version, có 2 phiên bản là HTTP 1.0 và HTTP 1.1. Tuy nhiên dùng HTTP 1.1 sẽ nhanh hơn, nên là khuyến cáo dùng HTTP 1.1 nếu server hỗ trợ. (Check server hỗ trợ HTTP 1.1 bằng cách check header response data)

Note: luôn set giá trị buffer length với chiều dài xác định đúng bằng chiều dài package. Việc sai khác giá trị chiều dài bufffer với giá trị thực tế của buffer dẫn tới lỗi 400 Bad Request từ phía server. Có thể shutdown send socket ngay sau khi send thành công.

int recv( SOCKET s, char *buf, int len, int flags
);

Vì mỗi lần nhận data từ server, ta chỉ nhận được số byte cố định, nên ta dùng vòng lặp để lấy triệt để data từ server(cơ bản quá mà):

 // Receive until the peer closes the connection do { iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); if ( iResult > 0 ) printf("Bytes received: %d\n", iResult); else if ( iResult == 0 ) printf("Connection closed\n"); else printf("recv failed: %d\n", WSAGetLastError()); } while( iResult > 0 );

closesocket() và shutdown

Nhìn tên thì có vẻ 2 hàm này dễ gây nhầm lẫn, nhưng khi nhìn vào code thì lại khác.

int closesocket( SOCKET s
);
int shutdown( SOCKET s, int how
);

Closesocket sẽ đóng hoàn toàn socket, tức là ta sẽ không thế sử dụng socket này nữa. Trong khi đó, với shutdown, ta có thể khóa chiều send, chiều receiv hoặc cả 2, Khuyến khích dùng shutdown trước khi closesocket.

Tổng kết

Trên đây là 1 số phần mình rút ra được khi làm việc với winsock2.h và http request. Mong sẽ giúp các bạn tiết kiệm được chút ít thời gian tra google. Happy coding

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học lập trình game cần những gì?

Lập trình game là làm gì. Các ngôn ngữ các bạn có thể sử dụng để lập trình game : C, C++, C#, Java, Python,... Các bước cơ bản để lập trình game. . Hiển thị: Đã là game thì hiển thị không thể thiếu, lúc đầu các bạn chỉ làm cho phần hiển thị thật đơn giản, các bạn đừng quá chú tâm vào việc làm sao ch

0 0 32

- vừa được xem lúc

Design parttern

Builder. 1. Ý tưởng. Builder là một mẫu thiết kế sáng tạo cho phép bạn xây dựng các đối tượng phức tạp theo từng bước.

0 0 22

- vừa được xem lúc

Kỹ thuật viết mã nguồn hiệu quả

Kỹ thuật viết mã nguồn hiệu quả? Hôm nay bài viết này mình không đề cập tới thuật toán, hãy coi như rằng chúng ta đã có thuật toán tốt nhất có thể và bây giờ chúng ta phải làm gì để có thể tăng tính hiệu quả của code. Bài viết này mình sẽ lấy ngôn ngữ lập trình C/C++ để minh họa về các hàm, các thao

0 0 23

- vừa được xem lúc

Singleton Design pattern

Singleton Design pattern. 1. Vấn đề. - Ý tưởng:.

0 0 21

- vừa được xem lúc

So sánh Python và C++

Cuộc tranh luận giữa Python và C ++ là một chủ đề hấp dẫn vì cả hai ngôn ngữ lập trình đều rất khác nhau về cú pháp, tính đơn giản, cách sử dụng và cách tiếp cận tổng thể để lập trình. Vì vậy, mọi người cảm thấy khó khăn khi lựa chọn ngôn ngữ lập trình nào để học.

0 0 26

- vừa được xem lúc

Thiết kế file cấu trúc với OOP C++: Chương 1 giới thiệu

Lập trình thường ngày các bạn dùng gì để lưu trữ dữ liệu - mình thường dùng database - sqlite, postgresql, ms sql server. Nhưng chúng đều là blackbox, mình chẳng hiểu gì cách chúng hoạt động như thế nào? Lưu trữ dữ liệu ở đâu, làm sao để tạo mục lục? ... Mình muốn tìm hiểu sâu hơn, làm sao để tạo ra

0 0 25