/************************************************************
去年学C语言socket通信时花三天时间写的。
功能
A,私聊 B,群聊,C,从服务器下载文件 D,上传文件到服务器,E,用户上下线通知,F,刷新在线用户列表,E,下线
1,一般情况请使用SUDO
权限运行
2,服务器文件中心路径为 /home/file_centre
,若没有该文件夹,请创建
3,客户端下载到的文件保存路径为 /home/file_download
使用该功能前,请先创建该文件夹
4,请在linux环境下测试运行(我使用的是ubuntu)
*****************************************************************/
//程序测试截图
说明:一共是三个文件:头文件,服务器代码,客户端代码。
/*头文件*/
#include<stdio.h>
#include<sys/types.h> /* See NOTES */
#include<sys/socket.h>
#include<string.h>
#include<strings.h>
#include<stdlib.h>
#include<netdb.h>
#include<errno.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<signal.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<dirent.h>
#define MAXSIZE 512
/*服务器接收消息后,创建的在线用户列表*/
struct user_info{
char user_name[20];
int id;
struct sockaddr_in cli_addr;
struct user_info *next;
};
/*客户端给服务器发送的消息*/
struct msg {
char type;
char self_name[20];
char dst_name[20];
char data[MAXSIZE];
};
/*消息类型定义*/
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);
if(desname != NULL)
strcpy(mymsg.dst_name, desname);
if(data != NULL)
strcpy(mymsg.data, data);
if(sendto(cli_fd,&mymsg,sizeof(struct msg),0,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0){
perror("sendto");
exit(1);
}
}
/*打印主菜单*/
void show_opt(void)
{
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){
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);
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");
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);
}
}
/*线程:接收文件*/
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下载数据至地址addr :%s(%d)\n",inet_ntoa(file_addr.sin_addr),ntohs(file_addr.sin_port));
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)
{
char file_name[64];
printf("当前资源中心所有文件:\n");
usleep(100000);
/*此次接收文件列表*/
printf("\n请选择一个文件下载:\n");
scanf("%s",file_name);
send_sig(myname,NULL,file_name,serv_addr,DOWNLOAD);
if(pthread_create(&tid4,NULL,recv_file,(void *)file_name)){
perror("pthread_create2");
exit(1);
}
pthread_join(tid4,NULL);
}
/*发送文件*/
void *send_file( void *file_name)
{
int file_fd = -1;
int send_fd = -1;
char buf[BUFSIZ];
int ret = -1;
int num = -1;
int len = -1;
struct sockaddr_in file_addr;
char file_path[80]="/home/file_centre/";
strcat(file_path, (char *)file_name);
file_fd = open(file_path,O_RDONLY);
if(file_fd < 0){
perror("open");
printf("对不起,您选择有误!请重新进入上传模式。\n");
pthread_exit(NULL);
}
send_fd = socket(AF_INET,SOCK_STREAM,0);
if(send_fd < 0){
perror("socket");
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(54321);
file_addr.sin_addr = *(*p_addr);
usleep(200000);
ret = connect(send_fd,(struct sockaddr *)&file_addr,sizeof(struct sockaddr_in));
if(ret < 0){
perror("connect");
pthread_exit(NULL);
}
len = lseek(file_fd,0L,SEEK_END);
lseek(file_fd,0L,SEEK_SET);
while(len > 0){
bzero(&buf,sizeof(buf));
ret = read(file_fd,buf,sizeof(buf));
if(ret < 0){
perror("read");
pthread_exit(NULL);
}
num = write(send_fd,buf,ret);
if(num < 0){
perror("write");
pthread_exit(NULL);
}
}
printf("文件上传成功!\n");
pthread_exit(NULL);
}
/*下载文件*/
void upload(void)
{
char download_path[80] = "/home/liaoye/file_download/";
char file_name[64];
DIR *dirp;
struct dirent *E;
pthread_t tid5 = -1;
dirp = opendir(download_path);
printf("您的文件夹:\n");
while( (E = readdir(dirp) )!= NULL)
printf("<%s>\t",E->d_name);
printf("\n");
printf("选择您要上传的文件:\n");
scanf("%s",file_name);
send_sig(myname,NULL,file_name,serv_addr,UPLOAD);
if(pthread_create(&tid5,NULL,send_file,(void *)&file_name)){
perror("pthread_create2");
exit(1);
}
pthread_join(tid5,NULL);
}
int main(void)
{
void myhandle(int signum);
signal(SIGINT,myhandle);
struct msg mymsg;
char ch;
int on = 1;
int ret = -1;
char chat_data[MAXSIZE];
char log[] = "上线了。\n";
bzero(&chat_data,sizeof(chat_data));
pthread_t tid1;
pthread_t tid2;
pthread_t tid3;
bzero(&serv_addr,sizeof(struct sockaddr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(7890);
serv_addr.sin_addr.s_addr = inet_addr("192.168.7.117");
cli_fd = udp_link();
sock_fd = udp_link();//组播套接字
ret = setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,(void *)&on,sizeof(on));
if(ret < 0){
perror("setsockopt to SO_REUSEADDR");
exit(1);
}
printf("\n");
system("clear");
printf("********************************************************\n");
printf("***********************欢迎使用**********************\n");
printf("********************************************************\n");
printf("\n======================用户
登录======================\n");
printf("--------------------------------------------------------\n");
printf("请输入您的用户名:\n");
scanf("%s",myname);
strcpy(chat_data,myname);
strcat(chat_data,log);
send_sig(myname,NULL,NULL,serv_addr,LOG_IN);
usleep(200000);
printf("提示:登录成功!\n");
printf("========================================================\n");
/*登录后发送刷新在线用户列表并打印*/
printf("当前在线用户:\n");
send_sig(myname,NULL,NULL,serv_addr,REFRESH);
send_sig(myname,NULL,chat_data,serv_addr,CHAT_ALL);
if(pthread_create(&tid1,NULL,recv_chat_func,NULL) < 0){
perror("pthread_create1");
exit(1);
}
pthread_detach(tid1);
if(pthread_create(&tid2,NULL,chat_toall_recv,NULL) < 0){
perror("pthread_create2");
exit(1);
}
pthread_detach(tid2);
/*列出所有功能*/
usleep(200000);
option: printf("\n********************************************************\n");
printf("********************用户主界面**********************\n");
printf("********************************************************\n");
show_opt();
scanf(" %c",&ch);
printf("-----------------------------------------------