Win32 Socket的使用
在微信公众号上看到了一篇文章,其中的内容是有关FTP传输文件的,文章的地址找不到了,但是是大佬写的文章。公众号:涛哥依旧。
下面来记录一下学习过程:
讲到Socket就不得不提网络通信模型,就是客户端–服务器模型。服务器等待客户端来连接。最初学习网络通信是在《VC++详解 第三本》这本书里面,有兴趣的大家可以都可以看看。
其中分别讲述了客户端–服务器模式的流程:
一、基于TCP(面向连接)服务器端Socket编程
1.基于TCP(面向连接)服务器端Socket编程的流程:
(1)创建一个套接字(socket).
(2)将套接字绑定到一个本地地址和端口上(bind).
(3)将套接字设为监听模式,准备接受客户端的请求(listen)
(4)等待客户请求的到来(accept).当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字。
(5)用返回的套接字和客户端进行通信(send/recv).
(6)返回或者等待另一客户请求。
(7)关闭套接字。
2.基于TCP(面向连接)客户端Socket编程的流程:
(1)创建一个套接字(socket).
(2)向服务器端发送连接请求(connect).
(3)和服务器进行通信(send/recv)
(4) 关闭套接字。
二、基于UDP(面向无连接)服务器端Socket编程:
3.基于UDP(面向无连接)服务器端Socket编程的流程:
(1)创建一个套接字(socket).
(2)将套接字绑定到一个本地地址和端口上(bind).
(3)准备接受数据(recvfrom)
(4)关闭套接字
4.基于UDP(面向无连接)客户端Socket编程的流程:
(1)创建一个套接字(socket).
(2)向服务器发送数据(sendto).
(3)关闭套接字。
下面我们以TCP的方式来构建一个实例程序:
TCP服务端程序:
#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#include "Ws2tcpip.h"
#include <string>
#include <fstream>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
// tcp连接部分
WORD wVersionRequested; // 用来指定准备要加载到套接字库的版本
WSADATA wsaData; // 库版本的有关信息
wVersionRequested = MAKEWORD(1,1);
// WSAStartup():①加载套接字库②进行套接字版本的协商,确定要使用的套接字的版本
WSAStartup(wVersionRequested, &wsaData); // 加载套接字库
// socket:创建套接字
// AF_INET:指定地址族;SOCK_STREAM:指定Socket的类型为流式套接字;Protocol:指定与地址族相关的协议
unsigned int sockSrv = socket(AF_INET,SOCK_STREAM,0); // 创建套接字
SOCKADDR_IN addrSrv; // 指定了该套接字本地地址信息
// 表示地址族,对于IP地址,将一直是AF_INET
addrSrv.sin_family = AF_INET;
// 套接字的主机IP地址
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
// htons:将u_short值从主机字节顺序转化为TCP/IP网络字节顺序 // 指定要分配给套接字的端口号
addrSrv.sin_port = htons(8888);
// 将Socket与本地的IP地址和端口号绑定
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
// listen():将指定的套接字设置为监听模式
// backlog:等待连接队列的最大长度
listen(sockSrv,5);
SOCKADDR_IN addrClient;
int len = sizeof(SOCKADDR);
// accept():接受客户端发送的连接请求
// sockSrv:当前监听的Socket; addrClient:连接的客户端的IP地址和端口号
unsigned int SockConn = accept(sockSrv, (SOCKADDR*)&addrClient,&len);
// 接收ftp client发送的文件的文件长度
char sizeFileStr[20] = {0};
// 从一个已连接的套接字中接收数据
recv(SockConn, sizeFileStr, sizeof(sizeFileStr)-1,0);
int fileSize = atoi(sizeFileStr);
// 接收ftp client发送的文件的并保存
char recvBuf[1024] = {0};
int recvTotalSize = 0;
char* buffer;
char absolutePath[MAX_PATH];
memset(absolutePath, 0, MAX_PATH);
GetModuleFileNameA(NULL, absolutePath, MAX_PATH);
string strPath = (std::string)absolutePath;
int nPos = strPath.find_last_of('\\', strPath.length());
string strDirectory = strPath.substr(0, nPos); // 返回不带有可执行文件名的路径
char* pFileName = "\\2.txt";
buffer = const_cast<char*>(strDirectory.c_str());
strcat(buffer, pFileName);
std::fstream fs;
fs.open(buffer, std::ios::in);
if (!fs)
{
ofstream fout(buffer);
if (fout)
{
fout << "写入内容,也可以不写入" << endl;
fout.close();
}
}
else
{
std::cout << "该文件已经存在!" << endl;
}
FILE* fp = fopen(buffer, "wt");
while (recvTotalSize<fileSize)
{
int recvSize = recv(SockConn,recvBuf,sizeof(recvBuf),0);
recvTotalSize += recvSize;
printf("recved %d MB\n", recvTotalSize/(1024*1024));
fwrite(recvBuf,1,recvSize,fp);
}
fclose(fp);
if (recvTotalSize == fileSize)
{
printf("Done!");
}
else
{
printf("Error!");
}
closesocket(sockSrv); // 关闭已建立连接的套接字
WSACleanup(); // 终止对套接字到使用
getchar();
return 0;
}
TCP客户端程序:
#include "stdafx.h"
#include<stdio.h>
#include<direct.h> // _getcwd
#include<iostream>
#include<winsock2.h>
#include "Ws2tcpip.h"
#include <fstream>
#include<windows.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
//tcp连接部分
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(8888);
int nRtn = connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
if (nRtn == SOCKET_ERROR)
{
cout << "连接服务器失败!请重试" << endl;
return -1;
}
// 在指定的目录下读取创建文件
char* buffer;
char absolutePath[MAX_PATH];
memset(absolutePath, 0, MAX_PATH);
GetModuleFileNameA(NULL, absolutePath, MAX_PATH);
string strPath = (std::string)absolutePath;
int nPos = strPath.find_last_of('\\', strPath.length());
string strDirectory = strPath.substr(0, nPos); // 返回不带有可执行文件名的路径
char* pFileName = "\\1.txt";
buffer = const_cast<char*>(strDirectory.c_str());
strcat(buffer, pFileName);
std::fstream fs;
fs.open(buffer, std::ios::in);
if (!fs)
{
ofstream fout(buffer);
if (fout)
{
for (int i = 0; i < 100000; i++)
{
fout << "写入内容,也可以不写入" << endl;
}
fout.close();
}
}
else
{
cout << "该文件已经存在!" << endl;
}
//Ftp Client读取文件的长度
FILE* fp=fopen(buffer,"rt");
fseek(fp,0,SEEK_END);
int totalSize = ftell(fp);
fclose(fp);
//ftp Client发送文件长度
char fileSizeChr[20] = {0};
_itoa(totalSize, fileSizeChr,10);
send(sockClient, fileSizeChr,strlen(fileSizeChr)+1,0);
//ftp client发送文件
fp = fopen(buffer,"rt");
int readSize = 0;
int sendTotalSize = 0;
char sendBuf[1024] = { 0 };
while ((readSize=fread(sendBuf,1,sizeof(sendBuf),fp))>0)
{
send(sockClient, sendBuf, readSize, 0);
sendTotalSize += readSize;
printf("Send %d MB\n", sendTotalSize/(1024*1024));
}
fclose(fp);
if (sendTotalSize == totalSize)
{
printf("Done!");
}
else
{
printf("Error!");
}
closesocket(sockClient);
WSACleanup();
getchar();
return 0;
}
程序说明:主要利用本地回环地址测试,在打开客户端程序时新建一个txt文本: 1.txt,并且在其中写入内容。然后等连接上服务器,将1.txt的内容发送给服务器端,然后服务器端生成一个新的txt,2.txt用来接收收到的数据。具体运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3qV45O3l-1691826441169)(C:\Users\Administrator\Desktop\微信截图_20230812154138.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q36SB5Ay-1691826441171)(C:\Users\Administrator\Desktop\微信截图_20230812154202.png)]
小结:
代码中用到的各个函数如下:
socket:创建套接字
bind:将套接字绑定到本地端口上
listen:将指定的套接字设置为监听模式
accept:允许在套接字上尝试传入连接。
在这个示例中还有几个结构体和其他的函数介绍,大家可以去微软的官方网站去查看具体的作用。Win32
本次示例的源码如下,需要的朋友可以去下载:源码
好了,这次就介绍到这里了,欢迎大家一起学习交流。
icrosoft.com/zh-cn/windows/win32/api/winsock2/nf-winsock2-accept):允许在套接字上尝试传入连接。
在这个示例中还有几个结构体和其他的函数介绍,大家可以去微软的官方网站去查看具体的作用。Win32
本次示例的源码如下,需要的朋友可以去下载:源码
好了,这次就介绍到这里了,欢迎大家一起学习交流。