C++实现多人聊天室

本文实例为大家分享了C++实现多人聊天室的具体代码,供大家参考,具体内容如下

UDP

服务端代码:

// Test_Console.cpp : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

#include <iostream>

#include <WinSock2.h>

#include <WS2tcpip.h>

#include <Windows.h>

#include <thread>

#include <cstdio>

using namespace std;

#pragma region 全局变量

SOCKET server; // 服务端套接字

sockaddr_in sai_server; // 服务端信息(ip、端口)

// 消息格式

struct umsg {

int type; // 协议(1:加入 2:退出 3:发消息)

char name[64]; // 用户名字

char text[512]; // 文本信息

};

// 客户端链表

typedef struct ucnode {

sockaddr_in addr; // 客户端的地址和端口号

umsg msg; // 客户端传来的消息

ucnode* next;

} *ucnode_t;

#pragma endregion

#pragma region 依赖函数

// 链表插入数据

ucnode* insertNode(ucnode* head, sockaddr_in addr,umsg msg) {

ucnode* newNode = new ucnode();

newNode->addr = addr;

newNode->msg = msg;

ucnode* p = head;

if (p == nullptr) {

head = newNode;

}

else {

while (p->next != nullptr) {

p = p->next;

}

p->next = newNode;

}

return head;

}

// 链表删除数据

ucnode* deleteNode(ucnode* head, umsg msg) {

ucnode* p = head;

if (p == nullptr) {

return head;

}

if (strcmp(p->msg.name, msg.name) == 0){

head = p->next;

delete p;

return head;

}

while (p->next != nullptr && strcmp(p->next->msg.name, msg.name) != 0) {

p = p->next;

}

if (p->next == nullptr) {

return head;

}

ucnode* deleteNode = p->next;

p->next = deleteNode->next;

delete deleteNode;

return head;

}

#pragma endregion

int main()

{

cout << "我是服务端" << endl;

// 初始化 WSA ,激活 socket

WSADATA wsaData;

if (WSAStartup(

MAKEWORD(2, 2), // 规定 socket 版本为 2.2

&wsaData // 接收关于套接字的更多信息

)) {

cout << "WSAStartup failed : " << GetLastError() << endl;

}

// 初始化 socket、服务器信息

server = socket(

AF_INET, // IPV4

SOCK_DGRAM, // UDP

0 // 不指定协议

);

sai_server.sin_addr.S_un.S_addr = 0; // IP地址

sai_server.sin_family = AF_INET; // IPV4

sai_server.sin_port = htons(8090); // 传输协议端口

// 本地地址关联套接字

if (bind(

server, // 要与本地地址绑定的套接字

(sockaddr*)&sai_server, // 用来接收客户端消息的 sockaddr_in 结构体指针

sizeof(sai_server)

)) {

cout << "bind failed : " << GetLastError() << endl;

WSACleanup();

}

// 初始化客户端链表

ucnode* listHead = new ucnode();

listHead->next = nullptr;

ucnode* lp = listHead;

// 监听消息

while (1) {

// 接收来自客户端的消息

umsg msg;

int len_client = sizeof(sockaddr);

recvfrom(

server, // 本地套接字

(char*)&msg, // 存放接收到的消息

sizeof(msg),

0, // 不修改函数调用行为

(sockaddr*)&sai_server, // 接收客户端的IP、端口

&len_client // 接收消息的长度,必须初始化,否则默认为0 收不到消息

);

// sin_addr 转 char[](char[] 转 sin_addr 使用 inet_top)

char arr_ip[20];

inet_ntop(AF_INET, &sai_server.sin_addr, arr_ip, 16);

// 处理消息(1:用户登录,2:用户退出,3:普通会话)

switch (msg.type) {

case 1:

insertNode(listHead, sai_server, msg);

cout << "[" << arr_ip << ":" << ntohs(sai_server.sin_port) << "] " << msg.name << ":" << "---登录---" << endl;

break;

case 2:

deleteNode(listHead, msg);

cout << "[" << arr_ip << ":" << ntohs(sai_server.sin_port) << "] " << msg.name << ":" << "---退出---" << endl;

break;

case 3:

cout << "[" << arr_ip << ":" << ntohs(sai_server.sin_port) << "] " << msg.name << ":" << msg.text << endl;

// 更新 msg.text

lp = listHead;

while (lp) {

if (strcmp(lp->msg.name, msg.name) == 0) {

strncpy(lp->msg.text, msg.text, sizeof(msg.text));

lp->msg.type = msg.type;

break;

}

lp = lp->next;

}

// 向其他客户端广播(除自己之外)

lp = listHead;

while (lp) {

if (strcmp(lp->msg.name,"") != 0 && strcmp(lp->msg.name, msg.name) != 0) {

sendto(

server, // 本地套接字

(char*)&msg, // 消息结构体

sizeof(msg),

0, // 不修改函数调用行为

(sockaddr*) & lp->addr, // 目标客户端地址

sizeof(lp->addr)

);

}

lp = lp->next;

}

break;

}

}

// 禁用 socket

WSACleanup();

getchar();

return 0;

}

