C++ Socket网络通信实现一个服务器端和多个客户端的通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011599639/article/details/69383088
一 描述

1 采用C++语言

2 编译环境是code::blocks

3 使用的是非阻塞套接字

二 功能描述

1 一个服务器对多个客户端

2 服务器端主要负责处理数据的输入并发送,具体是通过发送指令向其指定的客户端发送数据,发送接收指令读取其指定的客户端发送的数据

3 客户端的主要负责接收并发送数据,不做数据的处理

三 实现情况

1 server 端


2 client 端


四 server思路图


五 程序源码

#include "server.h"
#include <string.h>

//定义全局变量

char	dataBuf[MAX_NUM_BUF];				//写缓冲区
BOOL	bConning;							//与客户端的连接状态
BOOL    bSend ;                             //发送标记位
SOCKET	sServer;							//服务器监听套接字
CRITICAL_SECTION	cs;			            //保护数据的临界区对象
HANDLE	hAcceptThread;						//数据处理线程句柄
HANDLE	hRecvThread;						//数据接收线程
ClIENTVECTOR clientvector;
SVECTOR    stringvector;

/**
 * 初始化
 */
BOOL InitSever(void)
{

	InitMember();//初始化全局变量

	//初始化SOCKET
	if (!InitSocket())
		return FALSE;

	return TRUE;
}

/**
 * 初始化全局变量
 */
void	InitMember(void)
{
	InitializeCriticalSection(&cs);				            //初始化临界区
	memset(dataBuf, 0, MAX_NUM_BUF);
	bSend = FALSE;
	bConning = FALSE;									    //服务器为没有运行状态
	hAcceptThread = NULL;									//设置为NULL
	hRecvThread = NULL;
	sServer = INVALID_SOCKET;								//设置为无效的套接字
	clientvector.clear();									//清空向量
	stringvector.clear();
}

/**
 *  初始化SOCKET
 */
bool InitSocket(void)
{
	//返回值
	int reVal;

	//初始化Windows Sockets DLL
	WSADATA  wsData;
	reVal = WSAStartup(MAKEWORD(2,2),&wsData);

	//创建套接字
	sServer = socket(AF_INET, SOCK_STREAM, 0);
	if(INVALID_SOCKET== sServer)
		return FALSE;

	//设置套接字非阻塞模式
	unsigned long ul = 1;
	reVal = ioctlsocket(sServer, FIONBIO, (unsigned long*)&ul);
	if (SOCKET_ERROR == reVal)
		return FALSE;

	//绑定套接字
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(SERVERPORT);
	serAddr.sin_addr.S_un.S_addr = INADDR_ANY;
	reVal = bind(sServer, (struct sockaddr*)&serAddr, sizeof(serAddr));
	if(SOCKET_ERROR == reVal )
		return FALSE;

	//监听
	reVal = listen(sServer, SOMAXCONN);
	if(SOCKET_ERROR == reVal)
		return FALSE;

	//等待客户端的连接
	cout << "Server succeeded!" << endl;
	cout << "Waiting for clients..." << endl;

	return TRUE;
}

/**
 *  启动服务
 */
bool StartService(void)
{
    BOOL reVal = TRUE;	//返回值

	ShowTipMsg(TRUE);	//提示用户输入

	char cInput;		//输入字符
	do
	{
		cin >> cInput;
		if ('s' == cInput || 'S' == cInput)
		{
			if (createAcceptThread())	//接受客户端请求的线程
			{
				ShowServerStartMsg(TRUE);		//创建线程成功信息
			}else{
				reVal = FALSE;
			}
			bSend = TRUE;
			break;//跳出循环体

		}else{
			ShowTipMsg(TRUE);
		}

	} while(cInput != 's' && cInput != 'S'); //必须输入's'或者'S'字符

	return reVal;
}

/**
 *@des:create a client request thread
 */
