实现一个Linux聊天室程序可以有多种方式,下面我将介绍几种常见的实现方法,从简单到复杂逐步讲解。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT 8888
#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024
int client_sockets[MAX_CLIENTS] = {0};
void *handle_client(void *arg) {
int sock = *(int *)arg;
char buffer[BUFFER_SIZE];
int read_size;
while((read_size = recv(sock, buffer, BUFFER_SIZE, 0)) > 0) {
buffer[read_size] = '\0';
printf("Received: %s", buffer);
// Broadcast to all other clients
for(int i = 0; i < MAX_CLIENTS; i++) {
if(client_sockets[i] != 0 && client_sockets[i] != sock) {
send(client_sockets[i], buffer, strlen(buffer), 0);
}
}
}
// Remove client from array
for(int i = 0; i < MAX_CLIENTS; i++) {
if(client_sockets[i] == sock) {
client_sockets[i] = 0;
break;
}
}
close(sock);
pthread_exit(NULL);
}
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
pthread_t thread_id;
// Create socket
if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// Set socket options
if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// Bind socket to port
if(bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// Listen for connections
if(listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server started on port %d\n", PORT);
while(1) {
// Accept new connection
if((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
printf("New connection from %s:%d\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));
// Add new socket to array
for(int i = 0; i < MAX_CLIENTS; i++) {
if(client_sockets[i] == 0) {
client_sockets[i] = new_socket;
break;
}
}
// Create new thread for client
if(pthread_create(&thread_id, NULL, handle_client, (void*)&new_socket) < 0) {
perror("could not create thread");
return 1;
}
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT 8888
#define BUFFER_SIZE 1024
void *receive_messages(void *arg) {
int sock = *(int *)arg;
char buffer[BUFFER_SIZE];
int read_size;
while((read_size = recv(sock, buffer, BUFFER_SIZE, 0)) > 0) {
buffer[read_size] = '\0';
printf("%s", buffer);
}
pthread_exit(NULL);
}
int main(int argc, char const *argv[]) {
int sock = 0;
struct sockaddr_in serv_addr;
pthread_t thread_id;
if(argc != 2) {
printf("Usage: %s <server IP>\n", argv[0]);
return -1;
}
// Create socket
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IPv4 address from text to binary form
if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr) <= 0) {
printf("\nInvalid address/ Address not supported \n");
return -1;
}
// Connect to server
if(connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
}
printf("Connected to server\n");
// Create thread to receive messages
if(pthread_create(&thread_id, NULL, receive_messages, (void*)&sock) < 0) {
perror("could not create thread");
return 1;
}
// Send messages
char message[BUFFER_SIZE];
while(1) {
fgets(message, BUFFER_SIZE, stdin);
send(sock, message, strlen(message), 0);
}
return 0;
}
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define PORT 8888
#define MAX_CLIENTS 100
struct client {
struct bufferevent *bev;
char name[32];
};
struct client clients[MAX_CLIENTS];
void broadcast_message(const char *msg, struct bufferevent *exclude) {
for(int i = 0; i < MAX_CLIENTS; i++) {
if(clients[i].bev != NULL && clients[i].bev != exclude) {
bufferevent_write(clients[i].bev, msg, strlen(msg));
}
}
}
void event_cb(struct bufferevent *bev, short events, void *ptr) {
if(events & BEV_EVENT_EOF) {
printf("Connection closed\n");
} else if(events & BEV_EVENT_ERROR) {
printf("Got an error on the connection: %s\n", strerror(errno));
}
// Remove client
for(int i = 0; i < MAX_CLIENTS; i++) {
if(clients[i].bev == bev) {
char msg[128];
snprintf(msg, sizeof(msg), "%s has left the chat\n", clients[i].name);
broadcast_message(msg, bev);
clients[i].bev = NULL;
break;
}
}
bufferevent_free(bev);
}
void read_cb(struct bufferevent *bev, void *ptr) {
char buffer[1024];
int n = bufferevent_read(bev, buffer, sizeof(buffer));
buffer[n] = '\0';
// Check if this is a new client setting their name
for(int i = 0; i < MAX_CLIENTS; i++) {
if(clients[i].bev == bev && strlen(clients[i].name) == 0) {
strncpy(clients[i].name, buffer, sizeof(clients[i].name) - 1);
clients[i].name[sizeof(clients[i].name) - 1] = '\0';
char *nl = strchr(clients[i].name, '\n');
if(nl) *nl = '\0';
char msg[128];
snprintf(msg, sizeof(msg), "%s has joined the chat\n", clients[i].name);
broadcast_message(msg, bev);
return;
}
}
// Broadcast message to all clients
for(int i = 0; i < MAX_CLIENTS; i++) {
if(clients[i].bev == bev) {
char msg[1100];
snprintf(msg, sizeof(msg), "%s: %s", clients[i].name, buffer);
broadcast_message(msg, bev);
break;
}
}
}
void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *address, int socklen, void *ctx) {
struct event_base *base = evconnlistener_get_base(listener);
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
// Add new client
for(int i = 0; i < MAX_CLIENTS; i++) {
if(clients[i].bev == NULL) {
clients[i].bev = bev;
memset(clients[i].name, 0, sizeof(clients[i].name));
break;
}
}
bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
bufferevent_enable(bev, EV_READ|EV_WRITE);
bufferevent_write(bev, "Welcome to the chat! Please enter your name:\n", 45);
}
int main() {
struct event_base *base;
struct evconnlistener *listener;
struct sockaddr_in sin;
base = event_base_new();
if(!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&sin, sizeof(sin));
if(!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}
printf("Server started on port %d\n", PORT);
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
安装IRC服务器(如InspIRCd):
sudo apt-get install inspircd
配置IRC服务器(编辑/etc/inspircd/inspircd.conf
)
启动服务器:
sudo service inspircd start
客户端可以使用任何IRC客户端如irssi:
sudo apt-get install irssi
irssi -c localhost
安装Synapse:
sudo apt-get install matrix-synapse
配置服务器:
sudo nano /etc/matrix-synapse/homeserver.yaml
启动服务器:
sudo systemctl start matrix-synapse
使用Element等客户端连接
// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
const clients = new Set();
wss.on('connection', function connection(ws) {
clients.add(ws);
ws.on('message', function incoming(message) {
console.log('received: %s', message);
// Broadcast to all clients
clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
ws.on('close', function() {
clients.delete(ws);
});
});
console.log('WebSocket server started on ws://localhost:8080');
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Chat</title>
<style>
#messages { width: 500px; height: 300px; border: 1px solid #ccc; overflow-y: scroll; }
#message { width: 400px; }
</style>
</head>
<body>
<h1>WebSocket Chat</h1>
<div id="messages"></div>
<input type="text" id="message" placeholder="Type your message here">
<button onclick="sendMessage()">Send</button>
<script>
const socket = new WebSocket('ws://localhost:8080');
const messages = document.getElementById('messages');
socket.onmessage = function(event) {
const message = document.createElement('div');
message.textContent = event.data;
messages.appendChild(message);
messages.scrollTop = messages.scrollHeight;
};
function sendMessage() {
const input = document.getElementById('message');
socket.send(input.value);
input.value = '';
}
document.getElementById('message').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
</script>
</body>
</html>
以上方案可以根据需求选择适合的实现方式,从简单的Socket编程到使用成熟的聊天协议都有涵盖。