客户端代码:

// Test_Console_2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

//

#include <iostream>

#include <WinSock2.h>

#include <WS2tcpip.h>

#include <Windows.h>

#include <thread>

#include <cstdio>

#include <string>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

#pragma region 全局变量

SOCKET client; // 客户端套接字

sockaddr_in sai_client; // 存放客户端地址、端口

sockaddr_in sai_server; // 存放服务端发送的消息

// 发送和接收的信息体

struct umsg {

int type; // 协议(1:登录,2:退出,3:发消息)

char name[64]; // 用户名字

char text[512]; // 文本

};

#pragma endregion

#pragma region 依赖函数

// 监听服务器消息

void recvMessage()

{

while (1){

umsg msg;

int len_server = sizeof(sockaddr);

int len = recvfrom(client, (char*)&msg,sizeof(msg),0,(sockaddr*)&sai_server,&len_server);

cout << msg.name << ": " << msg.text << endl;

}

}

#pragma endregion

int main()

{

cout << "我是客户端" << endl;

// 初始化 WSA ,激活 socket

WSADATA wsaData;

if (WSAStartup(

MAKEWORD(2, 2), // 规定 socket 版本

&wsaData // 接收 socket 的更多信息

)) {

cout << "WSAStartup failed : " << GetLastError() << endl;

}

// 初始化 socket、客户端信息

client = socket(

AF_INET, // IPV4

SOCK_DGRAM, // UDP

0 // 不指定协议

);

sai_client.sin_family = AF_INET; // IPV4

inet_pton(AF_INET, "192.168.1.105", &sai_client.sin_addr); // 服务器 IP地址

sai_client.sin_port = htons(8090); // 端口

// 输入用户名

string name;

getline(cin, name);

// 发送登录消息

umsg msg;

msg.type = 1;

strncpy_s(msg.name, sizeof(msg.name), name.c_str(), 64);

strncpy_s(msg.text, sizeof(msg.text), "", 512);

sendto(

client, // 本地套接字

(char*)&msg, // 发送的消息

sizeof(msg),

0, // 不修改函数调用行为

(sockaddr*) & sai_client, // 消息目标

sizeof(sai_client)

);

// 接收服务器消息

HANDLE h_recvMes = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)recvMessage, 0, 0, 0);

if (!h_recvMes) { cout << "CreateThread failed :" << GetLastError() << endl; }

// 发送消息

while (1) {

string content;

getline(cin, content);

// 如果是退出消息

if (content == "quit") {

msg.type = 2;

sendto(client, (char*)&msg, sizeof msg, 0, (struct sockaddr*) & sai_client, sizeof(sai_client));

closesocket(client);

WSACleanup();

return 0;

}

// 如果是会话消息

msg.type = 3;

strncpy_s(msg.text, sizeof(msg.text), content.c_str(), 512);

sendto(

client, // 本地套接字

(char*)&msg, // 要发送的消息

sizeof(msg),

0, // 不修改函数调用行为

(sockaddr*) & sai_client, // 发送目标

sizeof(sai_client)

);

}

getchar();

return 0;

}

效果图:

TCP

服务器代码:

// Test_Console.cpp : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

#include <iostream>

#include <WinSock2.h>

#include <WS2tcpip.h>

#include <Windows.h>

#include <thread>

#include <cstdio>

using namespace std;

#pragma region 全局变量

SOCKET server; // 本地套接字

sockaddr_in sai_server; // 存放服务器IP、端口

// 消息格式

struct umsg {

int type; // 协议(1:登录,2:退出,3:发消息)

char name[64]; // 用户名字

char text[512]; // 文本信息

};

// 客户端信息

struct clientInfo {

SOCKET client;

sockaddr_in saddr;

umsg msg;

};

// 客户端链表

typedef struct ucnode {

clientInfo cInfo;

ucnode* next;

} *ucnode_t;

