用socket实现一个即时通讯程序 要求实现私聊 群聊 发送文件功能 c/c++实现
时间: 2023-07-14 22:14:13 浏览: 166
用VC++写的socket通讯程序,已经编译通过,使用时去掉文件只读属性,编译即可.rar
以下是一个简单的 C++ 实现的即时通讯程序,包含私聊、群聊和发送文件功能,使用了 socket 编程和多线程技术。
```
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <mutex>
#include <fstream>
#include <winsock2.h>
using namespace std;
const int MAX_CLIENTS = 10;
const int BUFFER_SIZE = 1024;
struct Client {
SOCKET socket;
string name;
};
vector<Client> clients;
mutex mtx;
void broadcast(string message, SOCKET sender = INVALID_SOCKET) {
mtx.lock();
for (auto& client : clients) {
if (client.socket != sender) {
send(client.socket, message.c_str(), message.size() + 1, 0);
}
}
mtx.unlock();
}
void send_file(SOCKET receiver, string file_path) {
ifstream file(file_path, ios::binary);
if (!file) {
cerr << "Failed to open file: " << file_path << endl;
return;
}
file.seekg(0, ios::end);
int file_size = file.tellg();
char* buffer = new char[file_size];
file.seekg(0, ios::beg);
file.read(buffer, file_size);
file.close();
send(receiver, (char*)&file_size, sizeof(file_size), 0);
send(receiver, buffer, file_size, 0);
delete[] buffer;
}
void handle_client(SOCKET client_socket) {
char buffer[BUFFER_SIZE];
int bytes_received;
string name;
while (true) {
bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0);
if (bytes_received == SOCKET_ERROR || bytes_received == 0) {
cerr << "Client disconnected." << endl;
break;
}
string message(buffer, bytes_received);
if (message.substr(0, 5) == "/name") {
name = message.substr(6);
cout << "Client connected: " << name << endl;
broadcast(name + " has joined the chat.", client_socket);
Client new_client = { client_socket, name };
mtx.lock();
clients.push_back(new_client);
mtx.unlock();
}
else if (message.substr(0, 6) == "/whisp") {
size_t pos = message.find(' ');
if (pos == string::npos) {
continue;
}
string receiver_name = message.substr(7, pos - 7);
string private_message = message.substr(pos + 1);
for (auto& client : clients) {
if (client.name == receiver_name) {
send(client.socket, private_message.c_str(), private_message.size() + 1, 0);
break;
}
}
}
else if (message.substr(0, 6) == "/file ") {
size_t pos = message.find(' ');
if (pos == string::npos) {
continue;
}
string receiver_name = message.substr(7, pos - 7);
string file_path = message.substr(pos + 1);
for (auto& client : clients) {
if (client.name == receiver_name) {
send_file(client.socket, file_path);
break;
}
}
}
else {
cout << name << ": " << message << endl;
broadcast(name + ": " + message, client_socket);
}
}
mtx.lock();
for (auto it = clients.begin(); it != clients.end(); ++it) {
if (it->socket == client_socket) {
cout << "Client disconnected: " << it->name << endl;
broadcast(it->name + " has left the chat.", client_socket);
clients.erase(it);
break;
}
}
mtx.unlock();
closesocket(client_socket);
}
int main() {
WSADATA wsa_data;
int result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (result != 0) {
cerr << "WSAStartup failed with error: " << result << endl;
return -1;
}
SOCKET server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server_socket == INVALID_SOCKET) {
cerr << "socket failed with error: " << WSAGetLastError() << endl;
WSACleanup();
return -1;
}
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
server_addr.sin_addr.s_addr = INADDR_ANY;
result = bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr));
if (result == SOCKET_ERROR) {
cerr << "bind failed with error: " << WSAGetLastError() << endl;
closesocket(server_socket);
WSACleanup();
return -1;
}
result = listen(server_socket, SOMAXCONN);
if (result == SOCKET_ERROR) {
cerr << "listen failed with error: " << WSAGetLastError() << endl;
closesocket(server_socket);
WSACleanup();
return -1;
}
cout << "Waiting for clients to connect..." << endl;
while (true) {
SOCKET client_socket = accept(server_socket, NULL, NULL);
if (client_socket == INVALID_SOCKET) {
cerr << "accept failed with error: " << WSAGetLastError() << endl;
closesocket(server_socket);
WSACleanup();
return -1;
}
thread t(handle_client, client_socket);
t.detach();
}
closesocket(server_socket);
WSACleanup();
return 0;
}
```
在此程序中,我们使用了一个结构体 `Client` 来保存每个客户端的信息,包括套接字和名称。使用 `vector<Client>` 来保存所有连接的客户端。使用 `mutex` 来保证多线程安全。
`broadcast` 函数用于向除了发送者之外的所有客户端广播消息,可以选择不指定发送者。`send_file` 函数用于将文件发送给指定客户端。
在 `handle_client` 函数中,首先接收客户端发送的消息,如果消息是以 `/name` 开头,则表示客户端发送了自己的名称,将其保存到 `Client` 结构体中,并向所有客户端广播其加入聊天室的消息。如果消息是以 `/whisp` 开头,则表示客户端想私聊某个用户,将消息发送给指定用户。如果消息是以 `/file` 开头,则表示客户端想发送文件给某个用户,将文件发送给指定用户。其他情况则表示客户端发送了群聊消息,将其向所有客户端广播。
在主函数中,创建服务器套接字并绑定端口号,然后等待客户端连接。每当有客户端连接时,就创建一个新的线程来处理客户端。
阅读全文