BOOL createAcceptThread(void)
{
    bConning = TRUE;//设置服务器为运行状态

	//创建释放资源线程
	unsigned long ulThreadId;
	//创建接收客户端请求线程
	hAcceptThread = CreateThread(NULL, 0, AcceptThread, NULL, 0, &ulThreadId);
	if( NULL == hAcceptThread)
	{
		bConning = FALSE;
		return FALSE;
	}
	else
    {
		CloseHandle(hAcceptThread);
	}
	return TRUE;
}

/**
 * 接受客户端连接
 */
DWORD __stdcall AcceptThread(void* pParam)
{
    SOCKET  sAccept;							//接受客户端连接的套接字
	sockaddr_in addrClient;						//客户端SOCKET地址

	while(bConning)						           //服务器的状态
	{
		memset(&addrClient, 0, sizeof(sockaddr_in));					//初始化
		int	lenClient = sizeof(sockaddr_in);				        	//地址长度
		sAccept = accept(sServer, (sockaddr*)&addrClient, &lenClient);	//接受客户请求
		if(INVALID_SOCKET == sAccept )
		{
		    Sleep(100);
			int nErrCode = WSAGetLastError();
			if(nErrCode == WSAEWOULDBLOCK)	//无法立即完成一个非阻挡性套接字操作
			{
				Sleep(TIMEFOR_THREAD_SLEEP);
				continue;//继续等待
			}
			else
            {
				return 0;//线程退出
			}

		}
		else//接受客户端的请求
		{
            //显示客户端的IP和端口
            char *pClientIP = inet_ntoa(addrClient.sin_addr);
            u_short  clientPort = ntohs(addrClient.sin_port);
            cout<<"Accept a client."<<endl;
            cout<<"IP: "<<pClientIP<<"\tPort: "<<clientPort<<endl;
            //加入容器
			clientvector.push_back(sAccept);
		}
	}
	return 0;//线程退出
}

/**
 *@des:处理数据
 */
 void inAndOut(void)
 {
    while(bConning)
    {
        memset(dataBuf, 0, MAX_NUM_BUF);		//清空接收缓冲区
        cin.getline(dataBuf,MAX_NUM_BUF);			//输入数据
        //发送数据
        if(bSend)
        {
            handleData(dataBuf);
        }
    }
 }

/**
 *@des:read a line words
 */
 /**
 *读一行数据
 */
bool RecvLine(SOCKET s, char* buf)
{
	bool retVal = TRUE;			//返回值
	bool bLineEnd = FALSE;		//行结束
	int	 nReadLen = 0;			//读入字节数
	int	 nDataLen = 0;			//数据长度
    memset(buf, 0, MAX_NUM_BUF);		//清空接收缓冲区
	while (!bLineEnd && bConning)	//与客户端连接 没有换行
	{
		nReadLen = recv(s, buf + nDataLen, 1, 0);	//每次接收一个字节
		//错误处理
		if(SOCKET_ERROR == nReadLen)
		{
			int nErrCode = WSAGetLastError();
			if ( WSAEWOULDBLOCK == nErrCode )	//接受数据缓冲区不可用
			{
				continue;						//继续循环
			}
			else if(WSAENETDOWN == nErrCode || WSAETIMEDOUT == nErrCode || WSAECONNRESET == nErrCode) //客户端关闭了连接
			{
			    retVal = FALSE;	//读数据失败
				break;							//线程退出
			}
		}

		if(0 == nReadLen)
		{
			retVal = FALSE;

			break;
		}

		if('\n'==*(buf + nDataLen))	//读取结束
		{
			bLineEnd = TRUE;
			bSend = TRUE;           //开始发送
		}
		else
		{
			nDataLen += nReadLen; //增加数据长度
		}
	}
	return retVal;
}

/**
 *	读取数据
 */

