Windows下Socket编程TCP——应付大部分的Windows TPC编程场景。
2023-09-14 09:07:02 时间
首先是这些函数的定义:
#define INTERNAL_LOG_ON 0
#define INTERNAL_LOG_OFF 1
#define INTERNAL_LOG_MODE INTERNAL_LOG_OFF //or INTERNAL_LOG_ON
//#define INTERNAL_LOG_MODE INTERNAL_LOG_ON //or INTERNAL_LOG_OFF
namespace WinTCP
{
void internalLog(const char* format, ...);
//windows 下加载组件
bool initWSADATA();
//windows 下卸载组件
void uninitWSADATA();
//创建TCP套接字
//return: socket_fd
int createWinTCPSocket();
//关闭套接字
void closeWinTCPSocket(int socket_fd);
//服务器绑定
//socket_fd 为服务器套接字fd
//port 为服务器要绑定的端口号
//return 返回绑定成功与否
bool hostBind(int socket_fd, int port);
//服务器监听
//socket_fd 为服务端套接字
//return 返回客户端套接字fd
int hostListen(int socket_fd);
//连接服务端
//socket_fd 为客户端fd
//addr 为服务端的ip地址
//addr 为所连服务端的端口
//return : 成功或者失败
bool connectToHost(int socket_fd, int addr, int port);
//读取数据
//socket_fd 为所要读取的socket_fd
//data 为缓冲区
//size 为缓冲区大小
//timeout 为超时时间(msec)
//try_count 为失败尝试的次数
//return 实际读取到的数据长度
int readData(int socket_fd, char* data, int size, int timeout, int try_count = 2);
//写入数据
//socket_fd 为所要写入的socket_fd
//data 为缓冲区
//size 为缓冲区大小
//timeout 为超时时间(msec)
//try_count 为失败尝试的次数
//return 实际读写入的数据长度
int writeData(int socket_fd, char* data, int size, int timeout, int try_count = 2);
}
然后是这些函数的实现:
#include <stdio.h>
#include <stdarg.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
namespace WinTCP
{
static bool hasInitWSADATA = false;
void internalLog(const char* format, ...)
{
if(INTERNAL_LOG_MODE == INTERNAL_LOG_OFF)
return;
va_list argList;
va_start(argList, format);
vfprintf(stdout, format, argList);
va_end(argList);
}
bool initWSADATA()
{
if(!hasInitWSADATA)
{
WSADATA wsa_data;
hasInitWSADATA = ::WSAStartup(MAKEWORD(2,2), &wsa_data) == 0;
if(!hasInitWSADATA)
{
int errorCode = ::WSAGetLastError();
internalLog("initWSADATA: hasInit(%d) errorCode(%d).\n", hasInitWSADATA, errorCode);
}
}
return hasInitWSADATA;
}
void uninitWSADATA()
{
hasInitWSADATA = false;
::WSACleanup();
}
int createWinTCPSocket()
{
SOCKET socket_fd;
socket_fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
return socket_fd;
}
void closeWinTCPSocket(int socket_fd)
{
if(0>::closesocket(socket_fd))
{
int errorCode = ::WSAGetLastError();
internalLog("closeWinTCPSocket: error(%d).\n", errorCode);
}
}
bool hostBind(int socket_fd, int port)
{
SOCKADDR_IN server_addr; //服务器配置
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port); //端口号范围: 0 ~65535
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //INADDR_ANY意思是接受所有IP的连接
if(0>::bind(socket_fd,(SOCKADDR *)&server_addr,sizeof(SOCKADDR))) //绑定ip域socket,出错返回SOCKET_ERROR
{
int errorCode = ::WSAGetLastError();
internalLog("hostBind: fd(%d) errorCode(%d).\n", socket_fd, errorCode);
return false;
}
internalLog("hostBind: fd(%d) port(%d).\n", socket_fd, port);
return true;
}
int hostListen(int socket_fd)
{
if(0 > ::listen(socket_fd, 5))
{
int errorCode = ::WSAGetLastError();
internalLog("hostListen: fd(%d) listen error(%d).\n", socket_fd, errorCode);
}
SOCKADDR_IN client_addr; //存储客户端信息
SOCKET fd_client; //客户端套接字
int addr_len = sizeof(SOCKADDR);
fd_client = ::accept(socket_fd,(SOCKADDR *)&client_addr,&addr_len);
if(0>= fd_client)
{
int errorCode = ::WSAGetLastError();
internalLog("hostListen: fd(%d) accept error(%d).\n", socket_fd, errorCode);
}
internalLog("hostListen: fd(%d) accept client(%d).\n", socket_fd, fd_client);
return fd_client;
}
bool connectToHost(int socket_fd, int addr, int port)
{
SOCKADDR_IN server_addr;
server_addr.sin_family = AF_INET; //Internet协议
server_addr.sin_port = htons(port);
server_addr.sin_addr.S_un.S_addr = addr;//inet_addr("127.0.0.1");
if(0>::connect(socket_fd,(SOCKADDR *)&server_addr,sizeof(SOCKADDR))) //连接失败则返回SOCKET_ERROR
{
int errorCode = ::WSAGetLastError();
internalLog("connectToHost: fd(%d) errorCode(%d).\n", socket_fd, errorCode);
return false;
}
internalLog("connectToHost: fd(%d) addr(%d) port(%d).\n", socket_fd, addr, port);
return true;
}
int readData(int socket_fd, char* data, int size, int timeout, int try_count /*= 2*/)
{
timeout *= 1000;
FD_SET read_fds;
FD_SET except_fds;
timeval t = {0, timeout};
int recv_size = 0;
u_long blacked_on = 0;
u_long blacked_off = 1;
if(0>::ioctlsocket(socket_fd, FIONBIO, &blacked_off))
{
int errorCode = ::WSAGetLastError();
internalLog("readData: fd(%d) set socket to blacked_off error(%d).\n", socket_fd, errorCode);
return recv_size;
}
for(int i = 0; i < try_count; i++)
{
internalLog("readData: fd(%d) try(%d/%d) read data...\n", socket_fd, i, try_count);
FD_ZERO(&read_fds);
FD_ZERO(&except_fds);
FD_SET(socket_fd, &read_fds);
FD_SET(socket_fd, &except_fds);
int res = ::select(0, &read_fds, 0, &except_fds, &t); //windows 中nfds没有意义
if(res < 0)
{
int errorCode = ::WSAGetLastError();
internalLog("readData: fd(%d) select error(%d).\n", socket_fd, errorCode);
}
else if(res == 0)
{
internalLog("readData: fd(%d) timeout...\n", socket_fd);
}
else
{
if(FD_ISSET(socket_fd, &except_fds))
{
int errorCode = ::WSAGetLastError();
internalLog("readData: fd(%d) exception(%d).\n", socket_fd, errorCode);
}
else if(FD_ISSET(socket_fd, &read_fds))
{
int ret = ::recv(socket_fd, data + recv_size, size - recv_size, 0);
if(ret > 0)
{
recv_size += ret;
}
}
}
if(recv_size == size) //*读取到够了的数据,返回
break;
}
if(0>::ioctlsocket(socket_fd, FIONBIO, &blacked_on))
{
int errorCode = ::WSAGetLastError();
internalLog("readData: fd(%d) set socket to blacked_on error(%d).\n", socket_fd, errorCode);
return recv_size;
}
return recv_size;
}
int writeData(int socket_fd, char* data, int size, int timeout, int try_count /*= 2*/)
{
FD_SET write_fds;
FD_SET except_fds;
timeval t = {0, timeout};
int write_size = 0;
u_long blacked_on = 0;
u_long blacked_off = 1;
if(0>::ioctlsocket(socket_fd, FIONBIO, &blacked_off))
{
int errorCode = ::WSAGetLastError();
internalLog("writeData: fd(%d) set socket to blacked_off error(%d).\n", socket_fd, errorCode);
return write_size;
}
for(int i = 0; i < try_count; i++)
{
internalLog("writeData: fd(%d) try(%d/%d) write data...\n", socket_fd, i, try_count);
FD_ZERO(&write_fds);
FD_ZERO(&except_fds);
FD_SET(socket_fd, &write_fds);
FD_SET(socket_fd, &except_fds);
int res = ::select(0, 0, &write_fds, &except_fds, &t); //windows 中nfds没有意义
if(res < 0)
{
int errorCode = ::WSAGetLastError();
internalLog("writeData: fd(%d) select error(%d).\n", socket_fd, errorCode);
}
else if(res == 0)
{
internalLog("writeData: fd(%d) timeout...\n", socket_fd);
}
else
{
if(FD_ISSET(socket_fd, &except_fds))
{
int errorCode = ::WSAGetLastError();
internalLog("writeData: fd(%d) exception(%d).\n", socket_fd, errorCode);
}
else if(FD_ISSET(socket_fd, &write_fds))
{
int ret = ::send(socket_fd, data + write_size, size - write_size, 0);
if(ret > 0)
{
write_size += ret;
}
}
}
if(write_size == size) //*读取到够了的数据,返回
break;
}
if(0>::ioctlsocket(socket_fd, FIONBIO, &blacked_on))
{
int errorCode = ::WSAGetLastError();
internalLog("writeData: fd(%d) set socket to blacked_on error(%d).\n", socket_fd, errorCode);
return write_size;
}
return write_size;
}
}
使用这些函数能应付几乎全部的Windows TPC编程场景。
以下提供简易实现服务的示例:
服务器
int main(int argc, char *argv[])
{
WinTCP::initWSADATA();
int server_fd = WinTCP::createWinTCPSocket();
WinTCP::hostBind(server_fd, 3306);
while(true)
{
printf("listening..\n");
int client_fd = WinTCP::hostListen(server_fd);
printf("recive:%s\n", buf);
memset(buf, 0, 2048);
strcpy(buf, "this is server\n");
printf("send:%s\n",buf);
int snd_count = WinTCP::writeData(client_fd, buf, strlen(buf), 3000, 3);
printf("send count:%d\n",snd_count);
WinTCP::closeWinTCPSocket(client_fd);
}
WinTCP::uninitWSADATA();
return 0;
}
相关文章
- 【windows核心编程】HideProcess
- 【windows核心编程】系统消息与自定义钩子(Hook)使用
- 【windows核心编程】远程线程DLL注入
- 使用winsw包装服务将nginx包装为Windows服务
- golang程序在windows上,注册为服务
- C++ Windows编程中WPARAM等数据类型在32位和64位系统中的差异
- 非可视化编程的windows窗口 C++ 代码设计:附例程并多多知识点
- windows映射模式
- windows环境里React-Native运行失败,找不到Nullable的原因分析
- Windows串口编程(同步异步)
- Windows核心编程条件变量
- android windows 上JNI编程
- 执行此安装程序之前,必须安装 32 位 Windows 映像处理组件(WIC)解决的方法
- windows 在命令行设置代理
- Qt开发,跨平台,区分windows和linux
- Windows上提示 api-ms-win-core-path-l1-1-0.dll 丢失怎么办?
- 不改动当前账户密码的情况下,启用其他账户登录windows系统
- 使用 Windows 10 WSL 搭建 ESP8266 编译环境并使用 VSCODE 编程(一)(2019-08-23)
- 恶意代码分析实战 windbg恶意软件分析 lab 10-3 通过rootkit隐藏恶意进程 这玩意要玩得很6的话 还是要对windows内核编程非常熟才行
- Neodynamic Barcode Professional for Windows Forms 14.0
- Windows Sever 2019 AD域怎么添加新账户并将新计算机入域