ucnode* listHead; // 客户端链表头

ucnode* lp; // 客户端链表指针

#pragma endregion

#pragma region 依赖函数

// 链表插入数据

ucnode* insertNode(ucnode* head,SOCKET client, sockaddr_in addr, umsg msg) {

ucnode* newNode = new ucnode();

newNode->cInfo.client = client;

newNode->cInfo.saddr = addr;

newNode->cInfo.msg = msg;

ucnode* p = head;

if (p == nullptr) {

head = newNode;

}

else {

while (p->next != nullptr) {

p = p->next;

}

p->next = newNode;

}

return head;

}

// 链表删除数据

ucnode* deleteNode(ucnode* head, SOCKET client) {

ucnode* p = head;

if (p == nullptr) {

return head;

}

if (p->cInfo.client == client) {

head = p->next;

delete p;

return head;

}

while (p->next != nullptr && p->next->cInfo.client != client) {

p = p->next;

}

if (p->next == nullptr) {

return head;

}

ucnode* deleteNode = p->next;

p->next = deleteNode->next;

delete deleteNode;

return head;

}

// 接收客户端消息(某个)

void recvMessage(PVOID pParam) {

clientInfo* cInfo = (clientInfo*)pParam;

while (1) {

// 接收来自客户端的消息

umsg msg;

int len_client = sizeof(sockaddr);

int ret_recv = recv(

cInfo->client, // 本地套接字

(char*)&msg, // 存放接收的消息

sizeof(msg), // 消息大小

0 // 不修改函数调用行为

);

if (ret_recv <= 0) { cout << msg.name << "断开连接: " << GetLastError() << endl; break; }

cInfo->msg = msg;

// sin_addr 转 char[](char[] 转 sin_addr 使用 inet_top)

char arr_ip[20];

inet_ntop(AF_INET, &cInfo->saddr.sin_addr, arr_ip, 16);

// 处理消息(1:登录,2:退出,3:会话)

switch (cInfo->msg.type) {

case 1:

// 插入数据到链表

insertNode(listHead,cInfo->client, cInfo->saddr,cInfo->msg);

// 打印消息

cout << "[" << arr_ip << ":" << ntohs(cInfo->saddr.sin_port) << "] " << msg.name << ":" << "---登录---" << endl;

break;

case 2:

// 从链表删除数据

deleteNode(listHead, /*cInfo->msg*/cInfo->client);

// 打印消息

cout << "[" << arr_ip << ":" << ntohs(cInfo->saddr.sin_port) << "] " << msg.name << ":" << "---退出---" << endl;

break;

case 3:

// 打印消息

cout << "[" << arr_ip << ":" << ntohs(cInfo->saddr.sin_port) << "] " << msg.name << ":" << cInfo->msg.text << endl;

// 向其他客户端广播(除自己之外)

lp = listHead;

while (lp) {

if (strcmp(lp->cInfo.msg.name, "") != 0 && strcmp(lp->cInfo.msg.name, cInfo->msg.name) != 0) {

send(

lp->cInfo.client, // 本地套接字

(char*)&cInfo->msg, // 发送的消息

sizeof(cInfo->msg), // 消息大小

0 // 不指定调用方式

);

int error_send = GetLastError();

if (error_send != 0) { cout << "send failed:" << error_send << endl; }

}

lp = lp->next;

}

break;

}

}

}

#pragma endregion

int main()

{

cout << "我是服务端" << endl;

// 初始化 WSA ,激活 socket

WSADATA wsaData;

if (WSAStartup(

MAKEWORD(2, 2), // 规定 socket 版本为 2.2

&wsaData // 接收关于套接字的更多信息

)) {

cout << "WSAStartup failed : " << GetLastError() << endl;

}

// 初始化 socket、服务器信息

server = socket(

AF_INET, // IPV4

SOCK_STREAM, // TCP

0 // 不指定协议

);

sai_server.sin_addr.S_un.S_addr = 0; // IP地址

sai_server.sin_family = AF_INET; // IPV4

sai_server.sin_port = htons(8090); // 传输协议端口

// 本地地址关联套接字

if (bind(

server, // 要与本地地址绑定的套接字

(sockaddr*)&sai_server, // 用来接收客户端消息的 sockaddr_in 结构体指针

sizeof(sai_server)

)) {

cout << "bind failed : " << GetLastError() << endl;

WSACleanup();

}

// 套接字进入监听状态

listen(

server, // 本地套接字

SOMAXCONN // 挂起连接队列的最大长度,SOMAXCONN:最大合理值

);

// 初始化客户端链表

listHead = new ucnode();

listHead->next = nullptr;

lp = listHead;

// 接收消息

while (1) {

// 接收登录消息(首次连接是触发,之后发送消息不触发)

clientInfo* cInfo = new clientInfo();

int len_client = sizeof(sockaddr);

cInfo->client = accept(server, (sockaddr*) &cInfo->saddr, &len_client);

if (GetLastError() != 0) { continue; }

// 接收登录者的消息(每个客户端对应一个线程)

HANDLE h_recvMes = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)recvMessage, cInfo, 0, 0);

