zl程序教程

您现在的位置是:首页 >  系统

当前栏目

Windows下Socket编程TCP——应付大部分的Windows TPC编程场景。

Windows编程TCP 场景 socket 大部分
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;
}