4 客户端
客户端
1 为客户端创建一个socket
1.1 声明一个包含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;
1.2 调用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) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
1.3 创建一个名为ConnectSocket的SOCKET对象。
SOCKET ConnectSocket = INVALID_SOCKET;
1.4 调用套接字函数,并将其值返回给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);
1.5 检查错误以确保该套接字是有效的套接字。
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
可以为不同的实现更改传递给套接字函数的参数。
错误检测是成功的网络代码的关键部分。 如果套接字调用失败,则返回INVALID_SOCKET。 先前代码中的if语句用于捕获创建套接字时可能发生的任何错误。 WSAGetLastError返回与最近发生的错误关联的错误号。
2 连接socket
调用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) {
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
getaddrinfo函数用于确定sockaddr结构中的值。
在此示例中,getaddrinfo函数返回的第一个IP地址用于指定传递给连接的sockaddr结构。
如果connect调用失败到第一个IP地址,请尝试从getaddrinfo函数返回的链表中的下一个addrinfo结构。
sockaddr结构中指定的信息包括:
客户端将尝试连接的服务器的IP地址。
客户端将连接到的服务器上的端口号。 当客户端调用getaddrinfo函数时,此端口被指定为端口27015。
3 在客户端上发送和接收数据
下面的代码演示了建立连接后客户端使用的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) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// 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) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// Receive data until the server 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);
send和recv函数都分别返回发送或接收的字节数的整数值,或者返回错误。
每个函数还采用相同的参数:活动套接字,char缓冲区,要发送或接收的字节数以及要使用的任何标志。
4 断开客户端连接
客户端完成发送和接收数据后,客户端将与服务器断开连接并关闭套接字。
4.1 shutdown
当客户端完成向服务器的数据发送后,可以通过指定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) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
4.2 closesocket
客户端应用程序完成数据接收后,将调用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) {
printf("usage: %s server-name\n", argv[0]);
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;
}