// testWinSock.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif//如果需要包含windows.h头文件,则前面应该有#define WIN32_LEAN_AND_MEAN,以防止Winsock1.1与Winsock2冲突
#include <Windows.h>
#include <WinSock2.h>
//#include <IPHlpApi.h>//使用Ip Helper API时包含,且应当包含在Winsock2.h之后
#include <WS2tcpip.h>
#include <cstdio>
#include <cstring>
#include <thread>
#pragma comment(lib,"Ws2_32.lib")
#define DEFAULT_PORT "27015"//定义默认端口号
#define DEFAULT_BUFLEN 512//定义默认的数据长度
int client(PCSTR pNodeName)
{
int iResult;
struct addrinfo *result = NULL, *ptr = NULL, hints;
//1.初始化Winsock
WSADATA wsaData;//定义一个WsaData对象
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);//初始化WS2_32.dll
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
//2.为客户端创建一个Socket
//定义包含sockaddr结构体的addrinfo对象并初始化
ZeroMemory(&hints, sizeof(hints));//零值初始化
hints.ai_family = AF_UNSPEC;//地址族,这里选择非特定类型,可以接受IPv4和IPv6
hints.ai_socktype = SOCK_STREAM;//套接字类型,主要有流套接字、数据报套接字和源套接字
hints.ai_protocol = IPPROTO_TCP;//协议,这里选择TCP协议
//调用getaddrinfo函数获得给定服务器名对应的IP地址
iResult = getaddrinfo(pNodeName, DEFAULT_PORT, &hints,&result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();//中止WS2_32.dll的使用
return 1;
}
//创建一个Socket对象
SOCKET ConnectSocket = INVALID_SOCKET;
//调用socket函数,检查返回的socket是否有效
ptr = result;
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {//如果socket调用失败,将返回INVALID_SOCKET
printf("Error at socket(): %ld\n", WSAGetLastError());//调用WSAGetLastError返回最近一次的错误类型
freeaddrinfo(result);//释放地址信息句柄
WSACleanup();//中止WS2_32.dll的使用
return 1;
}
//3.连接到一个套接字
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}//实际情况下如果连接失败应该尝试getaddrinfo返回的链表中下一个ip地址,在这里我们简单释放地址信息句柄
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!\n");
WSACleanup();//中止WS2_32.dll的使用
return 1;
}
//4.发送请求和接收响应
int recvbuflen = DEFAULT_BUFLEN;
const char * sendbuf = "hello world!";
char recvbuf[DEFAULT_BUFLEN];
//发送一个字符串
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {//如果发送失败立即结束
printf("send failed: %d\n", WSAGetLastError());//输出错误信息
closesocket(ConnectSocket);//关闭套接字
WSACleanup();//中止WS2_32.dll的使用
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
//当发送完所有数据后可以半关闭连接,客户端仍然可以使用该连接接收数据
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
//接收数据直到服务端关闭连接
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);
//5.断开连接
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
int server() {
int iResult,iSendResult;
struct addrinfo *result = NULL, *ptr = NULL, hints;
//1.初始化Winsock
WSADATA wsaData;//定义一个WsaData对象
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);//初始化WS2_32.dll
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
//2.为服务端创建一个套接字
ZeroMemory(&hints, sizeof(hints));//零值初始化
hints.ai_family = AF_INET;//协议族,AF_INET用于指定ipv4
hints.ai_socktype = SOCK_STREAM;//套接字类型,主要有流套接字、数据报套接字和源套接字
hints.ai_protocol = IPPROTO_TCP;//协议,这里选择TCP协议
hints.ai_flags = AI_PASSIVE;//AI_PASSIVE说明调用者意图在调用bind函数过程中使用getaddrinfo返回的
//套接字地址结构体。当AI_PASSIVE被设置并且getaddrinfo的nodename参数
//被传入NULL指针,那么套接字地址结构体的ip地址部分将被设为INADDR_ANY或IN6ADDR_ANY
//解析服务端所使用的ip地址和端口号
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();//中止WS2_32.dll的使用
return 1;
}
//创建一个套接字对象
SOCKET ListenSocket = INVALID_SOCKET;
//为服务端创建一个用于监听客户端请求的套接字
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());//错误类型
freeaddrinfo(result);//释放地址信息句柄
WSACleanup();//中止WS2_32的使用
return 1;
}
//3.绑定一个套接字
//建立TCP连接套接字
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());//错误类型
freeaddrinfo(result);//释放地址信息句柄
closesocket(ListenSocket);//关闭监听套接字
WSACleanup();//中止WS2_32的使用
return 1;
}
freeaddrinfo(result);//不再需要地址信息,释放地址信息句柄
//4.在一个套接字上监听
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
printf("Listen failed with error: %ld\n", WSAGetLastError());//错误类型
closesocket(ListenSocket);//关闭监听套接字
WSACleanup();//中止WS2_32的使用
return 1;
}
//5.接受一个连接
//一般情况下采用多线程为多个用户服务,如果一个连接请求出现,
//服务端将调用accept、AcceptEx或WSAAccept函数并把任务转交给
//下一个线程处理。这里我们仅仅为一个用户服务。
//创建一个客户套接字
SOCKET ClientSocket = INVALID_SOCKET;
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed: %d\n", WSAGetLastError());//错误类型
closesocket(ListenSocket);//关闭监听套接字
WSACleanup();//中止WS2_32的使用
return 1;
}
//当客户连接建立后,一般会将客户连接交给工作线程或IO端口继续进行附加的连接,此处从简
//6.接收并发送数据
char recvbuf[DEFAULT_BUFLEN];//接收的字符串
int recvbuflen = DEFAULT_BUFLEN;//接受字符串的最大长度
do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {//接收成功
printf("Bytes received: %d\n", iResult);
iSendResult = send(ClientSocket, recvbuf, iResult, 0);//把接收的字符串再传回去
if (iSendResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);//关闭套接字
WSACleanup();//中止WS2_32.dll的使用
return 1;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)//连接关闭
printf("Connection closing...\n");
else {//接收失败
printf("recv failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);//关闭套接字
WSACleanup();//中止WS2_32.dll的使用
return 1;
}
} while (iResult > 0);
//7.断开服务器连接
//如果不再发送数据可以半关闭客户套接字
/*iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}*/
// cleanup
closesocket(ClientSocket);//关闭套接字
WSACleanup();//中止WS2_32的使用
return 0;
}
int main() {
using namespace std;
thread t1([]() {server(); });
thread t2([]() {client("127.0.0.1"); });
t1.join();
t2.join();
return 0;
}
winsock 基本流程
最新推荐文章于 2023-04-01 20:49:00 发布