#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
此代码是一个基于Windows平台的简单的聊天室服务器端程序,使用了Winsock2库和多线程技术实现了多个客户端与服务器进行通信。其中,服务器端通过处理接收到的消息,实现了昵称注册、群组建立和加入等功能。以下是代码的详细解释:
- 头文件:
#include <winsock2.h> //Windows下的socket头文件
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include
- 全局变量:
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数组用于保存所有线程的句柄。
- 线程执行函数:
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;
}
该函数的作用是处理接收到的消息。根据消息头的不同,分别实现了昵称注册、群组建立和加入等功能,以及普通消息的群发。最后关闭套接字并返回线程函数。
- 主函数:
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资源。
以上就是该聊天室服务器端程序的详细解释
原文地址: https://www.cveoy.top/t/topic/eFRZ 著作权归作者所有。请勿转载和采集!