Ubuntu 22.04 LTS
VMware workstation 17
代码分为服务器server和客户端client两个文件,可以多机连接(但是需要配置虚拟机,后面会简要说明),编译后在终端运行:
服务器 ./server
客户端 ./client “服务器地址”,这个服务器地址会在服务器程序运行后显示在终端上
代码如下:
/*server*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define BUFF_SIZE 128 // 缓冲区大小
#define PORT 3333 // 端口号
#define MAX_CLIENTS 5 // 最大客户端数量
int clientSockets[MAX_CLIENTS];
int clientCount = 0;
pthread_mutex_t clientMutex = PTHREAD_MUTEX_INITIALIZER;
struct ClientInfo {
int clientSocket;
struct sockaddr_in clientAddr;
};
void error_handler(const char* msg)
{
perror(msg);
exit(1);
}
void* handle_client(void* arg)
{
struct ClientInfo* pClientInfo = (struct ClientInfo*)arg;
int clientSocket = pClientInfo->clientSocket;
struct sockaddr_in clientAddr = pClientInfo->clientAddr;
free(pClientInfo); // 释放分配的内存
char buffer[BUFF_SIZE];
int str_len;
char message[2 * BUFF_SIZE];
while ((str_len = read(clientSocket, buffer, BUFF_SIZE - 1)) > 0)
{
buffer[str_len] = '\0'; // 确保字符串正确终止
memset(message, 0, sizeof(message)); // 清空message
snprintf(message, sizeof(message), "--------------------------------\n>>Client/%s/%d :\n>>%s",
inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), buffer);
printf("%s", message);
pthread_mutex_lock(&clientMutex);
for (int i = 0; i < clientCount; ++i)
{
if (clientSockets[i] != clientSocket)
{
write(clientSockets[i], message, strlen(message));
}
}
pthread_mutex_unlock(&clientMutex);
}
close(clientSocket);
pthread_mutex_lock(&clientMutex);
for (int i = 0; i < clientCount; ++i)
{
if (clientSockets[i] == clientSocket)
{
clientSockets[i] = clientSockets[clientCount - 1];
--clientCount;
break;
}
}
pthread_mutex_unlock(&clientMutex);
printf("✕Client/%s/%d disconnected.\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
return NULL;
}
int main(int argc, char** argv)
{
printf("\033[H\033[J"); // ANSI转义序列清除屏幕内容
/***获取ip**************************************************/
struct ifaddrs *ifap, *ifa;
struct sockaddr_in *sa;
char *addr;
if (getifaddrs(&ifap) == -1) {
perror("getifaddrs");
return -1;
}
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
sa = (struct sockaddr_in *) ifa->ifa_addr;
addr = inet_ntoa(sa->sin_addr);
printf("-->%s: %s\n", ifa->ifa_name, addr);
}
}
freeifaddrs(ifap);
/*********************************************************/
int serverSocket;
struct sockaddr_in serverAddr, clientAddr;
socklen_t addrSize;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(PORT);
serverSocket = socket(PF_INET, SOCK_STREAM, 0);
if (serverSocket == -1) error_handler("!Failed to create socket");
if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1)
{
close(serverSocket);
error_handler("!Failed to bind server address");
}
if (listen(serverSocket, 5) == -1)
{
close(serverSocket);
error_handler("!Failed to listen client");
}
printf("Init server...\nWaiting for connections...\n");
while (1)
{
addrSize = sizeof(clientAddr);
int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &addrSize);
if (clientSocket == -1)
continue;
printf("✓Client/%s/%d connected.\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
pthread_mutex_lock(&clientMutex);
if (clientCount < MAX_CLIENTS)
{
clientSockets[clientCount++] = clientSocket;
pthread_mutex_unlock(&clientMutex);
// 创建并填充ClientInfo结构体
struct ClientInfo* pClientInfo = malloc(sizeof(struct ClientInfo));
pClientInfo->clientSocket = clientSocket;
pClientInfo->clientAddr = clientAddr;
pthread_t t_id;
pthread_create(&t_id, NULL, handle_client, (void*)pClientInfo);
pthread_detach(t_id);
}
else
{
pthread_mutex_unlock(&clientMutex);
close(clientSocket);
printf("Maximum clients reached. Connection rejected.\n");
}
}
close(serverSocket);
return 0;
}
/*client*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdbool.h>
#include <sys/wait.h> // 包含 waitpid 函数声明的头文件
#define BUFF_SIZE 128
#define PORT 3333
void readRoutine(int sock, char* buf);
void writeRoutine(int sock, char* buf);
int main(int argc, char *argv[])
{
printf("\033[H\033[J"); // ANSI转义序列清除屏幕内容
if (argc != 2)
{
fprintf(stderr, "Usage: %s hostname \n", argv[0]);
exit(1);
}
struct hostent *host;
if ((host = gethostbyname(argv[1])) == NULL)
{
fprintf(stderr, "Gethostname error\n");
exit(1);
}
char buffer[BUFF_SIZE];
struct sockaddr_in serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = *((unsigned long *)host->h_addr_list[0]);
serverAddr.sin_port = htons(PORT);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
perror("!Failed to create socket");
exit(1);
}
if (connect(sockfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1)
{
perror("!Failed to connect to server");
exit(1);
}
printf("✓Server connected.(Type '/quit' to quit)\n");
pid_t pid = fork();
if (pid < 0)
{
perror("!Fork failed");
exit(1);
}
else if (pid == 0)
{
// Child process handles sending messages
writeRoutine(sockfd, buffer);
exit(0); // 子进程结束时退出
}
else
{
// Parent process handles receiving messages
readRoutine(sockfd, buffer);
// 等待子进程结束
int status;
waitpid(pid, &status, 0);
}
close(sockfd);
return 0;
}
void readRoutine(int sock, char* buf)
{
while (true)
{
memset(buf, 0, BUFF_SIZE);
int str_len = read(sock, buf, BUFF_SIZE);
if (str_len <= 0) // Handle connection closed or error
{
printf("\n✕Server disconnected.\n");
return;
}
printf("\033[2K\033[1G");
printf("\033[F\033[2K\033[1G");
printf("\033[F\033[2K\033[1G");
printf("%s", buf);
printf("--------------------------------\n<<You :\n<<");
fflush(stdout);
}
}
void writeRoutine(int sock, char* buf)
{
while (true)
{
fputs("--------------------------------\n<<You :\n<<", stdout);
//printf("\033[F\033[2K\033[1G");
fgets(buf, BUFF_SIZE, stdin);
if (strncmp(buf, "/QUIT\n", 2) == 0 || strncmp(buf, "/quit\n", 2) == 0)
{
shutdown(sock, SHUT_WR);
return;
}
write(sock, buf, strlen(buf));
}
}
配置网络,需要所有电脑的外部主机连接到同一个热点,不能是校园网,然后虚拟机的编辑选项,选择虚拟网络编辑器,然后点击更改设置,要管理员权限,然后选择net0,改成桥接模式,并且改成你的网卡,比如intel的网卡或者realtek等,具体可以点开当前连接的网看是什么硬件。然后右键虚拟机,点击设置,将网络适配器改成桥接,复制物理地址,等待即可,最后观察虚拟机的ipv4和电脑的ipv4是不是相同段,也就是最后一位不一样。
具体操作看我的b站视频: