只写了前两个功能。
服务器:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define ERR_MSG(msg) do{\
printf("line:%d\n",__LINE__);\
perror(msg);\
}while(0);
#define IP "192.168.10.246"
#define PORT 6666
struct msgcli
{
char type;
char name[20];
char text[128];
};
typedef struct
{
//数据元素
struct sockaddr_in revaddr[1024];
struct msgcli revmsg[1024];
//顺序表长度
int len;
}Seqlist;
void SeqlistDeleteBySub(Seqlist *s,int sub);
Seqlist *CreateSpce();
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("socket create sucess sfd=%d __%d__\n",sfd,__LINE__);
//允许端口快速被重用,快速复用
int reuse =1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
{
ERR_MSG("setsockopt");
return -1;
}
//bind 将IP地址和端口绑定到文件描述符上
//表示真实的地址信息的结构体
struct sockaddr_in sin;
sin.sin_family= AF_INET; //必须填充AF_INET
sin.sin_port =htons(PORT); //端口的网络字节序1024~49151
sin.sin_addr.s_addr=inet_addr(IP); //ifconfig查看本机IP
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("bind");
return -1;
}
struct sockaddr_in cin;//储存发送方的地址信息
socklen_t addrlen=sizeof(cin);
struct msgcli data_cli;
int num;
Seqlist *s=CreateSpce();
char buf[128]="";
ssize_t res=0;
while(1)
{
//清空数据包
memset(&data_cli,0,sizeof(data_cli));
//接收数据
res = recvfrom(sfd, &data_cli, sizeof(data_cli), 0, (struct sockaddr*)&cin, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
//判断数据包的内容
if(data_cli.type=='l'||data_cli.type=='L')
{
printf("用户%s上线了\n",data_cli.name);
s->revaddr[s->len]=cin;
s->revmsg[s->len]=data_cli;
int i=0;
while(i<s->len)
{
if( sendto(sfd,&data_cli,sizeof(data_cli),0,(struct sockaddr*)&s->revaddr[i],sizeof(s->revaddr[i]))<0)
{
ERR_MSG("sendto");
return -1;
}
i++;
}
s->len++;
}
else if(data_cli.type=='c'||data_cli.type=='C')
{
int i=0;
for(;i<s->len;i++)
{
if(s->revaddr[i].sin_port==cin.sin_port)
{
break;
}
}
printf("%s:%s\n", s->revmsg[i].name, data_cli.text);
int j=0;
while(j<s->len)
{
if(s->revaddr[j].sin_port!=cin.sin_port)
{
if( sendto(sfd,&data_cli,sizeof(data_cli),0,(struct sockaddr*)&s->revaddr[j],sizeof(s->revaddr[j]))<0)
{
ERR_MSG("sendto");
return -1;
}
}
j++;
}
}
else if(data_cli.type=='q'||data_cli.type=='Q')
{
int i;
for(;i<s->len;i++)
{
if(s->revaddr[i].sin_port==cin.sin_port)
{
break;
}
}
SeqlistDeleteBySub(s,i);
}
}
close(sfd);
return 0;
}
Seqlist *CreateSpce()
{
Seqlist *s=(Seqlist *)malloc(sizeof(Seqlist));
if(s==NULL)
return NULL;
s->len=0;//表示顺序表数据元素的个数为0,顺序表空
return s;
}
void SeqlistDeleteBySub(Seqlist *s,int sub)
{
//3,循环前移
for(int i=sub;i<=s->len-1;i++)
{
s->revmsg[i]=s->revmsg[i+1];//前移
s->revaddr[i]=s->revaddr[i+1];//前移
}
//4,删除了一个元素
s->len--;
}
客户端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define ERR_MSG(msg) do{\
fprintf(stderr, "line:%d ", __LINE__);\
perror(msg);\
}while(0)
#define PORT 6666 //1024~49151
#define IP "192.168.10.246" //224.0.0.0-239.255.255.255
struct msgcli
{
char type;
char name[20];
char text[128];
};
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket create success cfd=%d __%d__\n", cfd, __LINE__);
//绑定--非必须绑定
//如果不绑定,则操作系统会默认给客户端绑定本机可用IP和随机端口
//填充接收放的地址信息结构体,给下面的sendto函数使用
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填充AF_INET
sin.sin_port = htons(PORT); //1024~49151
sin.sin_addr.s_addr = inet_addr(IP); //组播IP
char buf[128] = "";
ssize_t res = 0;
struct sockaddr_in rcvaddr; //存储数据包从谁哪里来
socklen_t addrlen = sizeof(rcvaddr);
struct msgcli data_cli;
char c;
printf("请登录账户:L/l 账户名\n");
scanf(" %c",&c);
data_cli.type=c;
scanf("%s",data_cli.name);
data_cli.name[strlen(data_cli.name)] = 0;
getchar();
printf("登录成功\n");
//sendto将数据发送给服务器,所以需要填充服务器的地址信息结构体
if(sendto(cfd, &data_cli, sizeof(data_cli), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
// printf("sendto success\n");
pid_t cpid=0;
cpid= fork();
if(cpid>0)
{
while(1)
{
bzero(data_cli.text, sizeof(data_cli.text));
//发送数据
// printf("%s:",data_cli.name);
data_cli.type='c';
//scanf(" %s",data_cli.text);
fgets(data_cli.text, sizeof(data_cli.text), stdin);
data_cli.text[strlen(data_cli.text)-1] = 0;
//sendto将数据发送给服务器,所以需要填充服务器的地址信息结构体
if(sendto(cfd, &data_cli, sizeof(data_cli), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//printf("sendto success\n");
}
}
if(cpid==0)
{
while(1)
{
//清空数据包
memset(&data_cli,0,sizeof(data_cli));
//接收数据
res = recvfrom(cfd, &data_cli, sizeof(data_cli), 0, (struct sockaddr*)&sin, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
//判断数据包的内容
if(data_cli.type=='l'||data_cli.type=='L')
{
printf("用户%s上线了\n",data_cli.name);
}
else if(data_cli.type=='c'||data_cli.type=='C')
{
printf("%s:%s\n",data_cli.name,data_cli.text);
}
else if(data_cli.type=='q'||data_cli.type=='Q')
{
printf("用户%s下线了\n",data_cli.name);
}
}
}
//关闭套接字
close(cfd);
return 0;
}