if (!h_recvMes) { cout << "CreateThread failed :" << GetLastError() << endl; }

}

// 禁用 socket

WSACleanup();

getchar();

return 0;

}

客户端代码:

// Test_Console_2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

//

#include <iostream>

#include <WinSock2.h>

#include <WS2tcpip.h>

#include <Windows.h>

#include <thread>

#include <cstdio>

#include <string>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

#pragma region 全局变量

SOCKET client; // 本地套接字

sockaddr_in sai_client; // 存放客户端IP地址、端口

// 消息格式

struct umsg {

int type; // 协议(1:登录,2:退出,3:发消息)

char name[64]; // 用户名字

char text[512]; // 文本

};

#pragma endregion

#pragma region 依赖函数

// 监听服务器消息

void recvMessage()

{

while (1){

umsg msg;

int ret_recv = recv(

client, // 本地套接字

(char*)&msg, // 存放接收的消息

sizeof(msg), // 消息大小

0 // 不指定调用方式

);

if (ret_recv <= 0) { cout << "recv failed: " << GetLastError() << endl; break; }

// 打印消息

cout << msg.name << ": " << msg.text << endl;

}

}

#pragma endregion

int main()

{

cout << "我是客户端" << endl;

// 初始化 WSA ,激活 socket

WSADATA wsaData;

if (WSAStartup(

MAKEWORD(2, 2), // 规定 socket 版本

&wsaData // 接收 socket 的更多信息

)) {

cout << "WSAStartup failed : " << GetLastError() << endl;

}

// 初始化 socket、客户端信息

client = socket(

AF_INET, // IPV4

SOCK_STREAM, // TCP

0 // 不指定协议

);

sai_client.sin_family = AF_INET; // IPV4

inet_pton(AF_INET, "192.168.1.100", &sai_client.sin_addr); // 服务器 IP地址

sai_client.sin_port = htons(8090); // 端口

// 连接服务器

int ret_connect = connect(

client, // 本地套接字

(sockaddr*) &sai_client, // 目标

sizeof(sai_client)

);if (ret_connect != 0) { cout << "connect failed:" << GetLastError() << endl; }

// 输入用户名

umsg msg;

msg.type = 1;

string name;

getline(cin, name);

strncpy_s(msg.name, sizeof(msg.name), name.c_str(), 64);

strncpy_s(msg.text, sizeof(msg.text), "", 512);

// 发送登录消息

send(

client, // 本地套接字

(char*)&msg, // 发送的消息

sizeof(msg), // 消息大小

0 // 不指定调用方式

);

int error_send = GetLastError();

if (error_send != 0) { cout << "send failed:" << error_send << endl; }

// 接收服务器消息

HANDLE h_recvMes = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)recvMessage, 0, 0, 0);

if (!h_recvMes) { cout << "CreateThread failed :" << GetLastError() << endl; }

// 发送消息

while (1) {

string content;

getline(cin, content);

// 退出消息

if (content == "quit") {

msg.type = 2;

send(

client, // 本地套接字

(char*)&msg, // 发送的消息

sizeof(msg), // 消息大小

0 // 不指定调用方式

);

error_send = GetLastError();

if (error_send != 0) { cout << "send failed:" << error_send << endl; }

closesocket(client);

WSACleanup();

return 0;

}

// 会话消息

msg.type = 3;

strncpy_s(msg.text, sizeof(msg.text), content.c_str(), 512);

send(

client, // 本体套接字

(char*)&msg, // 发送的消息

sizeof(msg), // 消息大小

0 // 不指定调用方式

);

error_send = GetLastError();

if (error_send != 0) { cout << "send failed:" << error_send << endl; }

}

getchar();

return 0;

}

效果图:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

以上是 C++实现多人聊天室 的全部内容, 来源链接: utcz.com/p/246355.html

回到顶部