为客户端创建套接字
-
声明一个包含sockaddr结构的addrinfo对象,并初始化这些值。对于此应用程序,未指定Internet地址系列,因此可以返回IPv6或IPv4地址。应用程序请求套接字类型为TCP协议的流套接字。
struct addrinfo *result = NULL, *ptr = NULL, hints; ZeroMemory( &hints, sizeof(hints) ); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP;
-
调用getaddrinfo函数,请求在命令行中传递的服务器名称的IP地址。客户端将连接到的服务器上的TCP端口在本示例中由DEFAULT_PORT定义为27015。该的getaddrinfo函数返回其值是错误检查的整数。
#define DEFAULT_PORT "27015" // Resolve the server address and port iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result); if (iResult != 0) { cout << "getaddrinfo failed:" << iResult << endl; WSACleanup(); return 1; }
-
创建一个名为ConnectSocket的SOCKET对象。
SOCKET ConnectSocket = INVALID_SOCKET;
-
调用套接字函数,并将其值返回给ConnectSocket变量。对于此应用程序,请使用调用返回的第一个IP地址访问getaddrinfo,该地址与hints参数中指定的地址族,套接字类型和协议相匹配。在此示例中,使用套接字类型SOCK_STREAM和协议IPPROTO_TCP指定了TCP流套接字。地址族未指定(AF_UNSPEC),因此返回的IP地址可以是服务器的IPv6或IPv4地址。
如果客户端应用程序只想使用IPv6或IPv4进行连接,则需要在hints参数中将地址族设置为针对IPv6的AF_INET6或针对IPv4的AF_INET 。
// Attempt to connect to the first address returned by // the call to getaddrinfo ptr=result; // Create a SOCKET for connecting to server ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
-
检查是否有错误,以确保该套接字是有效的套接字。
if (ConnectSocket == INVALID_SOCKET) { cout << "Error at socket():" << WSAGetLastError() << endl; freeaddrinfo(result); WSACleanup(); return 1; }
可以为不同的实现更改传递给套接字函数的参数。
错误检测是成功的网络代码的关键部分。如果套接字调用失败,则返回INVALID_SOCKET。先前代码中的if语句用于捕获创建套接字时可能发生的任何错误。WSAGetLastError返回与最近发生的错误关联的错误号。
连接到套接字
为了使客户端能够在网络上进行通信,它必须连接到服务器。
调用connect函数,将创建的套接字和sockaddr结构作为参数传递。检查一般错误。
// Connect to server.
iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
// Should really try the next address returned by getaddrinfo
// if the connect call failed
// But for this simple example we just free the resources
// returned by getaddrinfo and print an error message
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET) {
cout << "Unable to connect to server!" << endl;
WSACleanup();
return 1;
}
所述的getaddrinfo函数被用来确定在该值的sockaddr结构。在此示例中,由getaddrinfo函数返回的第一个IP地址用于指定传递给connect的sockaddr结构。如果connect调用失败到第一个IP地址,请尝试从getaddrinfo函数返回的链表中的下一个addrinfo结构。
sockaddr结构中指定的信息包括:
- 客户端将尝试连接的服务器的IP地址。
- 客户端将连接到的服务器上的端口号。客户端调用getaddrinfo函数时,此端口被指定为端口27015 。
在客户端上发送和接收数据
下面的代码演示了建立连接后客户端使用的send和recv函数。
#define DEFAULT_BUFLEN 512
int recvbuflen = DEFAULT_BUFLEN;
const char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
int iResult;
// Send an initial buffer
iResult = send(ConnectSocket, sendbuf, (int) strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
cout << "send failed:" << WSAGetLastError() << endl;
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
cout << "Bytes Sent:" << iResult << endl;
// shutdown the connection for sending since no more data will be sent
// the client can still use the ConnectSocket for receiving data
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
cout << "shutdown failed: " << WSAGetLastError() << endl;
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// Receive data until the server closes the connection
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
cout<<"Bytes received:"<< iResult<<endl;
else if (iResult == 0)
cout << "Connection closed" << endl;
else
cout << "recv failed:" << WSAGetLastError() << endl;
} while (iResult > 0);
的发送和recv的功能都返回分别发送或接收的,字节数,或错误的一个整数值。每个函数还采用相同的参数:活动套接字,char缓冲区,要发送或接收的字节数以及要使用的任何标志。
断开客户端连接
客户端完成发送和接收数据后,客户端将与服务器断开连接并关闭套接字。
-
当客户端完成向服务器的数据发送后,可以通过指定SD_SEND调用关闭函数来关闭套接字的发送端。这允许服务器释放该套接字的一些资源。客户端应用程序仍可以在套接字上接收数据。
// shutdown the send half of the connection since no more data will be sent iResult = shutdown(ConnectSocket, SD_SEND); if (iResult == SOCKET_ERROR) { cout << "shutdown failed:" << WSAGetLastError()<< endl; closesocket(ConnectSocket); WSACleanup(); return 1; }
-
客户端应用程序完成数据接收后,将调用closesocket函数关闭套接字。
使用Windows套接字DLL完成客户端应用程序后,将调用WSACleanup函数以释放资源。
// cleanup closesocket(ConnectSocket); WSACleanup(); return 0;
示例
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
int __cdecl main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
const char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
int iResult;
int recvbuflen = DEFAULT_BUFLEN;
// Validate the parameters
if (argc != 2) {
cout<<"usage:"<<argv[0]<<" server-name"<<endl;
return 1;
}
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port
iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
if ( iResult != 0 ) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
// Attempt to connect to an address until one succeeds
for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {
// Create a SOCKET for connecting to server
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
// Connect to server.
iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
// Send an initial buffer
iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// shutdown the connection since no more data will be sent
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// 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 with error: %d\n", WSAGetLastError());
} while( iResult > 0 );
// cleanup
closesocket(ConnectSocket);
WSACleanup();
return 0;
}