zoukankan      html  css  js  c++  java
  • C++封装的基于WinSock2的TCP服务端、客户端

      无聊研究Winsock套接字编程,用原生的C语言接口写出来的代码看着难受,于是自己简单用C++封装一下,把思路过程理清,方便自己后续翻看和新手学习。

      只写好了TCP通信服务端,有空把客户端流程也封装一下。

      先上主函数:

    // main.cpp : 异想家sandeepin poi!
    #include "stdafx.h"
    #include <iostream>
    extern int JTCPserver();
    extern int JTCPclient();
    int main()
    {
    	JTCPserver(); // 两者选一
    //	JTCPclient();
    	return 0;
    }
      JTCPserver.cpp内容:

    // JTCPserver.cpp : 蒋方正封装的TCP服务端
    
    #include "stdafx.h"
    #include <iostream>
    #include <string>
    #include <WinSock2.h> // WinSocket 
    #include <WS2tcpip.h> // IP地址转换用到inet_pton
    #pragma comment(lib,"ws2_32.lib")  
    using namespace std;
    
    // 【1】初始化WinSock
    bool initWinSock();
    // 【2】创建socket
    bool createSocket(SOCKET &listenScok);
    // 【3】socket绑定本机地址信息
    bool bindIPandPort(SOCKET &listenScok, const string ip, const unsigned short port);
    // 【4】侦听socket,接收客户端请求
    bool listenSocket(SOCKET &listenScok);
    // 【5】等待客户端连接-阻塞
    bool waitClientConnect(SOCKET &listenScok, SOCKET &clientSock);
    // 【6】接收数据-阻塞
    bool receiveData(SOCKET &clientSock, string &data);
    // 【7】停止套接字的接收、发送
    bool shutdownSocket(SOCKET &clientSock);
    // 【8】发送信息
    bool sendData(SOCKET &clientSock, const string &data);
    
    int JTCPserver()
    {		
    	SOCKET listenScok;	// 服务端Socket
    	SOCKET clientSock;	// 客户端Socket
    	string data;		// 收到的数据
    
    	// 【1】初始化WinSock
    	initWinSock();
    	// 【2】创建socket
    	createSocket(listenScok);
    	// 【3】socket绑定本机地址信息
    	bindIPandPort(listenScok, "127.0.0.1", 1994);
    	// 【4】侦听socket,接收客户端请求
    	listenSocket(listenScok);
    
    	// 坐等客户端连接
    	bool isClientSockConnect = false;	// 是否有客户端连进来
    	bool isReceiveData = false;			// 是否接收数据成功
    
    	while (true)
    	{ 
    		if (!isClientSockConnect)
    		{
    			// 【5】等待客户端连接
    			isClientSockConnect = waitClientConnect(listenScok, clientSock);
    		}
    		else
    		{
    			if(!isReceiveData)
    			{
    				// 【6】接收数据-阻塞
    				isReceiveData = receiveData(clientSock, data);
    				// 如果接收数据失败则断开
    				if(!isReceiveData) 
    				{
    					// 【7】停止套接字的接收、发送
    					shutdownSocket(clientSock);
    					cout << "等待客户端再连接..." << endl;
    					isClientSockConnect = false; // 可以重连了
    				}
    			}
    			if(isReceiveData && data != "jfzpoi"  && data != "@end#")
    			{
    				isReceiveData = false;
    			}
    			if(isReceiveData && data == "jfzpoi")
    			{
    				// 【8】发送信息(收的数据为jfzpoi)
    				sendData(clientSock, "sandeepin!
    ");
    				isReceiveData = false;
    			}
    			if (isReceiveData && data == "@end#")
    			{
    				// 【9】关闭相关
    				int err;
    //				err = shutdown(listenScok, 2);
    //				if (err == SOCKET_ERROR)
    //				{
    //					cout << "关闭失败!" << endl;
    //				}
    				// 关闭套接字,释放资源
    				err = closesocket(listenScok);
    				if (err == SOCKET_ERROR)
    				{
    					cout << "关闭socket失败!" << endl;
    				}
    				// 停止使用WinSock库,释放对应资源
    				if (WSACleanup() != 0)
    				{
    					cout << "WSA清空失败!" << endl;
    				}
    				cout << "关完了,坐等关机!poi" << endl;
    				return 0;
    			}
    		}
    	}
    }
    
    // 【1】初始化WinSock
    bool initWinSock()
    {
    	WORD verision = MAKEWORD(2, 2);
    	WSADATA lpData;
    	int intEr = WSAStartup(verision, &lpData); // 指定winsock版本并初始化
    	if (intEr != 0)
    	{
    		cout << "WinSock初始化失败!" << endl;
    		return false;
    	}
    	cout << "WinSock初始化成功!" << endl;
    	return true;
    }
    
    // 【2】创建socket
    bool createSocket(SOCKET &listenScok)
    {
    	// 创建侦听socket  
    	listenScok = socket(AF_INET, SOCK_STREAM, 0);
    	if (listenScok == INVALID_SOCKET)
    	{
    		cout << "socket创建失败!" << endl;
    		return false;
    	}
    	cout << "socket创建成功!" << endl;
    	return true;
    }
    
    // 【3】socket绑定本机地址信息
    bool bindIPandPort(SOCKET &listenScok, const string ip, const unsigned short port)
    {
    	// 制作sockaddr_in结构体
    	// 在bind函数,connect函数里提到了套接字编程网络地址信息结构体const struct sockaddr和const struct sockaddr_i
    	sockaddr_in hostAddr;
    	hostAddr.sin_family = AF_INET;
    	hostAddr.sin_port = htons(port);//转换成网络字节序  
    									//hostAddr.sin_addr.S_un.S_addr = inet_addr(SERVERIP);//转换成网络字节序  
    									//cout << "net IP:" << hostAddr.sin_addr.S_un.S_addr << endl;  
    									/*
    									inet_addr()版本太低,被弃用使用inet_pton(协议族,字符串IP地址,void目标in_addr*)
    									头文件:WS2tcpip.h
    									*/
    	in_addr addr;
    	inet_pton(AF_INET, ip.c_str(), (void*)&addr);
    	hostAddr.sin_addr = addr;
    	cout << "ip(网络字节序):" << addr.S_un.S_addr << endl;
    	cout << "ip(常规形式):" << ip.c_str() << endl;
    
    	// 侦听套接字listenSock绑定本机地址信息  
    	int err = bind(listenScok, (struct sockaddr*)&hostAddr, sizeof(sockaddr));
    	if (err != 0)
    	{
    		cout << "本地IP绑定失败!" << endl;
    		return false;
    	}
    	return true;
    }
    
    // 【4】侦听socket,接收客户端请求
    bool listenSocket(SOCKET &listenScok)
    {
    	// 设定套接字为侦听状态,准备接收客户机进程发送来的连接请求
    	int err = listen(listenScok, 3);
    	if (err != 0)
    	{
    		cout << "socket监听失败!" << endl;
    		return false;
    	}
    	cout << "监听客户端连接中……" << endl;
    	return true;
    }
    
    // 【5】等待客户端连接-阻塞
    bool waitClientConnect(SOCKET &listenScok, SOCKET &clientSock)
    {
    	sockaddr_in clientAddr;
    	int len = sizeof(struct sockaddr); // 必须指定长度,否则会导致accept返回10014错误
    									   // accept会循环等待客户端连接
    	clientSock = accept(listenScok, (struct sockaddr*)&clientAddr, &len);
    	cout << "客户端Socket编号:" << clientSock << endl;
    	if (clientSock == INVALID_SOCKET)
    	{
    		cout << "客户端连接失败!" << endl;
    		cout << WSAGetLastError() << endl;
    		return false;
    	}
    	return true;
    }
    
    // 【6】接收数据-阻塞
    bool receiveData(SOCKET &clientSock, string &data)
    {
    	static int cnt = 1; // 接收数据编号-静态
    						// 通过已建立连接的套接字,接收数据 设定缓冲1024字节
    	char buf[1024] = "";
    	// flags操作方式(0正常数据,MSG_PEED系统缓冲区的数据复制到所提供的接收缓冲区内,系统缓冲区数据未删除,MSG_OOB处理带外数据,通常用参数0即可)
    	int buflen = recv(clientSock, buf, 1024, 0);
    	if (buflen == SOCKET_ERROR)
    	{
    		cout << "接收失败!" << endl;
    		return false;
    	}
    	// 一切正常则显示接收数据
    	data = string(buf);
    	cout << "收到第" << cnt++ << "次,内容为:
    " << buf << endl;
    	return true;
    }
    
    // 【7】停止套接字的接收、发送
    bool shutdownSocket(SOCKET &clientSock)
    {
    	//(收完就关)停止套接字的接收、发送功能(0禁止接收,1禁止发送,2禁制接收发送)
    	int err = shutdown(clientSock, 2);
    	if (err == SOCKET_ERROR)
    	{
    		cout << "关闭Socket失败!" << endl;
    		return false;
    	}
    	return true;
    }
    
    // 【8】发送信息
    bool sendData(SOCKET &clientSock, const string &data)
    {
    
    	int err = send(clientSock, data.c_str(), data.size(), 0);
    	if (err == SOCKET_ERROR)
    	{
    		cout << "发送失败!" << endl;
    		return false;
    	}
    	cout << "发送数据为:
    " << data << endl;
    	return true;
    }
      JTCPclient.cpp内容:

    // JTCPclient.cpp : 蒋方正封装的TCP客户端代码
    
    #include "stdafx.h"  
    #include <iostream>
    #include <WinSock2.h>  
    #include <WS2tcpip.h>  
    #pragma comment(lib,"ws2_32.lib")
    using namespace std;
    
    // 【1】初始化WinSock
    bool initWinSockC();
    // 【2】创建socket
    bool createSocketC(SOCKET &listenScok);
    // 【3】连接到服务器
    bool connectSocketC(SOCKET &conSock, const string ip, const unsigned short port);
    // 【4】发送数据
    bool sendDataC(SOCKET &clientSock, const string &data);
    // 【5】接收数据
    bool receiveDataC(SOCKET &clientSock, string &data);
    
    int JTCPclient()
    {
    	SOCKET clientSock;	// 客户端Socket
    	string data;		// 收到的数据
    
    	bool isCreateSocket = false;	// 是否创建了Socket
    	bool isConnectSocket = false;	// 是否连上了服务器
    	bool isSendDataOK = false;		// 是否发送成功数据
    	bool isReceiveDataOK = false;	// 是否接收成功数据
    
    	// 【1】初始化WinSock
    	if(initWinSockC())
    	{
    		while (true)
    		{
    			if(!isCreateSocket)
    			{
    				// 【2】创建socket
    				createSocketC(clientSock);
    				isCreateSocket = true;
    			}
    			else
    			{
    				if(!isConnectSocket)
    				{
    					// 【3】连接到服务器
    					connectSocketC(clientSock, "127.0.0.1", 1994);
    					isConnectSocket = true;
    				}
    				else
    				{
    					if(!isSendDataOK)
    					{
    						// 【4】发送数据
    						isSendDataOK = sendDataC(clientSock, "jfz hello
    ");
    					}
    					else
    					{
    						if(!isReceiveDataOK)
    						{
    							// 【5】接收数据
    							isReceiveDataOK = receiveDataC(clientSock, data);
    						}
    						else
    						{
    							if(data == "@end#")
    							{
    								WSACleanup();
    								return 0;
    							}
    							isReceiveDataOK = false;
    							isSendDataOK = false;
    						}
    					}
    					
    				}
    			}
    		}
    	}
    	return 0;
    }
    
    // 【1】初始化WinSock
    bool initWinSockC()
    {
    	WORD verision = MAKEWORD(2, 2);
    	WSADATA lpData;
    	int intEr = WSAStartup(verision, &lpData); // 指定winsock版本并初始化
    	if (intEr != 0)
    	{
    		std::cout << "WinSock初始化失败!" << endl;
    		return false;
    	}
    	std::cout << "WinSock初始化成功!" << endl;
    	return true;
    }
    
    // 【2】创建socket
    bool createSocketC(SOCKET &listenScok)
    {
    	// 创建侦听socket  
    	listenScok = socket(AF_INET, SOCK_STREAM, 0);
    	if (listenScok == INVALID_SOCKET)
    	{
    		cout << "socket创建失败!" << endl;
    		return false;
    	}
    	cout << "socket创建成功!" << endl;
    	return true;
    }
    
    // 【3】连接到服务器
    bool connectSocketC(SOCKET &conSock, const string ip, const unsigned short port)
    {
    	// 建立地址结构体
    	sockaddr_in hostAddr;
    	hostAddr.sin_family = AF_INET;
    	hostAddr.sin_port = htons(port);//转换成网络字节序  
    									//hostAddr.sin_addr.S_un.S_addr = inet_addr(SERVERIP);//转换成网络字节序  
    									//cout << "net IP:" << hostAddr.sin_addr.S_un.S_addr << endl;  
    									/*
    									inet_addr()版本太低,被弃用使用inet_pton(协议族,字符串IP地址,void目标in_addr*)
    									头文件:WS2tcpip.h
    									*/
    	in_addr addr;
    	inet_pton(AF_INET, ip.c_str(), (void*)&addr);
    	hostAddr.sin_addr = addr;
    	cout << "ip(网络字节序):" << addr.S_un.S_addr << endl;
    	cout << "ip(常规形式):" << ip.c_str() << endl;
    
    	// 向服务器提出连接请求
    	int err = connect(conSock, (sockaddr*)&hostAddr, sizeof(sockaddr));
    	if (err == INVALID_SOCKET)
    	{
    		cout << "连接服务器失败!" << endl;
    		return false;
    	}
    	return true;
    }
    
    // 【4】发送数据
    bool sendDataC(SOCKET &clientSock, const string &data)
    {
    	int err = send(clientSock, data.c_str(), data.size(), 0);
    	if (err == SOCKET_ERROR)
    	{
    		cout << "发送失败!" << endl;
    		return false;
    	}
    	cout << "发送数据为:
    " << data.c_str() << endl;
    	return true;
    }
    
    // 【5】接收数据
    bool receiveDataC(SOCKET &clientSock, string &data)
    {
    	static int cnt = 1; // 接收数据编号-静态
    						// 通过已建立连接的套接字,接收数据 设定缓冲1024字节
    	char buf[1024] = "";
    	// flags操作方式(0正常数据,MSG_PEED系统缓冲区的数据复制到所提供的接收缓冲区内,系统缓冲区数据未删除,MSG_OOB处理带外数据,通常用参数0即可)
    	int buflen = recv(clientSock, buf, 1024, 0);
    	if (buflen == SOCKET_ERROR)
    	{
    		cout << "接收失败!" << endl;
    		return false;
    	}
    	// 一切正常则显示接收数据
    	data = string(buf);
    	cout << "收到第" << cnt++ << "次,内容为:
    " << buf << endl;
    	return true;
    }

  • 相关阅读:
    解决小程序sessionid不一致
    小程序实现写入缓存与读取缓存
    小程序登录时如何获取input框中的内容
    js实现截取字符串后几位
    js用正则判断身份证号码
    sublime Text3安装及配置与解决安装插件失败
    jQuery实现全选与全部选
    jQuery实现enter键登录
    常用正则表达式的判断与写法
    js实现正则判断手机号
  • 原文地址:https://www.cnblogs.com/sandeepin/p/12236521.html
Copyright © 2011-2022 走看看