此代码是一个基于Windows平台的简单的聊天室服务器端程序,使用了Winsock2库和多线程技术实现了多个客户端与服务器进行通信。其中,服务器端通过处理接收到的消息,实现了昵称注册、群组建立和加入等功能。以下是代码的详细解释:

  1. 头文件:

#include <winsock2.h> //Windows下的socket头文件 #include <stdio.h> #include <stdlib.h> #include <windows.h> #include #include #include <string.h> //字符串处理头文件 #define msg_Length 100 //消息最大长度 #define NUMBER 256 //最大连接数 #pragma comment(lib,"WS2_32.lib") //链接WS2_32.lib库文件 using namespace std;

  1. 全局变量:

int snum = 0;//建立的套接字数量 int SOCKETS[NUMBER];//套接字们

int Q[10];//存放群组信息 int q=0;//群组数量 char* names[10]; //所有注册用户的姓名 int u=0;//user数量

HANDLE Process[NUMBER];//线程数组

其中,snum表示当前建立的套接字数量,SOCKETS数组用于保存所有建立的套接字。Q和q用于存放群组信息和群组数量,names和u用于保存所有注册用户的姓名和用户数量。Process数组用于保存所有线程的句柄。

  1. 线程执行函数:

DWORD WINAPI processing(LPVOID e) { int chat_socket = ((int)e); //将接收到的套接字转换成int类型 int str_len = 0, i; char msg[msg_Length]; //接收到的消息 char u_msg[msg_Length]; //需要发送的消息 while ((str_len = recv(chat_socket, msg, sizeof(msg), 0)) != -1) { //接收客户端发来的消息 if(msg[0]=='#') { //通过信息头判断消息类型是昵称 int fla=1; cout<<"查询姓名信息"<<msg<<endl; //与服务器端数据比较,检验是否已经存在的昵称 for (int o=0; o<10; o++) { char* p=names[o]; if (strcmp(p,msg)==0) { fla=0;//存在的话就标识一下 break; } }

		if(fla==0) {	//如果存在,发出错误识别码,告知客户端已经存在这个昵称了
			sprintf (u_msg,"@%s",msg);//@:昵称已经存在
			//发送出去
			for (int i = 0; i < snum; i++) 
				send(SOCKETS[i], msg, str_len, 0);
		} else {	//如果不存在,将昵称存档,并告知客户端可以使用
			char b[20]= {""};
			strcpy(b,msg); //昵称不存在则存档
			names[u]=b;//存入数据中
			u++;
			sprintf (u_msg,"*%s",msg);//*:告知客户端昵称可以使用 
			for (int i = 0; i < snum; i++) 
				send(SOCKETS[i], msg, str_len, 0);
		}
	}
	else if(msg[0]=='('&&msg[1]=='G'&&msg[2]=='r') {	//群组信息
		cout<<"建立群组:"<<msg[6]<<endl;
		for (int i = 0; i < snum; i++) 
				send(SOCKETS[i], msg, str_len, 0);
		cout<<"转发群组代号\n";
	} 
	else if(msg[0]=='q'&&msg[1]=='u'&&msg[2]=='n') {	//群组信息
		cout<<"核对群组信息"<<msg[3]<<endl;
		for (int u=0; u<10; u++) {
			if (msg[3]==Q[u]) {
				sprintf (msg, "GE");
				for (int i = 0; i < snum; i++) 
					send(SOCKETS[i], msg, str_len, 0);
				break;
			}
		}
		Q[q]=msg[3];
		q++;
	} 
	else if(msg[0]=='j'&&msg[1]=='o'&&msg[2]=='i') {	//群组信息
		cout<<"核对群组信息"<<msg[3]<<endl;
		int flag=0;
		for (int u=0; u<10; u++) {
			//cout<<"!@"<<Q[u]<<endl;
			if (msg[3]==Q[u]) {
				flag=1;
				break;
			}
		}
		if(flag==1) {
			sprintf (msg, "GY");//GY群组存在
			for (int i = 0; i < snum; i++) 
				send(SOCKETS[i], msg, str_len, 0);
		} else {
			sprintf (msg, "GN");//GN群组不存在
			for (int i = 0; i < snum; i++) 
				send(SOCKETS[i], msg, str_len, 0);
		}
	} 
	else {	//普通消息,群发给所有客户端
		for (int i = 0; i < snum; i++) 
			send(SOCKETS[i], msg, str_len, 0);
		cout<<"群发成功"<<endl;
	}

}

printf("客户端退出:%d\n", GetCurrentThreadId());
// 从SOCKETS数组中删除此套接字
for (i = 0; i < snum; i++) {
	if (chat_socket == SOCKETS[i]) {
		while (i++ < snum - 1)
			SOCKETS[i] = SOCKETS[i + 1];
		break;
	}
}
snum--;
// 关闭同客户端的连接
closesocket(chat_socket);
return -1;

}

该函数的作用是处理接收到的消息。根据消息头的不同,分别实现了昵称注册、群组建立和加入等功能,以及普通消息的群发。最后关闭套接字并返回线程函数。

  1. 主函数:

int main() { for(int y=0; y<10; y++) { names[y]="xxx";//初始化昵称 } //初始化WSA WSADATA wsaData; WORD banben = MAKEWORD(2, 2); WSAStartup(banben, &wsaData); //请求socket

//创建套接字
SOCKET serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serv_sock == INVALID_SOCKET){
	cout<<"连接失败"<<endl;
    WSACleanup();
    exit(1);
}

//sockaddr
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);//端口设置
sin.sin_addr.S_un.S_addr = INADDR_ANY;	//本地地址

//将套接字绑定到地址 
if (bind(serv_sock, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR){
	cout<<"套接字绑定失败"<<endl;
    WSACleanup();
    exit(1);
}

//监听
if (listen(serv_sock, NUMBER) == SOCKET_ERROR){
	cout<<"端口监听失败"<<endl;
    WSACleanup();
    exit(1);
} 
cout<<"开始监听:"<<endl;
sockaddr_in addr;
int nAddrLen = sizeof(addr);
DWORD clientID;//此线程的id句柄,用于标记用户
SOCKET chat_socket;

while (1) {
	cout<<"等待新连接"<<endl;
	// 接受一个新连接
	chat_socket = accept(serv_sock, (SOCKADDR*)&addr, &nAddrLen);
	if (chat_socket == INVALID_SOCKET) {
		cout<<"接收失败"<<endl;
		continue;
	}
	Process[snum] = CreateThread(NULL,NULL,processing,(void*)&chat_socket,0,&clientID);
	//参数:安全性、堆栈大小(默认)、线程函数、函数参数、执行延迟、线程ID--返回值 
	SOCKETS[snum++] = chat_socket;
	cout<<"接收到来自"<<inet_ntoa(addr.sin_addr)<<"的连接"<<endl;
}

for (int i = 0; i < snum; i++) {
	CloseHandle(Process[i]);
}
// 关闭监听套节字
closesocket(serv_sock);
// 释放WSA
WSACleanup();
return 0;

}

主函数首先初始化昵称,然后创建套接字并绑定到本地地址,开始监听端口。当有新的连接请求时,创建一个新的线程处理该连接。接着循环等待连接请求,直到程序退出。最后关闭套接字并释放WSA资源。

以上就是该聊天室服务器端程序的详细解释

#include winsock2h#include stdioh#include stdlibh#include windowsh#include iostream#include string#include stringh#define msg_Length 100#define NUMBER 256#pragma commentlibWS2_32libusing namespace std

原文地址: https://www.cveoy.top/t/topic/eFRZ 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录