功能说明:服务端设定客户端连接个数,达到上限不可用。客户端可以单独同服务端进行通信交流。
一、服务端代码:MyServer.cpp
// MyServer.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
#include<stdlib.h>
#include<winsock2.h>//引用头文件
#pragma comment(lib,"ws2_32.lib")//引用库文件
//最大连接数
#define g_MaxConnect 20
int g_Connect = 0;
struct sock_params
{
SOCKET hsock;
int nSockIndex;
};
//线程实现函数
DWORD WINAPI threadpro(LPVOID pParam)
{
sock_params* sockPam = (sock_params*)pParam;
SOCKET hsock = sockPam->hsock;
int nSockIndex = sockPam->nSockIndex;
char aIndex[4];
_itoa_s(nSockIndex, aIndex, 10);
char buffer[1024];
char sendBuffer[1024];
if(hsock != INVALID_SOCKET)
{
cout<<"客户端 "<< nSockIndex << " 加入服务器!" <<endl;
}
while(1)
{
//循环接收发送的内容
int num = recv(hsock,buffer,1024,0);//阻塞函数,等待接收内容
if(num > 0)
{
cout << "客户端 "<< nSockIndex << ": "<< buffer<<endl;
memset(sendBuffer,0,1024);
char strValue[30] = "服务器 ";
strcat_s(strValue, aIndex);
strcpy_s(sendBuffer,strValue);
char strSpace[30] = " 收到数据如下: ";
strcat_s(sendBuffer,strSpace);
strcat_s(sendBuffer,buffer);
int ires = send(hsock,sendBuffer,sizeof(sendBuffer),0);//回送消息
//cout<<"Send to Client: "<<sendBuffer<<endl;
}
else
{
cout<<"客户端 "<< nSockIndex <<" 关闭!" <<endl;
//cout<<"Server Process " << nSockIndex << " Closed"<<endl;
return 0;
}
}
return 0;
}
//主函数
void main()
{
WSADATA wsd;//定义WSADATA对象
WSAStartup(MAKEWORD(2,2),&wsd);
SOCKET m_SockServer;
sockaddr_in serveraddr;
sockaddr_in serveraddrfrom;
SOCKET m_Server[g_MaxConnect];
serveraddr.sin_family = AF_INET;//设置服务器地址
serveraddr.sin_port = htons(4600);//设置端口号
serveraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
m_SockServer = socket(AF_INET,SOCK_STREAM,0);
int nStatus = bind(m_SockServer,(sockaddr*)&serveraddr,sizeof(serveraddr));
if (nStatus == 0)
{
cout << "服务端启动成功!" <<endl;
}
else
{
cout << "服务端启动失败!" <<endl;
return;
}
int iLisRet = 0;
int len = sizeof(sockaddr);
while(1)
{
iLisRet = listen(m_SockServer,0);//进行监听
m_Server[g_Connect] = accept(m_SockServer,(sockaddr*)&serveraddrfrom,&len);
//同意连接
if(m_Server[g_Connect] != INVALID_SOCKET)
{
if(g_Connect > g_MaxConnect-1)
{
char WarnBuf[50]="客户端连接个数:超限!";
int ires = send(m_Server[g_Connect],WarnBuf,sizeof(WarnBuf),0);
}
else
{
char cIndex[4];
_itoa_s(g_Connect, cIndex, 10);
char buf[50]="你的服务端ID: ";
strcat_s(buf, cIndex);
int ires = send(m_Server[g_Connect],buf,sizeof(buf),0);//发送字符过去
//cout << buf <<endl;
HANDLE m_Handel; //线程句柄
DWORD nThreadId=0; //线程ID
sock_params sockPam;
sockPam.hsock = m_Server[g_Connect];
sockPam.nSockIndex = g_Connect;
m_Handel = (HANDLE)::CreateThread(NULL,0,threadpro,&sockPam,0,&nThreadId);
CloseHandle(m_Handel);
}
++g_Connect;
}
}
WSACleanup();
}
二、客户端:MyClient.cpp
// MyClient.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>
using namespace std;
#include<stdlib.h>
#include<stdio.h>
#include"winsock2.h"
#include<time.h>
#pragma comment(lib,"ws2_32.lib")
void main()
{
WSADATA wsd;//定义WSADATA对象
WSAStartup(MAKEWORD(2,2),&wsd);
SOCKET m_SockClient;
sockaddr_in clientaddr;
clientaddr.sin_family = AF_INET; //设置服务器地址
clientaddr.sin_port = htons(4600); //设置服务器端口号
clientaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
m_SockClient = socket(AF_INET,SOCK_STREAM,0);
if (m_SockClient == INVALID_SOCKET)
{
printf("Sock 初始化失败: %d\n", WSAGetLastError());
WSACleanup();
return ;
}
// 获取发送缓冲区和接送缓冲区大小
{
int optlen = 0;
int optval = 0;
optlen = sizeof(optval);
getsockopt(m_SockClient, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);
printf("send buf len is %d\n",optval); //64位 默认发送缓冲区64k
getsockopt(m_SockClient, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);
printf("Recv buf len is %d\n",optval); //64位 默认接收缓冲区64k
}
// 设定发送缓冲区大小
// optval = 1024 * 2;
// setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);
int nSuccess = connect(m_SockClient,(sockaddr*)&clientaddr,sizeof(clientaddr));//连接超时
if (nSuccess == 0)
{
cout<<"连接服务器成功!"<<endl;
}
else
{
cout<<"连接服务器失败!"<<endl;
return;
}
char buffer[1024];
char inBuf[1024];
int num = 0;
num = recv(m_SockClient,buffer,1024,0);//阻塞
if(num > 0)
{
cout << /*"Receive from server:"<<*/ buffer<<endl;
char* pResult = strstr(buffer, "超限");
if (pResult != NULL)
{
cout<<"服务器连接个数超限,不可用!"<<endl;
system("pause");
return;
}
while(1)
{
cout<<"请输入要发送的消息:"<<endl;
cin >> inBuf;
if(strcmp(inBuf,"exit") == 0)
{
send(m_SockClient,inBuf,sizeof(inBuf),0);//发送退出指令
return;
}
int send_len = send(m_SockClient,inBuf,sizeof(inBuf),0);
if (send_len < 0)
{
cout << "发送失败!请检查服务器是否开启!" << endl;
system("pause");
return;
}
int recv_len = recv(m_SockClient,buffer,1024,0);//接收客户端发送过来的数据
if(recv_len>=0)
{
cout<< buffer <<endl;
}
}
}
}
资源文件:https://download.csdn.net/download/m0_37251750/36164589
三、 额外代码:加入线程锁或者识别消息类型
(可以跳过)
// 线程锁
class CAutoLock
{
public:
CAutoLock();
~CAutoLock();
void Lock();
void UnLock();
private:
CRITICAL_SECTION m_Section;
};
#endif
CAutoLock::CAutoLock()
{
InitializeCriticalSection(&m_Section);
}
CAutoLock::~CAutoLock()
{
DeleteCriticalSection(&m_Section);
}
void CAutoLock::Lock()
{
EnterCriticalSection(&m_Section);
}
void CAutoLock::UnLock()
{
LeaveCriticalSection(&m_Section);
}
#pragma once
#define INVALID_MSG 0 // 无效的消息
#define MSG_FOLDER_INFO 1 // 文件目录信息消息
#define MSG_CREATE_FOLDER 2 // 创建文件目录消息
#define MSG_DELETE_FOLDER 3 // 删除文件目录消息
#define MSG_RENAME_FOLDER 4 // 重命名文件目录消息
class Message
{
public:
struct MsgHead //头消息
{
int msgId; //消息标识
MsgHead(int msg = INVALID_MSG) :msgId(msg) {
};
};
struct MsgFolderInfo :public MsgHead
{
char chProjCode[_MAX_FNAME];
MsgFolderInfo() :MsgHead(MSG_FOLDER_INFO) {
}
};
struct MsgCreateFolder :public MsgHead
{
char chFolder[_MAX_DIR];
MsgCreateFolder() :MsgHead(MSG_CREATE_FOLDER) {
}
};
struct MsgDeleteFolder :public MsgHead
{
char chFolder[_MAX_DIR];
MsgDeleteFolder() :MsgHead(MSG_DELETE_FOLDER) {
}
};
struct MsgRenameFolder :public MsgHead
{
char chOldFolder[_MAX_DIR];
char chNewFolder[_MAX_DIR];
MsgRenameFolder() :MsgHead(MSG_RENAME_FOLDER) {
}
};
};
// 使用
CAutoLock g_lock;
bool ProcessConnection(SOCKET clientSocket)
{
int nLen = 0;
int nSize = 0;
nLen = recv(clientSocket, (char*)&nSize, sizeof(int), 0);
if (nLen <= 0)
{
hcDebugLog(_T("ZLY:接收Msg长度失败 \n"));
cout << "接收Msg长度失败" << WSAGetLastError() << endl;
return false;
}
char buff[MAX_PACK_SIZE];
memset(buff, 0x00, sizeof(char)*MAX_PACK_SIZE);
nLen = recv(clientSocket, buff, nSize, 0);
if (nLen <= 0)
{
if (SOCKET_ERROR == nLen)
{
hcDebugLog(_T("ZLY:接收Msg数据失败 \n"));
cout << "接收Msg数据失败" << WSAGetLastError() << endl;
}
else
{
hcDebugLog(_T("ZLY:客户端关闭连接 \n"));
cout << "客户端关闭连接" << endl;
}
return false;
}
Message::MsgHead* msgHead;
msgHead = (Message::MsgHead*)&buff;
switch(msgHead->msgId)
{
case MSG_FOLDER_INFO:
{
}
break;
case MSG_CREATE_FOLDER:
{
}
break;
default:
{
cout<<"无效消息"<<endl;
return false;
}
}
return true;
}
DWORD WINAPI CreateClientThread(LPVOID pData)
{
SocketData* pSocketData = (SocketData*)pData;
if (pSocketData == NULL)
{
HY_SAFE_DELETE(pSocketData);
return -1;
}
// 循环监听客户端发送消息
while (pSocketData->serverSocket.ProcessConnection(pSocketData->clientSocket))
{
}
// 处理客户端非正常关闭的情况
g_lock.Lock();
for (auto iter = g_vecProjCode.begin(); iter != g_vecProjCode.end(); iter++)
{
if (pSocketData->clientSocket == iter->socket)
{
g_vecProjCode.erase(iter);
break;
}
}
if (g_vecProjCode.size() == 0)
{
// 关闭套接字
pSocketData->serverSocket.CloseSocket();
exit(0);
}
g_lock.UnLock();
HY_SAFE_DELETE(pSocketData);
return 0;
}
bool MessageUtils::SendMsg(SOCKET socket, Message::MsgHead* pMsg)
{
int nSize = 0;
switch (pMsg->msgId)
{
case MSG_FOLDER_INFO:
nSize = sizeof(Message::MsgFolderInfo);
break;
case MSG_CREATE_FOLDER:
nSize = sizeof(Message::MsgCreateFolder);
break;
case MSG_DELETE_FOLDER:
nSize = sizeof(Message::MsgDeleteFolder);
break;
case MSG_RENAME_FOLDER:
nSize = sizeof(Message::MsgRenameFolder);
break;
default:
return false;
}
// 发送消息长度
if (sizeof(nSize) != send(socket, (char*)&nSize, sizeof(nSize), 0))
return false;
// 发送消息数据
if (nSize != send(socket, (char*)pMsg, nSize, 0))
return false;
return true;
}