编程思路
第一步
创建一个WASDATA
结构体变量,用于存储关于Winsock
库的信息;初始化Winsock
库。
第二步
创建TCP套接字。
第三步
创建sockaddr_in
结构体变量,用于储存服务器地址信息。里面包括设置地址族、IP地址、端口号。
第四步
调用connect
函数连接服务器。
通信
调send
函数发送数据
调recv
函数接收数据
实现代码
头文件部分
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib") //告诉编译器链接Winsock库
main 函数部分
int main()
{
WSADATA wsaData; //创建一个结构体变量,用于存储关于Winsock库的信息
int result = WSAStartup(MAKEWORD(2, 2), &wsaData); //初始化Winsock库,指定版本号2.2,检查返回值
if (result != 0)
{
std::cout << "WSAStartup failed: " << result << std::endl; //输出错误信息并退出程序
return 1;
}
SOCKET connectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //创建一个TCP套接字,检查返回值
if (connectSocket == INVALID_SOCKET)
{
std::cout << "socket failed with error: " << WSAGetLastError() << std::endl; //输出错误信息并退出程序
WSACleanup(); //清除Winsock库
return 1;
}
sockaddr_in service; //创建一个结构体变量,用于存储服务器地址信息
service.sin_family = AF_INET; //指定地址族为IPv4
inet_pton(AF_INET, "127.0.0.1", &service.sin_addr); //将字符串类型的IP地址转换为二进制网络字节序的IP地址,并存储在结构体中
service.sin_port = htons(6666); //将端口号从主机字节序转换为网络字节序,并存储在结构体中
result = connect(connectSocket, (SOCKADDR*)&service, sizeof(service)); //连接到服务器,检查返回值
if (result == SOCKET_ERROR)
{
std::cout << "connect failed with error: " << WSAGetLastError() << std::endl; //输出错误信息并退出程序
closesocket(connectSocket); //关闭套接字
WSACleanup(); //清除Winsock库
return 1;
}
std::cout << "Connected to server." << std::endl; //连接成功,输出消息
char sendBuffer[1024] = "Hello, server!"; //创建发送缓冲区,存储待发送的数据
result = send(connectSocket, sendBuffer, sizeof(sendBuffer), 0); //向服务器发送数据,检查返回值
if (result == SOCKET_ERROR)
{
std::cout << "send failed with error: " << WSAGetLastError() << std::endl; //输出错误信息并退出程序
closesocket(connectSocket); //关闭套接字
WSACleanup(); //清除Winsock库
return 1;
}
char recvBuffer[1024]; //创建接收缓冲区,用于存储从服务器接收到的数据
result = recv(connectSocket, recvBuffer, sizeof(recvBuffer), 0); //从服务器接收数据,检查返回值
if (result == SOCKET_ERROR)
{
std::cout << "recv failed with error: " << WSAGetLastError() << std::endl; //输出错误信息并退出程序
closesocket(connectSocket); //关闭套接字
WSACleanup(); //清除Winsock库
return 1;
}
std::cout << "Received message from server: " << recvBuffer << std::endl; //输出从服务器收到的数据
closesocket(connectSocket); //关闭套接字
WSACleanup(); //清除Winsock库
return 0;
}
注意事项(debug过程)
运行代码之前要使用网络调试助手打开TCP服务端,注意端口号、IP地址这些要匹配。
代码主要是C语言,C++部分是控制台输入输出的,如果纯C语言就include<stdio.h>,用printf
、scanf
这些库函数替换就好了。
以上代码使用VS的编译器上是可以直接运行的,但是如果是使用MinGW gcc/g++就会出现问题。我们来看报错。
1.inet_pton
函数未定义。
inet_pton函数将点分十进制串转换成网络字节序二进制值,此函数对IPv4地址和IPv6地址都能处理,在Windows下只需包含ws2tcpip.h
头文件就行了
根据网上的说法,是因为Windows gcc 默认的 _WIN32_WINNT
是 502 Windows Server 2003,所以解决方案是重新定义_WIN32_WINNT
,在ws2tcpip.h
前加入
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
还有一个方法,那就是。。。不用inet_pton()
函数了,用别的IP地址转换函数——inet_addr()
。
这样第三步的代码改为
sockaddr_in service; //创建一个结构体变量,用于存储服务器地址信息
service.sin_family = AF_INET; //指定地址族为IPv4
service.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //将点分十进制转化32位unsigned int
service.sin_port = htons(6666); //将端口号从主机字节序转换为网络字节序,并存储在结构体中
这个问题就解决了。
2. 链接动态库问题undefined reference to __imp_WSAStartup
在vs中直接加入
#pragma comment(lib, "ws2_32.lib")
就可以链接winsock动态库,不知道为什么gcc编译器不行。1 问题解决后,报如下错误:
所以在vscode的配置tasks.json的args
参数加一个:"-lwsock32"
,在编译命令中指定链接就可以了。
对于如果找不到_imp_inet_pton
的话,就需要再加args
参数:"-lws2_32"
。
然后就能连上TCP服务器了。
标签:WINNT,函数,Windows,C++,WIN32,tcp,Winsock,inet From: https://www.cnblogs.com/gofan-SiTu/p/18069624