服务器端
c
#include<myhead.h>
#define SERIP "192.168.0.132"
#define SERPORT 8888
#define MAX 50
//定义用户结构体
typedef struct{
struct sockaddr_in addr;
int flag;
}User;
User users[MAX];//用户列表
void add_user(struct sockaddr_in *client){
// 检查用户是否已经在列表中
for (int i = 0; i < MAX; i++) {
if (users[i].flag == 1 && memcmp(&users[i].addr, client, sizeof(struct sockaddr_in)) == 0) {
// 用户已经在列表中
return;
}
}
for(int i=0;i<MAX;i++){
if(users[i].flag==0){
users[i].addr = *client;
users[i].flag = 1;
break;
}
}
}
//向用户转发消息,排除发送者
void transform(char *buff,struct sockaddr_in *send,int oldfd){
for(int i=0;i<MAX;i++){
if(users[i].flag!=0){
if(memcmp(&users[i].addr,send,sizeof(struct sockaddr_in))!=0){
sendto(oldfd,buff,strlen(buff),0,(struct sockaddr *)&users[i].addr,sizeof(users[i].addr));
}
}
}
}
int main (int argc, const char *argv[])
{
//定义套接字
int oldfd = socket(AF_INET,SOCK_DGRAM,0);
if(oldfd==-1){
perror("oldfd");
return -1;
}
int kkk=2;
if(setsockopt(oldfd,SOL_SOCKET,SO_BROADCAST,&kkk,sizeof(kkk))==-1){
perror("setsockopt");
return -1;
}
//绑定端口
struct sockaddr_in server = {
.sin_family = AF_INET,
.sin_addr.s_addr = inet_addr(SERIP),
.sin_port = htons(SERPORT)
};
int server_len = sizeof(server);
if(bind(oldfd,(struct sockaddr *)&server,server_len)==-1){
perror("bind");
return -1;
}
printf("UDP聊天服务器启动,端口号为:%d\n",SERPORT);
//IO多路复用
struct pollfd fds[1];
fds[0].fd = oldfd;
fds[0].events = POLLIN;
char buff[1024];
struct sockaddr_in client;
int client_len = sizeof(client);
while(1){
int poll_count = poll(fds,1,-1);
if(fds[0].revents == POLLIN){
memset(buff,0,sizeof(buff));
int len = recvfrom(oldfd,buff,sizeof(buff),0,(struct sockaddr *)&client,&client_len);
if(len == 0){
perror("recvfrom");
return -1;
}
buff[len] = '\0';
printf("接收到来自%s:%d的消息%s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buff);
//添加用户到列表
add_user(&client);
//转发给其他用户
transform(buff,&client,oldfd);
}
}
close(oldfd);
return 0;
}
客户端
c
#include<myhead.h>
#define SERIP "192.168.0.132"
#define SERPORT 8888
#define MAX 50
int main (int argc, const char *argv[])
{
//创建套接字
int oldfd = socket(AF_INET,SOCK_DGRAM,0);
if(oldfd==-1){
perror("oldfd");
return -1;
}
struct sockaddr_in server={
.sin_family = AF_INET,
.sin_port = htons(SERPORT),
.sin_addr.s_addr = inet_addr(SERIP)
};
struct sockaddr_in client;
int client_len = sizeof(client);
int server_len = sizeof(server);
printf("UDP客户端启动,连接到服务器%s:%d\n",inet_ntoa(server.sin_addr),ntohs(server.sin_port));
char message[20] = "UDP客户端连接";
sendto(oldfd,message,strlen(message),0,(struct sockaddr *)&server,server_len);
struct pollfd fds[2];
fds[0].fd = oldfd;
fds[0].events = POLLIN;
fds[1].fd = 0;
fds[1].events = POLLIN;
char buff[1024];
while(1){
int poll_count = poll(fds,2,5000);
if(poll_count == -1){
perror("poll_count");
return -1;
}
if(poll_count == 0){
continue;
}
if(fds[0].revents == POLLIN){
memset(buff,0,sizeof(buff));
int len = recvfrom(oldfd,buff,sizeof(buff),0,(struct sockaddr *)&server,&server_len);
if(len<0){
perror("recvfrom");
continue;
}
buff[len]='\0';
printf("服务器发来消息:%s\n",buff);
}
if(fds[1].revents==POLLIN){
memset(buff,0,sizeof(buff));
if(fgets(buff,sizeof(buff),stdin)!=NULL){
buff[strlen(buff)-1]='\0';
sendto(oldfd,buff,strlen(buff),0,(struct sockaddr *)&server,server_len);
}
}
}
close(oldfd);
return 0;
}
效果:
使用UDP以及I/O多路复用,实现了聊天内容的转发,以及在各个终端窗口的实时显示
不足
无法识别用户并再转发时加上用户的信息