局域网通信软件——by liaoye928
/************************************************************
功能
A,私聊 B,群聊,C,从服务器下载文件 D,上传文件到服务器,E,用户上下线通知 ,
F,刷新在线用户列表,E,下线
/* See NOTES */
1,一般情况请使用 SUDO 权限运行
2,服务器文件中心路径为 /home/file_centre ,若没有该文件夹,请创建
3,客户端下载到的文件保存路径为 /home/file_download 使用该功能前,请先创建该文件夹
4,请在 linux 环境下测试运行(我使用的是 ubuntu)
*****************************************************************/
/*头文件*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXSIZE 512
/*服务器接收消息后,创建的在线用户列表*/
struct user_info{
id;
char user_name[20];
int
struct sockaddr_in cli_addr;
struct user_info *next;
};
/*客户端给服务器发送的消息*/
struct msg {
char type;
char self_name[20];
char dst_name[20];
char data[MAXSIZE];
};
/*消息类型定义*/
1
局域网通信软件——by liaoye928
//要求刷新用户在线用户(重新打印在线用户)
//私聊消息
//群聊消息
//从服务器下载文件(下载之前先打印文件列表)
//上传文件到服务器
//登录
enum msg_type{
LOG_IN = 1,
REFRESH,
CHAT_PRI,
CHAT_ALL,
DOWNLOAD,
UPLOAD,
OFFLINE,
OVER,
ERROR,
FILE_NAME,
FILE_ERROR//选择文件名失败
//下线通知
//服务器发送本次消息结束
//重复登录
//发送文件列表
};
/*服务器总列表*/
struct servmsg
{
struct msg recvmsg;
struct sockaddr_in addr;
struct servmsg *next;
};
/*客户端源程序 --liaoye928*/
#include"include.h"
static int cli_fd = -1; //主要套接字,用于接受服务器各种消息
static int sock_fd = -1; //组播套接字,用于群聊
static struct sockaddr_in serv_addr; //用于存储服务器 IP 和端口
static char myname[20]; //用于存储用户名
static pthread_t tid4 = -1; //将其定义为全局变量的原因是,接收线程会将其取消
/*创建数据报套接字函数*/
int udp_link(void)
{
int sock_fd;
sock_fd = socket(AF_INET,SOCK_DGRAM,0);
return sock_fd;
}
/*用于给服务其发送数据的函数*/
void send_sig(char myname[],char desname[],char data[],struct sockaddr_in serv_addr,char ch)
{
struct msg mymsg;
mymsg.type = ch;
if(myname != NULL)
strcpy(mymsg.self_name,myname);
2
局域网通信软件——by liaoye928
if(desname != NULL)
strcpy(mymsg.dst_name, desname);
if(data != NULL)
strcpy(mymsg.data, data);
if(sendto(cli_fd,&mymsg,sizeof(struct
*)&serv_addr,sizeof(serv_addr)) < 0){
perror("sendto");
exit(1);
}
}
/*打印主菜单*/
void show_opt(void)
{
msg),0,(struct
sockaddr
printf("《A:@私聊模式》\t《B:@群聊模式》\n《C:@上传文件》\t《D:@下载文
件》\n《E:@刷新列表》\t《F:@下线离开》\n");
printf("请选择(如:A):\n");
}
/*接收消息函数,接收各种由服务器发来的数据,根据数据类型分别处理*/
void *recv_chat_func()
{
int ret = -1;
int i = 0;
struct msg rcv_buf;
char quick_repeat[] = "亲,我有点事离开,稍后联系!";
while(1){
bzero(&rcv_buf,sizeof(rcv_buf));
ret = recvfrom(cli_fd,&rcv_buf,sizeof(rcv_buf),0,NULL,NULL);
if(ret < 0){
perror("recvfrom");
exit(1);
}
printf("接收到消息,消息类型:%d\n 内容:%s\n",rcv_buf.type,rcv_buf.data);
rcv_buf.data[ret
-
//
//
sizeof(rcv_buf.type)-sizeof(rcv_buf.self_name)-sizeof(rcv_buf.dst_name)] = '\0';
/*登录消息,刷新消息*/
if(rcv_buf.type == LOG_IN || rcv_buf.type == REFRESH){
printf("@%s\t",rcv_buf.data);
fflush(NULL);
}
/*发送结束消息*/
else if(rcv_buf.type == OVER){
3
局域网通信软件——by liaoye928
printf("\n");
}
/*用户名重复提示消息*/
else if(rcv_buf.type == ERROR){
printf("登录失败!\n");
printf("%s",rcv_buf.data);
exit(1);
}
/*下载时文件不存在提示消息*/
else if(rcv_buf.type == FILE_ERROR){
printf("<文件传输>提示信息:%s\n",rcv_buf.data);
pthread_cancel(tid4); //如若输入文件名出错,取消线程,退出监听
}
/*文件名列表消息*/
else if(rcv_buf.type == FILE_NAME){
printf("<%s>\t\t",rcv_buf.data);
i++;
if(i%4 == 0){
printf("\n");
}
fflush(NULL);
}
/*正常聊天消息(私聊)*/
else {
printf("新消息!===消息来自《%s》:\n",rcv_buf.self_name);
printf("--------------------------\n");
printf("消息内容:%s",rcv_buf.data);
printf("( 若 要 与 %s 聊 天 , 请 退 回 主 界 面 重 新 选 择 私 聊 对
象。)\n\n",rcv_buf.self_name);
}
}
}
/*私聊函数,参数均为全局变量*/
void chat_private(void)
{
char peer_name[20];
char chat_data[MAXSIZE];
bzero(&chat_data,sizeof(chat_data));
bzero(&peer_name,sizeof(peer_name));
printf("请选择聊天对象:\n");
scanf("%s",peer_name);
4
局域网通信软件——by liaoye928
while(getchar() != '\n');
//usleep(100000);//延时下
printf("-----------------正在与《%s》聊天-------------------\n",peer_name);
while(1){
printf("请输入聊天内容(按回车键发送):\n");
printf("-----输入 quit 退回主界面-----\n");
fgets(chat_data,sizeof(chat_data),stdin);
if(strncmp(chat_data,"quit",4) == 0)
break;
send_sig(myname,peer_name,chat_data,serv_addr,CHAT_PRI);
printf("--------------------\n");
}
}
/*线程:接收群聊消息(广播)*/
void *chat_toall_recv()
{
int ret = -1;
int num = -1;
sock_fd = cli_fd;
//
struct msg rcv_buf;
/*获取本机 IP 并分配一个端口*/
struct hostent *h_info;
struct in_addr **p_addr;
h_info = gethostbyname("ubuntu");
p_addr = ((struct in_addr **)(h_info->h_addr_list));
struct sockaddr_in self_addr;
memset(&self_addr,0,sizeof(self_addr));
self_addr.sin_family = AF_INET;
self_addr.sin_port = htons(17891);
self_addr.sin_addr.s_addr = htonl(INADDR_ANY);
struct ip_mreq group;
bzero(&group,sizeof(group));
group.imr_multiaddr.s_addr = inet_addr("224.100.100.100");
group.imr_interface = *(*p_addr);
/*允许地址重用*/
ret = setsockopt(sock_fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group));
if(ret < 0){
perror("setsockopt to ADD_MEMBERSHIP");
5
-
局域网通信软件——by liaoye928
exit(1);
}
ret = bind(sock_fd,(struct sockaddr *)&self_addr, sizeof(self_addr));
if(ret < 0){
perror("bind");
exit(1);
}
while(1){
//printf("等待接收群聊消息\n");
ret = recvfrom(sock_fd,&rcv_buf,sizeof(rcv_buf),0,NULL,NULL);
//printf("新的群聊消息\n");
if(ret < 0){
perror("recvfrom");
exit(1);
}
rcv_buf.data[ret
sizeof(rcv_buf.type)-sizeof(rcv_buf.self_name)-sizeof(rcv_buf.dst_name)] = '\0';
printf("=====《群聊》消息来自<%s>:\n",rcv_buf.self_name);
printf("--------------------------\n");
printf("消息内容:%s",rcv_buf.data);
}
}
/*发送群聊消息,其实跟饲料消息基本一样的,消息类型不一样而已*/
void chat_toall(void)
{
char chat_data[MAXSIZE];
bzero(&chat_data,sizeof(chat_data));
printf("--------------------------------------------------------\n");
printf("---------------<正在与所有在线用户聊天>-----------------\n");
while(getchar() != '\n');
while(1){
printf("请输入聊天(群聊)内容(按回车键发送):\n");
printf("-----输入 quit 退回主界面-----\n");
fgets(chat_data,sizeof(chat_data),stdin);
if(strncmp(chat_data,"quit",4) == 0)
break;
send_sig(myname,NULL,chat_data,serv_addr,CHAT_ALL);
}
}
6
局域网通信软件——by liaoye928
/*线程:接收文件*/
void *recv_file(void *file_name)
{
char download_path[80] = "/home/file_download/";
int file_fd = -1;
int recv_fd = -1;
int new_fd = -1;
int ret = -1;
int num = -1;
char buf[BUFSIZ];
struct sockaddr_in file_addr;
//printf("文件名:%s\n",(char *)file_name);
strcat(download_path,(char *)file_name);
file_fd = open(download_path, O_WRONLY|O_CREAT,0666);
if(file_fd < 0){
perror("open");
pthread_exit(NULL);
}
recv_fd = socket(AF_INET,SOCK_STREAM,0);
if (recv_fd < 0){
perror("socket");
pthread_exit(NULL);
}
int on = 1;
ret = setsockopt(recv_fd,SOL_SOCKET,SO_REUSEADDR,(void *)&on,sizeof(on));
if(ret < 0){
perror("setsockopt to SO_REUSEADDR");
pthread_exit(NULL);
}
struct hostent *h_info;
struct in_addr **p_addr;
h_info = gethostbyname("ubuntu");
p_addr = ((struct in_addr **)(h_info->h_addr_list));
bzero(&file_addr,sizeof(struct sockaddr));
file_addr.sin_family = AF_INET;
file_addr.sin_port = htons(12345);
file_addr.sin_addr = *(*p_addr);
//printf("\n
下
7
至
:%s(%d)\n",inet_ntoa(file_addr.sin_addr),ntohs(file_addr.sin_port));
载
数
addr
据
地
址
局域网通信软件——by liaoye928
ret = bind(recv_fd,(struct sockaddr *)&file_addr, sizeof(struct sockaddr));
if(ret < 0){
perror("bind");
pthread_exit(NULL);
}
ret = listen(recv_fd,8);
if(ret < 0){
perror("listen");
pthread_exit(NULL);
}
new_fd = accept(recv_fd,NULL,NULL);
while(1){
bzero(&buf,sizeof(buf));
ret = read(new_fd,buf,sizeof(buf));
if(ret < 0){
perror("read");
pthread_exit(NULL);
}
if(ret == 0){
break;
}
if(ret > 0){
num = write(file_fd,buf,ret);
if(num < 0){
perror("write");
pthread_exit(NULL);
}
}
}
printf("<%s>下载完成\n",(char *)file_name);
printf("(文件保存路径为:/home/file_download/)\n");
close(file_fd);
close(recv_fd);
close(new_fd);
pthread_exit(NULL);
}
/*发出下载请求并创建接收数据线程*/
void download(void)
{
8