bool recvData(SOCKET s, char* buf)
{
	memset(buf, 0, MAX_NUM_BUF);		//清空接收缓冲区
    int	 nReadLen = 0;			//读入字节数
    bool bLineEnd = FALSE;		//行结束
    BOOL retVal = TRUE;

    while(!bLineEnd)
    {
        nReadLen = recv(s, buf, MAX_NUM_BUF, 0);
        if(SOCKET_ERROR == nReadLen)
        {
            int nErrCode = WSAGetLastError();
            if ( WSAEWOULDBLOCK == nErrCode )	//接受数据缓冲区不可用
            {
                continue;						//继续循环
            }
            else if(WSAENETDOWN == nErrCode || WSAETIMEDOUT == nErrCode || WSAECONNRESET == nErrCode) //客户端关闭了连接
            {
                retVal = FALSE;	//读数据失败
                break;							//线程退出
            }
        }

       if(0 == nReadLen)           //未读取到数据
        {
            retVal = FALSE;
            break;
        }
        bLineEnd = TRUE;

    }


    return retVal;
}

/**
 * @Des:send data to client
 */
bool sendData(SOCKET s, char* str)
{
    int retVal;                             //返回值
    bool bLineEnd = FALSE;		//行结束

    while(!bLineEnd)
    {
        retVal = send(s, str, strlen(str), 0);//一次发送
        //错误处理
        if (SOCKET_ERROR == retVal)
        {
            int nErrCode = WSAGetLastError();//错误代码
            if (WSAEWOULDBLOCK == nErrCode)
            {
                continue;
            }
            else if(WSAENETDOWN == nErrCode || WSAETIMEDOUT == nErrCode || WSAECONNRESET == nErrCode)
            {
                return FALSE;
            }
        }
        bLineEnd = TRUE;
    }

	return TRUE;		//发送成功
}

/**
 *@des:handle  data to choose what kind of pattern
 */
 void handleData(char* str)
 {
    SOCKET  sClient;
    char cnum;                //显示几号字套接字
    int num;

    if(!strncmp(WRITE, str, strlen(WRITE))) //判断输入指令是否为
    {
        str += strlen(WRITE);
        cnum = *str++;
        num = cnum - '1';
        //增加容量处理
        sClient = clientvector.at(num);     //发送到指定客户端

        if(!sendData(sClient, str))
        {
            ExitServer();//退出
        }

    }
    else if(!strncmp(READ, str, strlen(READ)))
    {
        str += strlen(READ);
        cnum = *str++;
        num = cnum - '1';
        sClient = clientvector.at(num);     //发送到指定客户端

        //接收客户端数据
        if (!recvData(sClient, dataBuf))
        {
            ExitServer();//退出
        }
        //显示客户端数据
        cout << dataBuf<<endl;
    }
    else if(('e'==str[0] || 'E'== str[0])&&('\n' == str[1]))     //判断是否退出
    {
        bConning = FALSE;
        ExitServer();
        ShowTipMsg(FALSE);
    }
    else
    {
        cout <<"Input error!!"<<endl;
    }
 }

/**
 *  释放资源
 */
void  ExitServer(void)
{
	closesocket(sServer);					//关闭SOCKET
	WSACleanup();							//卸载Windows Sockets DLL
}

void ShowTipMsg(BOOL bFirstInput)
{
	if (bFirstInput)//第一次
	{
		cout << endl;
		cout << endl;
		cout << "**********************" << endl;
		cout << "* s(S): Start server *" << endl;
		cout << "**********************" << endl;
		cout << "Please input:" ;

	}else{//退出服务器
		cout << endl;
		cout << endl;
		cout << "**********************" << endl;
		cout << "* e(E): Exit  server *" << endl;
		cout << "**********************" << endl;
		cout << " Please input:" ;
	}
}

/**
 * 显示启动服务器成功与失败消息
 */
