初始环境:cmake stl23
环境配置:
add_library(WS2 SHARED IMPORTED)
set_target_properties(WS2 PROPERTIES
IMPORTED_IMPLIB "C:\\Windows\\System32\\ws2_32.dll"
)
target_link_libraries(winsocket WS2)
file(COPY "C:\\Windows\\System32\\ws2_32.dll" DESTINATION ${CMAKE_BINARY_DIR})
//模板
add_library(库名 SHARED IMPORTED)
set_target_properties(库名 PROPERTIES
IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/external/DLL文件名.dll
IMPORTED_IMPLIB ${CMAKE_SOURCE_DIR}/external/LIB文件名.lib
)
target_link_libraries(程序名 库名)
初始化:
WSADATA wsaData;
int iResult;
//initialize Winsock
iResult= WSAStartup(MAKEWORD(2,2),&wsaData);
if(iResult!=0){
std::cout<<"init failed: "<<iResult;
return 1;
}
WSADATA:包含有关 Windows 套接字实现的信息
WSAStartup:通过进程启动对 Winsock DLL 的使用,第一个参数是unsigned short的值,但是低位和高位指定版本,MAKEWORD辅助创建
[out] lpWSAData:
指向 WSADATA 数据结构的指针,该结构用于接收 Windows 套接字实现的详细信息。
失败返回-1
客户端:
创建套接字
1.声明包含 sockaddr 结构的 addrinfo 对象并初始化这些值。 对于此应用程序,未指定 Internet 地址系列,因此可以返回 IPv6 或 IPv4 地址。 应用程序将套接字类型请求为 TCP 协议的流套接字。
struct addrinfo
*result= nullptr,
*ptr = nullptr,
hints{};
ZeroMemory(&hints, sizeof(hints));
hints.ai_family=AF_INET;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;
socketaddr:关于地址信息的结构体
addrinfo:getaddrinfo 函数使用 addrinfo 结构来保存主机地址信息
ai_family:当前支持的值 是 AF_INET 或 AF_INET6,它们是 IPv4 和 IPv6 的 Internet 地址系列格式。
ai_socktype:
ai_portocal:协议类型:
std::string defaultPort="27015";
std::string nodeName="127.0.0.1";
// Resolve the server address and port
iResult= getaddrinfo(nodeName.c_str(),defaultPort.c_str(),&hints,&result);
if (iResult!=0){
std::cout<<"getaddrinfo failed: "<<iResult;
WSACleanup();
return 1;
}
getaddrinfo:addrinfo 结构来保存主机地址信息,result是一个结构体链表
SOCKET ConnectSocket =INVALID_SOCKET;
ptr=result;
ConnectSocket= socket(ptr->ai_family,ptr->ai_socktype,ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
std::cout<<"socket error: "<<WSAGetLastError();
freeaddrinfo(result);
WSACleanup();
return 1;
}
SOCKET:套接字的描述符
socket():函数创建绑定到特定传输服务提供程序的套接字
freeaddrinfo:函数释放 getaddrinfo 函数在 addrinfo 结构中动态分配的地址信息。
WSACleanup:函数终止使用 Winsock 2 DLL (Ws2_32.dll)
总结:到目前为止,只从系统获取了socket相关信息。
连接到套接字
// Connect to server.
iResult= connect(ConnectSocket,ptr->ai_addr,static_cast<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){
std::cout<<"ubanle to connect Server\n";
WSACleanup();
return 1;
}
connect:与指定的套接字建立连接;
freeaddrinfo:释放内存;
tcp三次握手在此完成。
发送&接受
// Send an initial buffer
const int receiveBufLen=512;
char receiveBBuf[receiveBufLen];
std::string sendBuf{"this is a text"};
iResult= send(ConnectSocket,sendBuf.c_str(),static_cast<int>(sendBuf.length()),0);
if (iResult==SOCKET_ERROR){
std::cout<<"send error "<<WSAGetLastError();
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
std::cout<<"send success: "<<iResult<<'\n';
// 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, receiveBBuf, receiveBufLen, 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);
std::cout<<"receive success\n";
send:函数在连接的套接字上发送数据
recv:从连接的套接字或绑定的无连接套接字接收数据。
如果how参数为 SD_RECEIVE, 则将不允许对套接字上的recv函数进行后续调用。这对较低的协议层没有影响。对于 TCP 套接字,如果套接字上仍有数据排队等待接收,或者随后有数据到达,则连接将重置,因为无法将数据传送给用户。对于 UDP 套接字,将接受传入的数据报并将其排队。在任何情况下都不会生成 ICMP 错误数据包。
如果how参数为 SD_SEND,则不允许后续调用 send函数。对于 TCP 套接字,所有数据发送完毕并由接收方确认后,将发送 FIN。
设置如何SD_BOTH 将禁用发送和接收,如上所述。
关闭
// cleanup
closesocket(ConnectSocket);
WSACleanup();
完整代码:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
int main(int argc,char **argv) {
WSADATA wsaData;
int iResult;
//initialize Winsock
iResult= WSAStartup(MAKEWORD(2,2),&wsaData);
if(iResult!=0){
std::cout<<"init failed: "<<iResult;
return 1;
}
std::cout<<"start success\n";
struct addrinfo
*result= nullptr,
*ptr = nullptr,
hints{};
ZeroMemory(&hints, sizeof(hints));
hints.ai_family=AF_INET;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;
std::cout<<"hints success\n";
std::string defaultPort="27015";
std::string nodeName="127.0.0.1";
// Resolve the server address and port
iResult= getaddrinfo(nodeName.c_str(),defaultPort.c_str(),&hints,&result);
if (iResult!=0){
std::cout<<"getaddrinfo failed: "<<iResult;
WSACleanup();
return 1;
}
std::cout<<"get success\n";
SOCKET ConnectSocket =INVALID_SOCKET;
for(ptr=result;ptr!= nullptr;ptr=ptr->ai_next){
ConnectSocket= socket(ptr->ai_family,ptr->ai_socktype,ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
std::cout<<"socket error: "<<WSAGetLastError();
freeaddrinfo(result);
WSACleanup();
return 1;
}
std::cout<<"create success\n";
// Connect to server.
iResult= connect(ConnectSocket,ptr->ai_addr,static_cast<int>(ptr->ai_addrlen));
if (iResult==SOCKET_ERROR){
closesocket(ConnectSocket);
ConnectSocket=INVALID_SOCKET;
continue;
}
break;
}
// 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){
std::cout<<"ubanle to connect Server\n";
WSACleanup();
return 1;
}
std::cout<<"connect success\n";
// Send an initial buffer
const int receiveBufLen=512;
char receiveBBuf[receiveBufLen];
std::string sendBuf{"this is a text"};
iResult= send(ConnectSocket,sendBuf.c_str(),static_cast<int>(sendBuf.length()),0);
if (iResult==SOCKET_ERROR){
std::cout<<"send error "<<WSAGetLastError();
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
std::cout<<"send success: "<<iResult<<'\n';
// 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, receiveBBuf, receiveBufLen, 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);
std::cout<<"receive success\n";
// cleanup
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
评论区