/*
*
*
* 服务器端代码实现
*
*
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
#define MAX_CLIENTS 20
#define BUFFER_SIZE 100
typedef struct {
int fd;
char name[20];
char buff[BUFFER_SIZE];
} MSG;
int fds[MAX_CLIENTS];
void service(int a);
void *service_thread(void *arg);
void sendtoall(int this_fd, const char *name, const char *message);
int main() {
int sockfd;
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
perror("sock error :");
return -1;
}
struct sockaddr_in myaddr;
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(6666);
myaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int ret_bind;
ret_bind = bind(sockfd,(struct sockaddr *)&myaddr,sizeof(myaddr));
if(ret_bind < 0)
{
perror("bind error :");
return -1;
}
int ret_listen;
ret_listen = listen(sockfd,20);
if(ret_listen < 0)
{
perror("listen error :");
return -1;
}
printf("listen .......\n");
service(sockfd);
close(sockfd);
return 0;
}
void service(int a) {
int sockfd = a;
printf("服务器等待客户链接...\n");
while (1) {
char buf[100] = {0};
struct sockaddr_in cliaddr;
memset(&cliaddr,0,sizeof(cliaddr));
int addrln = sizeof(cliaddr);
int connfd;
connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&addrln);
if(connfd < 0)
{
perror("accept error :");
exit(-1);
}
printf("accept成功\n");
if (recv(connfd, buf, sizeof(buf), 0) < 0)
{
perror("accept recv error");
close(connfd); // 关闭连接
continue; // 继续等待下一个连接
}
int i;
//将文件描述符放入坚听队列
for (i = 0; i < MAX_CLIENTS; i++)
{
if (fds[i] == 0) {
fds[i] = connfd;
break;
}
}
//如果监听完 i 为最大值那么就是满了
if (i == MAX_CLIENTS)
{
printf("聊天室已满...\n");
close(connfd);
continue;
}
// 动态分配 MSG 结构体并初始化
MSG *msg = (MSG *)malloc(sizeof(MSG));
msg->fd = connfd;
strncpy(msg->name, buf, sizeof(msg->name) - 1); // 复制用户名,确保不会溢出
msg->name[sizeof(msg->name) - 1] = '\0'; // 确保字符串以 null 结尾
printf("用户:%s 进入聊天\n", msg->name);
sendtoall(connfd, msg->name, "已加入聊天");
// 创建线程处理客户端请求,并传递 MSG 结构体的地址
pthread_t tid;
if (pthread_create(&tid, NULL, service_thread, (void *)msg) != 0)
{
perror("pthread_create error");
free(msg);
close(connfd);
continue; // 继续等待下一个连接
}
}
}
void *service_thread(void *arg) {
MSG *msg = (MSG *)arg;
int fd = msg->fd;
char buffer[BUFFER_SIZE];
int ret;
while ((ret = recv(fd, buffer, sizeof(buffer), 0)) > 0)
{
buffer[ret] = '\0'; // 确保字符串以 null 结尾
sendtoall(fd, msg->name, buffer); // 发送消息给所有客户端(除了自己)
}
if (ret == 0 || errno != EINTR)
{ // 连接已关闭或出错
int i;
for (i = 0; i < MAX_CLIENTS; i++) {
if (fds[i] == fd) {
fds[i] = 0;
break;
}
}
printf("用户:%s 退出聊天\n", msg->name);
sendtoall(fd, msg->name, "已退出聊天");
}
free(msg); // 释放内存
close(fd); // 关闭连接
pthread_exit(NULL);
}
void sendtoall(int this_fd, const char *name, const char *message)
{
int i;
for (i = 0; i < MAX_CLIENTS; i++) {
if (fds[i] != 0 && fds[i] != this_fd) { // 发送给所有客户端(除了自己)
char send_buffer[BUFFER_SIZE + 20]; // 分配足够的空间来存储用户名和消息
snprintf(send_buffer, sizeof(send_buffer), "[%s]: %s", name, message);
send(fds[i], send_buffer, strlen(send_buffer), 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>
void start(int sockfd,char *name,int len);
void *recv_thread(void *p);
char name[20];
int main() {
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket error:");
return -1;
}
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int ret_connect;
ret_connect = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
if (ret_connect < 0) {
perror("connect error:");
close(sockfd);
return -1;
}
printf("启动服务.....\n");
char name[20];
int num;
printf("你的名字是:");
fgets(name, sizeof(name), stdin);
start(sockfd,name,20);
return 0;
}
void start(int sockfd,char *name,int len) {
pthread_t pid;
printf("%s",name);
pthread_create(&pid, NULL, recv_thread, (void *)&sockfd); // 传递sockfd的地址给线程函数
send(sockfd, name, strlen(name), 0);
while (1) {
char buf[100];
//printf("请输入发送内容:\n");
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = 0;
if (strcmp(buf, "bye") == 0) {
break;
}
send(sockfd, buf, strlen(buf), 0); // 只发送实际的字符串长度
}
close(sockfd);
pthread_join(pid, NULL);
}
void *recv_thread(void *p) {
int sockfd = *(int *)p;
while (1) {
char buf[100];
int ret_recv = recv(sockfd, buf, sizeof(buf), 0);
if (ret_recv <= 0) {
break;
} else {
buf[ret_recv] = '\0'; // 确保字符串以null结尾
buf[strcspn(buf, "\n")] = 0;
//write(STDOUT_FILENO, "你:", 3);
write(STDOUT_FILENO, buf, ret_recv); // 写入接收到的消息
write(STDOUT_FILENO, "\n", 1);
}
}
return NULL;
}