void  ShowServerStartMsg(BOOL bSuc)
{
	if (bSuc)
	{
		cout << "**********************" << endl;
		cout << "* Server succeeded!  *" << endl;
		cout << "**********************" << endl;
	}else{
		cout << "**********************" << endl;
		cout << "* Server failed   !  *" << endl;
		cout << "**********************" << endl;
	}

}

#include "client.h"

//变量
SOCKET	sClient;							//套接字
char dataBuf[MAX_NUM_BUF];					//数据缓冲区
BOOL    bSend = FALSE;                      //发送标记位
BOOL	bConnecting;						//与服务器的连接状态

/**
 *	初始化
 */
BOOL InitClient(void)
{
	//初始化全局变量
	InitMember();

	//创建SOCKET
	if (!InitSockt())
	{
		return FALSE;
	}

	return TRUE;
}


/**
 * 初始化全局变量
 */
void InitMember(void)
{
//	InitializeCriticalSection(&cs);

	sClient = INVALID_SOCKET;	//套接字
//	handleThread = NULL;		//接收数据线程句柄
	bConnecting = FALSE;		//为连接状态

//	//初始化数据缓冲区
//	memset(dataBuf, 0, MAX_NUM_BUF);
}

/**
 * 创建非阻塞套接字
 */
BOOL  InitSockt(void)
{
	int			reVal;	//返回值
	WSADATA		wsData;	//WSADATA变量
	reVal = WSAStartup(MAKEWORD(2,2),&wsData);//初始化Windows Sockets Dll

	//创建套接字
	sClient = socket(AF_INET, SOCK_STREAM, 0);
	if(INVALID_SOCKET == sClient)
		return FALSE;


	//设置套接字非阻塞模式
	unsigned long ul = 1;
	reVal = ioctlsocket(sClient, FIONBIO, (unsigned long*)&ul);
	if (reVal == SOCKET_ERROR)
		return FALSE;

	return TRUE;
}

/**
 * 连接服务器
 */
BOOL ConnectServer(void)
{
	int reVal;			//返回值
	sockaddr_in serAddr;//服务器地址
	//输入要连接的主机地址
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(SERVERPORT);
    serAddr.sin_addr.S_un.S_addr = inet_addr(SERVERIP);

	while(true)
	{
		//连接服务器
		reVal = connect(sClient, (struct sockaddr*)&serAddr, sizeof(serAddr));
		//处理连接错误
		if(SOCKET_ERROR == reVal)
		{
			int nErrCode = WSAGetLastError();
			if( WSAEWOULDBLOCK == nErrCode || WSAEINVAL == nErrCode)    //连接还没有完成
			{
				continue;
			}
			else if (WSAEISCONN == nErrCode)//连接已经完成
			{
				break;
			}
			else//其它原因,连接失败
			{
				return FALSE;
			}
		}

		if ( reVal == 0 )//连接成功
			break;
	}

	bConnecting = TRUE;

	return TRUE;
}

/**
 *@des:recvive and send data
 */
 void recvAndSend(void)
{
    while(bConnecting)
    {
        //接收客户端数据
        if (!recvData(sClient, dataBuf))
        {
            ExitClient();//退出
        }
        //显示客户端数据
        cout << dataBuf<<endl;
        //向客户端发送数据
        if(!sendData(sClient, dataBuf))
        {
            ExitClient();//退出
        }
    }
}

/**
 *@des:read a line words
 */
 /**
 *读一行数据
 */
bool RecvLine(SOCKET s, char* buf)
{
	bool retVal = TRUE;			//返回值
	bool bLineEnd = FALSE;		//行结束
	int	 nReadLen = 0;			//读入字节数
	int	 nDataLen = 0;			//数据长度
    memset(buf, 0, MAX_NUM_BUF);		//清空接收缓冲区
	while (!bLineEnd && bConnecting)	//与客户端连接 没有换行
	{
		nReadLen = recv(s, buf + nDataLen, 1, 0);	//每次接收一个字节
		//错误处理
		if(SOCKET_ERROR == nReadLen)
		{
			int nErrCode = WSAGetLastError();
			if ( WSAEWOULDBLOCK == nErrCode )	//接受数据缓冲区不可用
			{
				continue;						//继续循环
			}
			else if(WSAENETDOWN == nErrCode || WSAETIMEDOUT == nErrCode || WSAECONNRESET == nErrCode) //客户端关闭了连接
			{
			    retVal = FALSE;	//读数据失败
				break;							//线程退出
			}
		}

		if(0 == nReadLen)
		{
			retVal = FALSE;

			break;
		}

        if(('s'==buf[0] || 'S'==buf[0])&&('\n' == buf[1]))     //判断是否退出
        {
            bConnecting = FALSE;
            bSend = FALSE;
            memset(buf, 0, MAX_NUM_BUF);		//清空接收缓冲区
            ShowConnectMsg(FALSE);
        }

		if('\n'==*(buf + nDataLen))	//读取结束
		{
			bLineEnd = TRUE;
			bSend = TRUE;           //开始发送
		}
		else
		{
			nDataLen += nReadLen; //增加数据长度
		}
	}
	return retVal;
}

/**
 *	读取数据
 */
bool recvData(SOCKET s, char* buf)
{
	BOOL retVal = TRUE;
	bool bLineEnd = FALSE;		//行结束
	memset(buf, 0, MAX_NUM_BUF);		//清空接收缓冲区
    int	 nReadLen = 0;			//读入字节数

   while(!bLineEnd)
    {
        nReadLen = recv(s, buf, MAX_NUM_BUF, 0);
        if(SOCKET_ERROR == nReadLen)
        {
            int nErrCode = WSAGetLastError();
            if ( WSAEWOULDBLOCK == nErrCode )	//接受数据缓冲区不可用
            {
                continue;						//继续循环
            }
            else if(WSAENETDOWN == nErrCode || WSAETIMEDOUT == nErrCode || WSAECONNRESET == nErrCode) //客户端关闭了连接
            {
                retVal = FALSE;	//读数据失败
				bSend = FALSE;
                break;							//线程退出
            }
        }

        if(0 == nReadLen)           //未读取到数据
        {
            retVal = FALSE;
			bSend = FALSE;
            break;
        }
		if(('e'==buf[0] || 'E'==buf[0])&&(0 == buf[1]))     //判断是否退出
        {
            bConnecting = FALSE;
            bSend = FALSE;
            memset(buf, 0, MAX_NUM_BUF);		//清空接收缓冲区
            ShowConnectMsg(FALSE);
        }
		bSend = TRUE;           //开始发送
		bLineEnd = TRUE;
    }


    return retVal;
}


/**
 * @Des:send data to client
 */
bool sendData(SOCKET s, char* str)
{
    int retVal;//返回值

    while(bSend)
    {
        retVal = send(s, str, strlen(str), 0);//一次发送
        //错误处理
        if (SOCKET_ERROR == retVal)
        {
            int nErrCode = WSAGetLastError();//错误代码
            if (WSAEWOULDBLOCK == nErrCode)
            {
                continue;
            }
            else if(WSAENETDOWN == nErrCode || WSAETIMEDOUT == nErrCode || WSAECONNRESET == nErrCode)
            {
                return FALSE;
            }
        }
        bSend = FALSE;
    }

	return TRUE;		//发送成功
}

/**
 * 客户端退出
 */
void ExitClient(void)
{
//	DeleteCriticalSection(&cs);
//	CloseHandle(handleThread);
	closesocket(sClient);
	WSACleanup();
}

/**
 * 显示连接服务器失败信息
 */
void ShowConnectMsg(BOOL bSuc)
{
	if (bSuc)
	{
		cout << "* Succeed to connect server! *" << endl;
	}
	else
    {
		cout << "* Client has to exit! *" << endl;
	}
}



猜你喜欢

转载自blog.csdn.net/u011599639/article/details/69383088