基于UDP的网络聊天室:
服务器代码:
#include<myhead.h>
struct cin_fm
{
char type;
char name[20];
char text[128];
};
int main(int argc, const char *argv[])
{
//判断输入是否正确
if(argc!=3){
printf("enter error\n");
return -1;
}
//创建用于通信的套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//bind绑定端口号和ip地址
struct sockaddr_in sin;
sin.sin_family = AF_INET; //ipv4通信
sin.sin_port = htons((short)(atoi(argv[2]))); //端口号
sin.sin_addr.s_addr = inet_addr(argv[1]); //ip地址
if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
//定义客户端结构体数组
struct sockaddr_in cin[100];
for(int i=0;i<100;i++)
cin[i].sin_family = AF_INET;
//定义一个集合管理文件描述符
struct pollfd fds[100];
int i;
//清空集合
for(i=0;i<100;i++){
fds[i].fd=-1;
}
//将标准输入描述符放入集合中
fds[0].fd=0;
fds[0].events=POLLIN;
//将sfd放入集合中
fds[1].fd=sfd;
fds[1].events=POLLIN;
//定义储存客户信息的结构体数组
struct cin_fm add_cin[100];
//定义用来通信的结构体和客户信息结构体
struct cin_fm temp;
struct sockaddr_in cintemp;
cintemp.sin_family = AF_INET;
socklen_t socklen = sizeof(cintemp);
//清空结构体数组
for(int i=0;i<100;i++){
bzero(add_cin[i].name, sizeof(add_cin[0].name));
bzero(add_cin[i].text, sizeof(add_cin[0].text));
}
int res;
char buf[128] = "";
int maxcin=0;
while(1)
{
res = poll(fds,2, -1); //第三个参数如果是负数,表明一直等待
if(res == -1)
{
perror("poll error");
return -1;
}else if(res == 0)
{
printf("time out\n");
return -1;
}
if(fds[1].revents==POLLIN){
//清空内容
temp.type=0;
bzero(temp.name,sizeof(temp.name));
bzero(temp.text,sizeof(temp.text));
//读取信息
recvfrom(sfd,&temp, sizeof(temp), 0, (struct sockaddr*)&cintemp, &socklen);
//判断是登录,群聊,还是退出
if(temp.type=='L'){
printf("%s[%s:%d]:登录成功\n",temp.name,inet_ntoa(cintemp.sin_addr),ntohs(cintemp.sin_port));
strcpy(add_cin[maxcin].name,temp.name);
cin[maxcin].sin_port=cintemp.sin_port;
cin[maxcin].sin_addr.s_addr=cintemp.sin_addr.s_addr;
maxcin++;
for(int i=0;i<=maxcin;i++){
//向所用其他客户端发送数据
if(cintemp.sin_port==cin[i].sin_port)continue;
sendto(sfd,&temp, sizeof(temp),0, (struct sockaddr*)&cin[i], sizeof(cin[i]));
}
}
if(temp.type=='C'){
for(int i=0;i<=maxcin;i++){
//向所用其他客户端发送数据
if(cintemp.sin_port==cin[i].sin_port)continue;
sendto(sfd,&temp, sizeof(temp),0, (struct sockaddr*)&cin[i], sizeof(cin[i]));
}
printf("%s[%s:%d]:chat成功\n",temp.name,inet_ntoa(cintemp.sin_addr),ntohs(cintemp.sin_port));
}
if(temp.type=='Q'){
printf("%s[%s:%d]:已离线\n",temp.name,inet_ntoa(cintemp.sin_addr),ntohs(cintemp.sin_port));
for(int i=0;i<=maxcin;i++){
//删除该用户
if(cintemp.sin_port==cin[i].sin_port){
int t=i;
for(int j=maxcin;j>=i;j--){
cin[t]=cin[t+1];
add_cin[t]=add_cin[t+1];
t++;
}
maxcin--;
}
//向所用其他客户端发送数据
sendto(sfd,&temp, sizeof(temp),0, (struct sockaddr*)&cin[i], sizeof(cin[i]));
}
}
}
if(fds[0].revents==POLLIN){
bzero(buf,sizeof(buf));
//从终端输入中获取数据
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strcmp(buf,"quit")==0){
close(sfd);
return 0;
}
temp.type='C';
strcpy(temp.text,buf);
strcpy(temp.name,"^^^systeam^^^");
for(int i=0;i<=maxcin;i++){
//向所用客户端发送数据
sendto(sfd,&temp, sizeof(temp),0, (struct sockaddr*)&cin[i], sizeof(cin[i]));
}
}
}
//关闭套接字
close(sfd);
return 0;
}
客户端代码:
#include<myhead.h>
struct add_cin
{
char type;
char name[20];
char text[128];
};
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd == -1)
{
perror("socket error");
return -1;
}
//填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons((short)(atoi(argv[2]))); //端口号
sin.sin_addr.s_addr = inet_addr(argv[1]); //ip地址
char buf[128] = "";
char rbuf[128] = "";
char nm[20]="";
//定义客户端信息结构体
struct add_cin temp;
bzero(temp.name,sizeof(temp.name));
bzero(temp.text,sizeof(temp.text));
//清空内容
bzero(buf, sizeof(buf));
bzero(rbuf, sizeof(rbuf));
bzero(nm,sizeof(nm));
//登录请求
printf("请输入姓名>>>");
scanf("%s",nm); //从终端获取数据
getchar();
strcpy(temp.name,nm);
temp.type='L';
//将数据发送给服务器
sendto(cfd,&temp, sizeof(temp),0,(struct sockaddr*)&sin, sizeof(sin));
//定义一个集合管理文件描述符
struct pollfd fds[2];
//将标准输入描述符放入集合中
fds[0].fd=0;
fds[0].events=POLLIN;
//将sfd放入集合中
fds[1].fd=cfd;
fds[1].events=POLLIN;
int res;
while(1)
{
res = poll(fds,2, -1); //第三个参数如果是负数,表明一直等待
if(res == -1)
{
perror("poll error");
return -1;
}else if(res == 0)
{
printf("time out\n");
return -1;
}
if(fds[0].revents==POLLIN){
bzero(buf,sizeof(buf));
fgets(buf, sizeof(buf), stdin); //从终端获取数据
buf[strlen(buf)-1] = '\0';
if(strcmp(buf,"quit")==0){
temp.type='Q';
sendto(cfd,&temp, sizeof(temp), 0, (struct sockaddr*)&sin, sizeof(sin));
close(cfd);
return 0;
}else{
strcpy(temp.text,buf);
temp.type='C';
sendto(cfd,&temp, sizeof(temp), 0, (struct sockaddr*)&sin, sizeof(sin));
}
}
if(fds[1].revents==POLLIN){
temp.type='N';
bzero(buf,sizeof(buf));
bzero(rbuf,sizeof(rbuf));
//接收服务器段发来的消息
recvfrom(cfd,&temp,sizeof(temp), 0,NULL,NULL);
if(temp.type=='L'){
printf("----%s登录成功----\n",temp.name);
}
if(temp.type=='Q'){
printf("%s已离线\n",temp.name);
}
if(temp.type=='C'){
strcpy(buf,temp.text);
strcpy(rbuf,temp.name);
printf("%s:%s\n",rbuf,buf);
if(strcmp(temp.name,"^^^steam^^^")){
strcpy(temp.name,nm);
}
}
}
}
//关闭套接字
close(cfd);
return 0;
}
运行结果: