聊天工具(linux+tcp)

编译源代码的命令
gcc client.c -o client
gcc server.c -o server

在一台linux运行server,另外一台linux运行client,2台电脑处于同一个局域网网段。

功能列表
1 注册信息写入文件 服务端将用户信息写入文件
2 查看用户所有用户信息 服务端查看所有用户信息
3 删除用户注册信息 服务端将用户信息删除
4 踢掉用户 服务端踢掉指定用户
5 注册 客户端用户可进行注册
6 登录 客户端已注册的用户可以进行登录
7 查看在线(所有)好友 客户端查看在线(所有)好友
8 私聊 客户端一对一的聊天
9 公聊 客户端对在线所有用户进行群发消息
10 查看聊天记录 客户端用户查看本用户的聊天信息
11 聊天记录写入文件 客户端将用户聊天信息写入文件
12 修改密码 客户端用户可以修改登陆密码
13 传文件 可一对一的传输文件

企业微信截图_20200103182129.png
111.png
222.png

server.c

//===包含的头文件=========
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <fcntl.h>
#include "linklist_cwy.c"
//========================


//===定义的宏=============
#define BACKLOG 50
//=========================


//===全局变量=======================
LinkList L;//链表头指针
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//mutex快速互斥锁
int server_sockfd;
//==================================


//===函数声明======================
void pthread_server_menu(void);
void pthread_client(int *temp);
int sendto_client(int *client_sockfd_n,char *buf,int length);
int recvfrom_client(int *client_sockfd_n,char *buf,int length);
int regist_server(int *p_client_sockfd_n);
struct user_msg log_in_server(int *p_client_sockfd_n);
int chat_server(int *p_client_sockfd_n,struct user_msg user);
void speak2all(LinkList L,char *buf,int len);
void broadcast(void);
void kick_off(void);
void Getuseronline_client(LinkList L,int p_client_sockfd_n);
void Getalluser_client(LinkList L,int p_client_sockfd_n);
void delete_user(void);
static void change_passwd(int *p_client_sockfd_n,char myname[]);
void sendfile_client(int file_from_socket,int file_to_socket);
//==================================


//===主函数=======================
int main(int argc,char *argv[])
{  
    //参数合法性判断
    if(argc<2)
    {
        printf("\n");
        printf("---请按以下示例输入---\n");
        printf("./server_cwy 3333 \n");
        printf("\n");
        exit(1);
    }
    
    CreateList_L(&L);//创建一个链表,传入参数:头指针的地址
    file2list(&L);//将文件的内容读到链表,传入参数:头指针的地址
    
    pthread_mutex_init(&mutex,NULL);//初始化互斥锁
    int ret;
    
    //===创建服务器-功能菜单-子线程======================
    pthread_t pid_server_menu;//服务器-菜单子线程ID号
    ret = pthread_create(&pid_server_menu,NULL,(void *)pthread_server_menu,NULL);
    if(ret != 0)
    {
        perror("pthread_creat");
        exit(1);
    }
    printf("\n");
    printf("---服务器-功能菜单-子线程创建成功!---\n");
    printf("\n");
    
    
    //===服务器-主线程(等待客户端连接)==============
    printf("\n");
    printf("---服务器-主线程(等待客户端连接)启动成功!---\n");
    printf("\n");
    
    //创建主socket

    server_sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(server_sockfd == -1)
    {
        perror("socket");
        exit(1);
    }
    printf("\n");
    printf("---创建主socket成功!server_sockfd=%d---\n",server_sockfd);
    printf("\n");
    
    //设置服务器地址结构体
    struct sockaddr_in server_sockaddr;
    unsigned short int port;//端口号
    //atol()能将字符串转换成长整形数
    port = (unsigned short int)atol(argv[1]);//通过main函数参数传进端口号

    server_sockaddr.sin_family = AF_INET;
    //htons()主机字节顺序转换为网络字节顺序(对无符号短型进行操作2bytes)
    server_sockaddr.sin_port = htons(port);
    //htonl()主机字节顺序转换为网络字节顺序(对无符号长型进行操作4bytes)
    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);//获取服务器当前 IP INADDR_ANY:表示本机网卡的IP
    bzero(&(server_sockaddr.sin_zero),8);
    
    
    
    //主socket绑定IP和端口
    ret = bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr));
    if(ret == -1)
    {
        perror("bind");
        exit(1);
    }
    printf("\n");
    printf("---主socket绑定IP和端口成功!---\n");
    printf("\n");
    
    //监听
    ret = listen(server_sockfd,BACKLOG);
    if(ret == -1)
    {
        perror("listen");
        exit(1);
    }
    printf("\n");
    printf("---主socket监听成功!---\n");
    printf("\n");

    
    struct sockaddr_in client_sockaddr_n;   
    int len;
    int pthread_n = 0;
    
    while(1)
    {
        pthread_t id_client;
        
        printf("\n");
        printf("---服务器-主线程正在等待新的客户端连接~~~\n");
        printf("\n");
        
        int client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_sockaddr_n,(socklen_t*)&len);
        
        printf("\n");
        printf("---有一个新的客户端(client_sockfd=%d)连接成功---\n",client_sockfd);
        printf("\n");
        
        printf("\n");
        printf("---获取客户端(client_sockfd=%d)IP和端口成功!---\n");
        //inet_ntoa()将一个IP转换成一个互联网标准点分格式的字符串
        printf("IP:%s\n",inet_ntoa(client_sockaddr_n.sin_addr));
        printf("端口号:%d\n",port);
        printf("\n");
        
    
        int *p_client_sockfd_n = (int *)malloc(sizeof(int));
        *p_client_sockfd_n = client_sockfd;
        
        //创建新的处理客户端-子线程
        pthread_create(&id_client,NULL,(void *)pthread_client,p_client_sockfd_n);
        
        printf("\n");
        printf("---新的处理客户端(client_sockfd=%d)-子线程%d创建成功---\n",client_sockfd,++pthread_n);    
        printf("\n");
    }
   
    return 0;
}
//=====================================================================


//===功能菜单==================================
void menu()
{
    printf("======功能菜单======\n");
    printf("[1]显示所有用户信息\n");
    printf("[2]显示在线用户信息\n");
    printf("[3]发送广播\n");
    printf("[4]踢掉指定用户\n");  
    printf("[5]删除指定用户信息\n");
    printf("[6]退出服务器\n");
    printf("ctrl+\\(唤出功能菜单)\n");
    printf("===============\n");
}
//======================================



//===服务器功能菜单-子线程处理函数===========
void pthread_server_menu(void)
{

    signal(SIGQUIT,menu);
    
    char buf_input[10];
    char option;
    
    while(1)
    {
        printf("\n\n");
        menu();
        memset(buf_input,0,10);     
        fflush(stdin);//清输入缓存   
        printf("请输入数字1-6进入选项:");
        scanf("%s",buf_input);
        printf("\n\n"); 
        
        option=buf_input[0];

        switch (option)
        {
        case '1':
            {
                Disp_L(L);//服务端显示所有用户
                break;
            }
        case '2':
            {
                Getstate_L(L);//服务端显示在线用户
                break;
            }
        case '3':
            {
                broadcast();//广播系统信息
                break;      
            }
        case '4':
            {   
                kick_off();//踢掉指定用户
                break;
            }
        case '5':
            {
                delete_user();//删除指定用户
                break;
            }
        case '6':
            {
                pthread_mutex_destroy(&mutex);//退出服务器
                close(server_sockfd);
                //close(socketClt);
                exit(0);
            }
        default:
            {
                printf("---输入无效,请重新输入数字1-6---\n");
                break;
            }
        }
        
    }

}
//========================================



//===系统广播=============================
void broadcast(void)
{
    int len;
    char buf_input[1024];
    char buf_send[1024];
            
    memset(buf_input,0,1024);       
    fflush(stdin);//清输入缓存   
    printf("请输入广播内容:");
    scanf("%s",buf_input);
    
    memset(buf_send,0,1024);                
    sprintf(buf_send,"系统公告:\n%s",buf_input);
    len=strlen(buf_send);           
    speak2all(L,buf_send,len);

    printf("---广播已发送---\n");
}
//=========================================


//===踢掉指定用户函数===========================
void kick_off(void)
{
    char c;
    char name_off[10];
    char buf_send[1024];
    int len;
    int socketoff;
loopoff:
                
    memset(name_off,0,10);
    printf("请输入要踢掉的用户名:");
    fflush(stdin);//清输入缓存   
    scanf("%s",name_off);
            
    if((socketoff=LocateUsersoket_L(L,name_off))>0)
    {
        if(send(socketoff,"!t",2,0)==-1)
                perror("send");
        printf("用户:%s已被踢掉线\n",name_off);
    }
    else
    {
        printf("用户名:%s不存在,请重新输入\n",name_off);
        goto loopoff;
    }
    
    //===踢掉线通知===
    memset(buf_send,0,1024);
    sprintf(buf_send,"用户:%s已被踢掉线!\n",name_off);
    len=strlen(buf_send);           
    speak2all(L,buf_send,len);
}
//====================================================





//===删除指定用户信息=================================
void delete_user(void)
{
    char c;
    char name1[10];

loopdel:
            
    memset(name1,0,10);
    printf("请输入要删除的用户名\n");
    printf(":");

    scanf("%s",name1);
    while((c=getchar())!='\n'&&c!=EOF);
                
    if(ListDelete_L(&L,name1)==0)
    {
        if(pthread_mutex_lock(&mutex)<0)
        {
            perror("pthread_mutex_lock");
        }
                    
        update_file(L);
                
        if(pthread_mutex_unlock(&mutex)!=0)
        {
            perror("pthread_mutex_unlock");
        }
                    
        printf("该用户信息已成功删除\n");
    }
    else
    {
        printf("该用户名不存在,请重新输入\n");
        goto loopdel;
    }

}
//=================================================



//===处理客户端-子线程函数==============
void pthread_client(int *p_client_sockfd_n)
{
    //struct user_msg user;
    int sendbytes,recvbytes;
    char buf[10];
    
    while(1)
    {
        memset(buf,0,10);//清空数据收发缓存 
        recvbytes = recvfrom_client(p_client_sockfd_n,buf,10);  //recvbytes = recv(*p_client_sockfd_n,buf,10,0);
        
        //===收到0字节表示客户端断开连接
        if(recvbytes == 0) 
        {   
            printf("\n");
            printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
            printf("\n");           
            close(*p_client_sockfd_n);
            pthread_exit(NULL); 
        }
            
        switch (buf[0])
        {
        case '1'://注册
            {
                printf("\n");
                printf("---注册新的用户中---\n");  
                printf("\n");
                regist_server(p_client_sockfd_n);
                break;
            }
        case '2'://登陆
            {
                printf("\n");
                printf("---客户端client_sockfd=%d登陆中---\n",*p_client_sockfd_n);
                printf("\n");               
                log_in_server(p_client_sockfd_n);
                break;
            }
        case '3'://下线
            {
                printf("\n");
                printf("---客户端client_sockfd=%d退出---\n",*p_client_sockfd_n);
                printf("\n");               
                close(*p_client_sockfd_n);
                pthread_exit(NULL);
                break;
            }
        default:
            {
                printf("\n");
                printf("---客户端client_sockfd=%d发来无法识别的指令信息---\n",*p_client_sockfd_n);
                printf("\n");   
                break;
            }
        }
    }
    
}
//====================================================================


//===注册=========================================
int regist_server(int *p_client_sockfd_n)
{
    struct user_msg user;
    int ret;
    int recvbytes;
    while(1)
    {
        char buf[10];
        char name[10];
        char password[10];
        char flag[2];
        
        //===接收客户端发来的要注册的用户名===
        memset(buf,0,10);       
        recvbytes = recvfrom_client(p_client_sockfd_n,buf,10);
        
        //===收到0字节表示客户端断开连接
        if(recvbytes == 0) 
        {   
            printf("\n");
            printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
            printf("\n");           
            close(*p_client_sockfd_n);
            pthread_exit(NULL); 
        }
        
        //===确认要注册的用户名是否可用===
        ret = LocateName_L(L,buf);
        if( ret == 0)//要注册的用户名已存在
        {
            sendto_client(p_client_sockfd_n,"n",1);         
            printf("\n");
            printf("---要注册的用户名:%s已存在---\n",buf);
            printf("\n");
            continue;
        }
        else//要注册的用户名可用
        {
        
            sendto_client(p_client_sockfd_n,"y",1);
            memset(name,0,10);
            strcpy(name,buf);//记录要注册的用户名
            printf("\n");
            printf("---用户名:%s可以使用---\n",name);
            printf("\n");
        }
        
        //===接收客户端发来的密码===  
        memset(password,0,10);
        recvbytes = recvfrom_client(p_client_sockfd_n,password,10);
        
        //===收到0字节表示客户端断开连接
        if(recvbytes == 0) 
        {   
            printf("\n");
            printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
            printf("\n");           
            close(*p_client_sockfd_n);
            pthread_exit(NULL); 
        }

        //===接收客户端发来的确认注册信息===
        memset(flag,0,2);       
        recvbytes = recvfrom_client(p_client_sockfd_n,flag,2);
        
        //===收到0字节表示客户端断开连接
        if(recvbytes == 0) 
        {   
            printf("\n");
            printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
            printf("\n");           
            close(*p_client_sockfd_n);
            pthread_exit(NULL); 
        }
        
        if(flag[0] == 'y')
        {
            printf("客户端确认注册\n");
            sendto_client(p_client_sockfd_n,"y",1);
            //===保存用户注册信息===
            strcpy(user.name,name);
            strcpy(user.passwd,password);
            user.mysockfd = *p_client_sockfd_n;
            user.state = 0;
            user.channel = -1;
            
            //上快速互斥锁
            if(pthread_mutex_lock(&mutex)<0)
            {
                perror("pthread_mutex_lock");
            }
            
            //===将用户注册信息添加到链表中===
            ListInsert_L(&L,user);
            printf("\n");
            printf("将用户:%s注册信息添加到链表中\n",name);
            printf("\n");
            
            //===将用户注册信息保存到文件中===
            uer2file(user);
            printf("\n");
            printf("将用户:%s注册信息保存到文件中\n",name);
            printf("\n");
        
            //解快速互斥锁
            if(pthread_mutex_unlock(&mutex)!=0)
            {
                perror("pthread_mutex_unlock");
            }
            
            //打印注册成功的用户信息
            printf("\n");
            printf("---注册成功---\n");
            printf("用户名:%s\n",user.name);
            printf("密码:%s\n",user.passwd);
            printf("\n");
        }
        
        if(flag[0] == 'n')
        {
            printf("---取消注册---\n");
            sendto_client(p_client_sockfd_n,"n",1);
        }
        
        break;
    }
    
    return 0;
}
//=================================================





//===登录==========================================
struct user_msg log_in_server(int *p_client_sockfd_n)
{
    struct user_msg user;
    int recvbytes;
    while(1)
    {
        char name[10];
        char password[10];
        char flag[2];
        
        //===接收用户名===
        memset(name,0,10);
        recvbytes = recvfrom_client(p_client_sockfd_n,name,10);
        
        //===收到0字节表示客户端断开连接
        if(recvbytes == 0) 
        {   
            printf("\n");
            printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
            printf("\n");           
            close(*p_client_sockfd_n);
            pthread_exit(NULL); 
        }
        
        
        //===接收密码===    
        memset(password,0,10);
        recvbytes = recvfrom_client(p_client_sockfd_n,password,10);
        
        //===收到0字节表示客户端断开连接
        if(recvbytes == 0) 
        {   
            printf("\n");
            printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
            printf("\n");           
            close(*p_client_sockfd_n);
            pthread_exit(NULL); 
        }
        
        
        //===校验用户名和密码信息===
        
        if(LocateName_L(L,name)==0)//要登陆的用户名有注册
        {
            printf("---登陆用户名:%s有注册---\n",name);
            //根据要登陆的用户名查链表获取用户密码
            user=LocateUser_L(L,name);
            if(strcmp(user.passwd,password) == 0)
            {
                printf("---登陆密码正确---\n");
                sendto_client(p_client_sockfd_n,"y",1); 
            }
            else
            {
                printf("---登陆密码不正确---\n");
                sendto_client(p_client_sockfd_n,"n",1);
                continue;
            }
        }
        else//要登陆的用户名未注册
        {
            printf("---登陆用户名:%s未注册---\n",name);
            sendto_client(p_client_sockfd_n,"n",1);
            continue;
        }
        
        
        //===检查是否在其他地方登陆===
        if(user.state == 1)
        {
            printf("---要登陆的用户名:%s在其他地方登陆---\n",name);
            if(send(user.mysockfd,"!o",2,0)==-1)
                perror("send");
        }
        

        
        //===更新用户信息===
        user.mysockfd=*p_client_sockfd_n;
        user.state=1;
        user.channel=-1;        
        
        //上快速互斥锁
        if(pthread_mutex_lock(&mutex)<0)
        {
            perror("pthread_mutex_lock");
        }
        
        update_list(&L,user);//更新要登陆的用户信息到链表中
        
        //解快速互斥锁
        if(pthread_mutex_unlock(&mutex)!=0)
        {
            perror("pthread_mutex_unlock");
        }
        
        
        chat_server(p_client_sockfd_n,user);//进入聊天模式
        return user;
    }
    
}
//=====================================================================

 

//===聊天==============================================================
int chat_server(int *p_client_sockfd_n,struct user_msg user)
{
    
#if 0
########################################################################
    int i;
    int len;
    int running=1;
    int recvsize;

    char filepath[60];
    int filesocket;
    
    struct tm *pt;
    time_t t;
    
    char option;
    char buf1[MAX_RECVSIZE];
    char buf2[MAX_TEXTSIZE];
    char id[10];
    int destsocket;
    
    
    //===上线通知===
    memset(buf2,0,MAX_TEXTSIZE);
    sprintf(buf2,"%s上线!\n",name);
    len=strlen(buf2);           
    speak2all(L,buf2,len);
    
    
    while(running)
    {
        memset(id,0,10);
        memset(buf1,0,MAX_RECVSIZE);
        memset(buf2,0,MAX_TEXTSIZE);
        memset(filepath,0,60);
        
        if((recvsize=recv(*p_client_sockfd_n,buf1,MAX_RECVSIZE,0))<=0)
        {
            perror("recv");
            printf("用户%s掉线\n",name);
            pthread_exit(NULL);
        }
        
        option=buf1[0];
        
        switch(option)
        {
        case '#':
            {
                time(&t);
                pt = localtime(&t);

                sprintf(buf2,"%s对大家说:(%02d:%02d:%02d)\n%s\n",name,pt->tm_hour,pt->tm_min,pt->tm_sec,buf1+1);
                len=strlen(buf2);
                
                speak2all(L,buf2,len);
                break;
            }
        case '$':
            {
                time(&t);
                pt = localtime(&t);
            
                for(i=1;((buf1[i]!=' ')&&(i<MAX_RECVSIZE));i++)
                {
                    id[i-1]=buf1[i];
                }
                
                if((LocateName_L(L,id)<0)||(i==1)||(i>=MAX_RECVSIZE))
                {
                    //printf("输入用户名不存在\n");
                    break;
                }
                
                destsocket=LocateUsersoket_L(L,id);
                
                sprintf(buf2,"%s对你说:(%02d:%02d:%02d)\n%s\n",name,pt->tm_hour,pt->tm_min,pt->tm_sec,buf1+i);
                
                len=strlen(buf2);
                
                sendto_client(&destsocket,buf2,len+1);
                break;
            }

        case ':':
            {
                if(buf1[1]=='0')
                {
                    change_passwd(socketCtl2,name);
                }
                else if(buf1[1]=='1')
                {
                    
                    Getuseronline_client(L,*socketCtl2);
                }
                else if(buf1[1]=='2')
                {
                    
                    Getalluser_client(L,*socketCtl2);
                }
                else if(buf1[1]=='q')
                {
                    running=0;
                    sendto_client(socketCtl2,"!q",3);
                    continue;
                }
                else if(buf1[1]=='y')
                {
                    filesocket=atol(buf1+2);

                    sendto_client(&filesocket,"!y",3);
                    
                }
                else if(buf1[1]=='n')
                {
                    filesocket=atol(buf1+2);
                    
                    sendto_client(&filesocket,"!n",3);
                }
                else if(buf1[1]=='s')
                {
                    sendfile_client(*socketCtl2,destsocket);
                }
                else if(buf1[1]=='t')
                {
                    //===更新被踢掉的用户信息===
                    user.state = 0;     
                    //上快速互斥锁
                    if(pthread_mutex_lock(&mutex)<0)
                    {
                        perror("pthread_mutex_lock");
                    }
                    update_list(&L,user);//更被踢掉的用户信息到链表中
                    //解快速互斥锁
                    if(pthread_mutex_unlock(&mutex)!=0)
                    {
                        perror("pthread_mutex_unlock");
                    }
                    printf("---%s对应线程已退出---\n",name);
                    pthread_exit(NULL);
                }
                else if(buf1[1]=='g')
                {
                    
                    //group_chat(socketCtl2,name);
                }

                break;
            }
        default:
            {
                //printf("default\n");
                break;
            }
            
        }   
        
    }
######################################################################################
#endif

    int i;
    int len;    
    int running=1;//运行标志    
    int recvbytes;  

    struct tm *pt;
    time_t t;
    
    char buf_recv[1024];
    char buf_send[1024];
    char option;
    
    char dest_id[10];//记录目标用户名
    int dest_socket;//记录聊天目标socket
    
    char myname[10];
    memset(myname,0,10);
    strcpy(myname,user.name);
    
    char file_from_path[1024];//文件来源路径
    int file_to_socket;//文件接收目标socket
    int file_from_socket;//文件接收目标socket
    
    //---上线通知---------------------------------------------------------------
    memset(buf_send,0,1024);
    sprintf(buf_send,"%s上线!\n",myname);
    len=strlen(buf_send);           
    speak2all(L,buf_send,len);
    //--------------------------------------------------------------------------
    
    
    while(running)
    {
        //---接收客户端发来的字符串信息(@aaa:hello)-----------------------------
        memset(buf_recv,0,1024);
        recvbytes = recvfrom_client(p_client_sockfd_n,buf_recv,1024);
        //----------------------------------------------------------------------
        
        
        //---判断客户端是否断开连接---------------------------------------------
        if(recvbytes == 0) //收到0字节表示客户端断开连接
        {   
            printf("\n");
            printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
            printf("\n");   
            
            //---更新掉线用户状态信息---
            user.state=0;   
        
            if(pthread_mutex_lock(&mutex)<0)//上快速互斥锁
            {
                perror("pthread_mutex_lock");
            }
        
            update_list(&L,user);//更新掉线用户状态信息到链表中
        
            if(pthread_mutex_unlock(&mutex)!=0)//解快速互斥锁
            {
                perror("pthread_mutex_unlock");
            }
            
            //---群发用户掉线通知---
            memset(buf_send,0,1024);
            sprintf(buf_send,"%s掉线!\n",myname);
            len=strlen(buf_send);           
            speak2all(L,buf_send,len);  
            
            close(*p_client_sockfd_n);
            pthread_exit(NULL); 
            
        }
        //-------------------------------------------------------------------
        
        
        //---从客户端发来的字符串信息中截取第一个标识字符(@aaa:hello)--------
        
        option=buf_recv[0];
        
        switch(option)//对标识字符进行判断
        {
            case '#'://#表示群聊
            {
                time(&t);
                pt = localtime(&t);
                sprintf(buf_send,"%s对大家说:(%02d:%02d:%02d)\n%s\n",myname,pt->tm_hour,pt->tm_min,pt->tm_sec,buf_recv+1);//+1去掉#号
                len=strlen(buf_send);
                speak2all(L,buf_send,len);
                break;
            }
                
            case '@'://@表示私聊
            {
                time(&t);
                pt = localtime(&t);
                
                //---从客户端发来的字符串信息中截取目标用户名(@aaa:hello)---
                memset(dest_id,0,10);
                for(i=1;((buf_recv[i]!=':')&&(i<1024));i++)//遇到:结束
                {
                    dest_id[i-1]=buf_recv[i];
                }
                //-----------------------------------------------
                
                //---查看用户名是否存在----------------------------------
                if((LocateName_L(L,dest_id)<0)||(i==1)||(i>=1204))
                {
                    printf("输入用户名不存在\n");
                    break;
                }
                
                //根据目标用户确定目标socket
                dest_socket=LocateUsersoket_L(L,dest_id);
                
                //截取聊天信息加1去掉冒号(@aaa:hello) buf_recv+i+1
                sprintf(buf_send,"%s对你说:(%02d:%02d:%02d)\n%s\n",myname,pt->tm_hour,pt->tm_min,pt->tm_sec,buf_recv+i+1);
                
                len=strlen(buf_send);
                
                sendto_client(&dest_socket,buf_send,len);
                break;
            }

            case '$'://$表示要发送文件
            {

                //---从客户端发来的字符串信息中截取目标用户名($aaa:hello.txt)---
                memset(dest_id,0,10);
                for(i=1;((buf_recv[i]!=':')&&(i<1024));i++)//遇到:结束
                {
                    dest_id[i-1]=buf_recv[i];
                    
                }
                //-----------------------------------------------
                
                //---查看目标用户名是否存在--------------------------
                if((LocateName_L(L,dest_id)<0)||(i==1)||(i>=1204))
                {
                    printf("输入用户名不存在\n");
                    break;
                }
                
                //根据目标用户名确定文件接受目标file_to_socket
                file_to_socket=LocateUsersoket_L(L,dest_id);
                
                
                //截取要文件来源路径加1去掉冒号($aaa:hello.txt) buf_recv+i+1
                strcpy(file_from_path,buf_recv+i+1);//保存文件来源路径
                sprintf(buf_send,"!%d!%s向你发送文件:%s 是否接受(y/n)?\n",*p_client_sockfd_n,myname,buf_recv+i+1);
                //sprintf(buf_send,"!e!%s向你发送文件:%s 是否接受(y/n)?\n",myname,buf_recv+i+1);
                len=strlen(buf_send);
                sendto_client(&file_to_socket,buf_send,len);

                break;
            }
            
            case ':'://:表示功能选项
            {
                if(buf_recv[1]=='1')//客户端要查看在线用户
                {
                    
                    Getuseronline_client(L,*p_client_sockfd_n);
                }
                
                else if(buf_recv[1]=='2')//客户端要查看所有用户
                {
                    
                    Getalluser_client(L,*p_client_sockfd_n);
                }
                
                else if(buf_recv[1]=='3')//客户端要修改密码
                {
                    
                    change_passwd(p_client_sockfd_n,myname);
                }
                
                else if(buf_recv[1]=='4')//客户端要查看聊天记录
                {
                    
                    sendto_client(p_client_sockfd_n,"!c",3);
                }
                
                else if(buf_recv[1]=='5')
                {
                    
                    //group_chat(socketCtl2,myname);//群组聊天
                }
                
                else if(buf_recv[1]=='6')
                {
                    running=0;
                    sendto_client(p_client_sockfd_n,"!q",3);
                    continue;
                }
                
                else if(buf_recv[1]=='y')//文件接收者发来(:y1)
                {
                    file_from_socket=atol(buf_recv+2);

                    sendto_client(&file_from_socket,"!y",3);
                    
                }
                
                else if(buf_recv[1]=='n')
                {
                    //filesocket=atol(buf_recv+2);
                    
                    //sendto_client(&filesocket,"!n",3);
                }
                
                else if(buf_recv[1]=='s')
                {
                    sendfile_client(*p_client_sockfd_n,file_to_socket);
                }
                
                else if(buf_recv[1]=='t')
                {
                    //===更新被踢掉的用户信息===
                    user.state = 0;     
                    //上快速互斥锁
                    if(pthread_mutex_lock(&mutex)<0)
                    {
                        perror("pthread_mutex_lock");
                    }
                    update_list(&L,user);//更被踢掉的用户信息到链表中
                    //解快速互斥锁
                    if(pthread_mutex_unlock(&mutex)!=0)
                    {
                        perror("pthread_mutex_unlock");
                    }
                    printf("---%s对应线程已退出---\n",myname);
                    pthread_exit(NULL);
                }
                break;
            }
            
            default:
            {
                printf("---用户:%s发来无法识别的聊天信息---\n",myname);
                break;
            }           
            
            
        }
        //sendto_client(p_client_sockfd_n,buf_chat,recvbytes);
    }

    return 0;
}
//======================================================================

 
 
 
//===文件收发函数=========================================================
void sendfile_client(int file_from_socket,int file_to_socket)
{
    printf("---服务器正在发送文件---\n");

    sendto_client(&file_to_socket,"!j",3);
    sleep(1);

    int recvsize;
    char recvbuf[1024]; 
    
    while(1)
    {
        memset(recvbuf,0,1024);
        if((recvsize=recv(file_from_socket,recvbuf,1024,0))<0)
        {
            perror("recv");
        }

        sendto_client(&file_to_socket,recvbuf,recvsize);

        if(!strncmp(recvbuf,"end",3))
        {
            break;
        }
    }

    printf("---文件发送完毕---\n");
    
}
//===========================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
//===客户端要获取在线用户信息=================================================
void Getuseronline_client(LinkList L,int p_client_sockfd_n)
{
    int len;
    char buf[500];
    memset(buf,0,500);
    LinkList p;
    p=L->next;
    
    sprintf(buf,"%s\n","~~~~~~在线用户~~~~~~");
                       
    while(p!=NULL)
    {
        if(p->user.state==1)
        {
            sprintf(buf,"%s%s\n",buf,p->user.name);
            
        }
        p=p->next;
    }

    sprintf(buf,"%s%s\n",buf,"~~~~~~~~~~~~~~~~~~~~");
    
    len=strlen(buf);
    if(send(p_client_sockfd_n,buf,len,0)==-1)
        perror("send");
    
}
//===================================================================
 
 
 //===客户端要获取所有用户信息========================================
 void Getalluser_client(LinkList L,int p_client_sockfd_n)
{
    int len;
    char buf[500];
    memset(buf,0,500);
    LinkList p;
    p=L->next;
    
    sprintf(buf,"%s\n","~~~~~~所有用户~~~~~~");
    
    while(p!=NULL)
    {
        sprintf(buf,"%s%s\n",buf,p->user.name);
        p=p->next;
    }

    sprintf(buf,"%s%s\n",buf,"~~~~~~~~~~~~~~~~~~~~");
    
    len=strlen(buf);
    if(send(p_client_sockfd_n,buf,len,0)==-1)
        perror("send");
    
}
//===============================================================
 
 

//===客户端要修改密码==========================================
static void change_passwd(int *p_client_sockfd_n,char myname[])
{
    sendto_client(p_client_sockfd_n,"!m",3);
    
    struct user_msg user;
    user=LocateUser_L(L,myname);

    int  sendbytes;
    char buf_recv[1024];
    char buf_send[1024];
    

    
    while(1)
    {
        
        printf("---用户:%s修改密码中---\n",myname);
        
        //---发送提示信息到客户端----------------------------
        memset(buf_send,0,1024);
        strcpy(buf_send,"---修改密码中---\n");
        sendbytes=strlen(buf_send);
        sendto_client(p_client_sockfd_n,buf_send,sendbytes);
        //---------------------------------------------------
        
        //---发送提示信息到客户端----------------------------
        memset(buf_send,0,1024);
        strcpy(buf_send,"---请输入旧密码---\n");
        sendbytes=strlen(buf_send);
        sendto_client(p_client_sockfd_n,buf_send,sendbytes);
        //---------------------------------------------------       
        
        
        //---接收客户端发来的旧密码---------------------------
        memset(buf_recv,0,1024);
        recvfrom_client(p_client_sockfd_n,buf_recv,1024);
        //----------------------------------------------------
        
        //---验证旧密码-----------------------------------------
        if(strcmp(user.passwd,buf_recv)==0)
        {
            //---发送提示信息到客户端----------------------------
            memset(buf_send,0,1024);
            strcpy(buf_send,"---旧密码验证正确---\n");
            sendbytes=strlen(buf_send);
            sendto_client(p_client_sockfd_n,buf_send,sendbytes);
            //---------------------------------------------------
            break;
        }
        else
        {
            //---发送提示信息到客户端----------------------------
            memset(buf_send,0,1024);
            strcpy(buf_send,"---旧密码错误,请重新输入旧密码---\n");
            sendbytes=strlen(buf_send);
            sendto_client(p_client_sockfd_n,buf_send,sendbytes);
            //---------------------------------------------------
            continue;
        }
        //--------------------------------------------------------
        
    }
    
    
    //---发送提示信息到客户端----------------------------
    memset(buf_send,0,1024);
    strcpy(buf_send,"---请输入新密码---\n");
    sendbytes=strlen(buf_send);
    sendto_client(p_client_sockfd_n,buf_send,sendbytes);
    //---------------------------------------------------

    //---接收客户端发来的新密码---------------------------
    memset(buf_recv,0,1024);
    recvfrom_client(p_client_sockfd_n,buf_recv,1024);
    //----------------------------------------------------

    memset(user.passwd,0,10);
    strcpy(user.passwd,buf_recv);

    
    //-------------------更新用户信息------------------------
    if(pthread_mutex_lock(&mutex)<0)
    {
        perror("pthread_mutex_lock");
    }
    
    update_list(&L,user);//更新用户信息到链表中
    update_file(L);//更新用户信息文件
    
    if(pthread_mutex_unlock(&mutex)!=0)
    {
        perror("pthread_mutex_unlock");
    }
    //--------------------------------------------------------------

    //---发送提示信息到客户端----------------------------
    memset(buf_send,0,1024);
    strcpy(buf_send,"---修改密码成功---");
    sendbytes=strlen(buf_send);
    sendto_client(p_client_sockfd_n,buf_send,sendbytes);
    //---------------------------------------------------

}
//======================================================================
 
 
 
//===群聊函数==========================================================
void speak2all(LinkList L,char *buf,int len)
{
    LinkList p;
    p=L->next;
    while (p!=NULL)
    {
        if(p->user.state==1)
        {
            if(send((p->user.mysockfd),buf,len,0)==-1)
                perror("send");
        }   
        p=p->next;
    }
}
//==================================================================== 
 

 

//===从指定客户端接收一条信息==========================================
int recvfrom_client(int *p_client_sockfd_n,char *buf,int length)
{
    int recvbytes;
    recvbytes = recv(*p_client_sockfd_n,buf,length,0);
    if(recvbytes == -1)
    {
        perror("recv");
        exit(1);
    }
    printf("\n");
    printf("---收到一条[%d]字节的信息(信息来源:client_sockfd=%d的客户端)---\n",recvbytes,*p_client_sockfd_n);
    printf("信息内容:%s\n",buf);
    printf("\n");
    return recvbytes;
}
//====================================================================



//===发送一条信息给指定客户端=================================== 
int sendto_client(int *p_client_sockfd_n,char *buf,int length)
{
    int sendbytes;
    sendbytes = send(*p_client_sockfd_n,buf,length,0);
    if(sendbytes == -1)
    {
        perror("recv");
        exit(1);
    }
    printf("\n");
    printf("---发送一条[%d]字节的信息(信息目的:client_sockfd=%d客户端)---\n",sendbytes,*p_client_sockfd_n); 
    printf("信息内容:%s\n",buf);
    printf("\n");
    return sendbytes;
}
//====================================================================





client.c

//===包含的头文件=========
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
//=======================


//===定义的宏============
//#define MSG_MAXSIZE 1024//消息队列正文最大字节数
//#define REC_PATH "/home/"
//=======================


//===定义的全局变量=========
struct message
{
    long msg_type;
    char msg_text[1024];
};

int client_sockfd;
int qid_msg;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//mutex互斥锁

char default_path[50];//用户默认路径
char chatdata_path[50];//聊天记录二进制文件存放路径
char chatdata_txt_path[50];//聊天记录存文本文件放存放路径
char file_from_socket[6];//保存文件来源file_from_socket
volatile int confirm,yes,no,enable,inputdone,changeflag,chating;//标志位

//==================================



//===函数声明=======================
void primary_menu(void);
void paint_primary_menu(void);
void pthread_client(void);
int regist_client(void);
int sendto_server(char *buf,int length);
int recvfrom_server(char *buf,int length);
int log_in_client(void);
int chat_client(void);
static void log_out(void);
void callmenu(int signo);
void view_chat(void);
//==================================



//===主函数===============
int main(int argc,char *argv[])
{
    //---参数合法性判断----------------------------
    if(argc<4)
    {
        printf("\n");
        printf("---请按以下示例输入---\n");
        printf("./client_cwy 127.0.0.1 3333 11\n");
        printf("\n");
        exit(1);
    }
    //---------------------------------------------
    
        
    pthread_mutex_init(&mutex,NULL);//初始化互斥锁
    
    
    //---创建消息队列------------------------------------------
    key_t key;
    key = (key_t)atol(argv[3]);//通过main函数参数传进消息队列ID
    qid_msg = msgget(key,IPC_CREAT|0666);   
    if(qid_msg == -1)
    {
        perror("msgget");
        exit(1);
    }
    printf("\n");
    printf("---创建消息队列成功qid_msg=%d---\n",qid_msg);
    printf("\n");
    //-------------------------------------------------------------
    
    
    //---创建socket------------------------------------------------
    client_sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(client_sockfd == -1)
    {
        perror("socket");
        exit(1);
    }
    printf("\n");
    printf("---创建socket成功!client_sockfd=%d---\n",client_sockfd);
    printf("\n");
    //-------------------------------------------------------------------------
    
    //---配置服务器地址结构体--------------------------------------------------
    struct sockaddr_in server_sockaddr;
    unsigned short int port;
    
    port = (unsigned short int)atol(argv[2]);//通过main函数参数传进服务器端口号//atol()能将字符串转换成长整形数
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_port = htons(port);//htons()主机字节顺序转换为网络字节顺序(对无符号短型进行操作2bytes)
    //inet_addr()能将一个用数字和点表示IP 地址的字符串转换成一个无符号长整型。
    //htonl()主机字节顺序转换为网络字节顺序(对无符号长型进行操作4bytes)
    server_sockaddr.sin_addr.s_addr = inet_addr(argv[1]);//通过main函数参数传进服务器IP
    bzero(&(server_sockaddr.sin_zero),8);//字节清零补齐保证sockaddr_in和sockaddr大小一样
    //------------------------------------------------------------------------------------
    
    
    //---连接服务器---------------------------------------------------------------------------
    int ret;
    ret = connect(client_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr));
    if(ret == -1)
    {
        perror("connect");
        exit(1);
    }
    printf("\n");
    printf("---连接服务器成功!---\n");
    printf("\n");
    //-----------------------------------------------------------------------------------------
    
    primary_menu();//进入主菜单
    
    //exit(0);
}
//======================================================



//===画主菜单函数========================================
void paint_primary_menu(void)
{
    printf("\n");
    printf("======主菜单======\n");
    printf("[1]注册           \n");
    printf("[2]登录           \n");
    printf("[3]下线           \n");
    printf("==================\n");
    printf("\n");
}
//========================================================




//===主菜单===============================================
void primary_menu(void)
{
    char buf_option[10];
    char option;
    while(1)
    {
        paint_primary_menu();//画主菜单
        
        //---接收键盘输入------------------
        memset(buf_option,0,10);
        fflush(stdin);//清缓存
        printf("请输入数字1-3进行选择:");
        scanf("%s",buf_option);
        //--------------------------------
        
        option = buf_option[0];
        
        switch (option)
        {
        case '1':
            {
                printf("\n");
                printf("---注册中---\n");
                printf("\n");               
                sendto_server("1",1);
                regist_client();//注册新的用户
                break;
            }
        case '2':
            {
                printf("\n");
                printf("---登陆中---\n");  
                printf("\n");
                sendto_server("2",1);
                log_in_client();//用户登陆
                break;
            }
        case '3':
            {
                printf("\n");
                printf("---退出---\n");
                printf("\n");
                sendto_server("3",1);
                log_out();//退出客户端
                break;
            }
        default:
            {   
                printf("\n");
                printf("---输入无效,请重新输入数字1-3!---\n");
                printf("\n");
                break;
            }
        }
    }


}
//======================================================



//===客户端退出函数================================================OK  
static void log_out(void)
{
    close(client_sockfd);
    pthread_mutex_destroy(&mutex);
                
    if(msgctl(qid_msg, IPC_RMID, 0) == -1)//删除消息队列
    {
        fprintf(stderr, "msgctl failed\n");
        exit(EXIT_FAILURE);
    }
                
    exit(0);
}
//==========================================================




//===注册新的用户=======================================
int regist_client(void)
{
    char name[10];
    char password[10];
    char buf1[10];
    char buf2[10];
    char flag[2];
    int len;
    
    while(1)
    {
        //---输入要注册的用户名--------
        memset(name,0,10);      
        fflush(stdin);//清输入缓存   
        printf("请输入要注册的用户名:");
        scanf("%s",name);
        //-----------------------------
        
        //---发送要注册的用户名给服务器---
        len=strlen(name);
        sendto_server(name,len);
        //-------------------------------
        
        //---等待服务器确认要注册的用户名是否可用----
        memset(flag,0,2);
        recvfrom_server(flag,1);

        if(flag[0] == 'n')//要注册的用户名不可用返回重新输入
        {   
            printf("\n");
            printf("---要注册的用户名:%s已被注册,请重新输入---\n",name);
            printf("\n");
            continue;
        }
        
        if(flag[0] == 'y')//要注册的用户名可以使用继续往下走
        {
            printf("\n");       
            printf("---要注册的用户名:%s可以使用---\n",name);
            printf("\n");
        }
        //-------------------------------------------------------
        
        //---输入密码--------------------------------------------
loop_passsword1:    

        //---第一次输入密码---
        memset(buf1,0,10);
        fflush(stdin);//清缓存     
        printf("请输入密码:");
        scanf("%s",buf1);
        //--------------------
        
        //---第二次输入密码---
        memset(buf2,0,10);
        fflush(stdin);//清缓存     
        printf("请再次输入密码:");
        scanf("%s",buf2);
        //--------------------
        
        //---比较两次输入的密码是否一致---
        if(strcmp(buf1,buf2) != 0)
        {
            printf("\n");
            printf("---两次输入密码不一致请重新输入---\n");
            printf("\n");
            goto loop_passsword1;//两次输入密码不一致返回重新输入          
        }
        //
        
        //---发送密码到服务器-------
        strcpy(password,buf1);
        len=strlen(password);
        sendto_server(password,len);
        //--------------------------
        
        //---输入确认注册信息-------
        memset(flag,0,2);       
        fflush(stdin);//清输入缓存   
        printf("确认注册?y or n:");
        scanf("%s",flag);
        //---------------------------
        
        //---将确认注册信息发送给服务器---   
        if(flag[0] == 'n')
        {
            sendto_server("n",1);
        }
        if(flag[0] == 'y')
        {
            sendto_server("y",1);
        }
        //-------------------------------
        
        //---等待服务器确认注册成功---
        memset(flag,0,2);
        recvfrom_server(flag,1);
        
        if(flag[0] == 'n')
        {
            printf("\n");
            printf("---取消注册---\n");
            printf("\n");
        }       
        
        if(flag[0] == 'y')
        {
        printf("\n");
        printf("---注册成功---\n");
        printf("\n");
        printf("请记住您的用户名和密码\n");
        printf("\n");
        printf("用户名:%s\n",name);
        printf("密码:%s\n",password);
        printf("\n");
        }
        //---------------------------------------------
        
        break;
    }
    
    return 0;//返回主菜单
}
//======================================================






//===登陆===============================================
int log_in_client(void)
{
    int len;
    int i;
    char name[10];
    char password[10];
    char flag[2];   
    
    while(1)
    {       
        //---将要登陆的用户名发送给服务器---
        memset(name,0,10);      
        fflush(stdin);//清输入缓存   
        printf("请输入要登陆的用户名:");
        scanf("%s",name);       

        len=strlen(name);
        if(len == 0)
        {
            printf("---未输入要登陆的用户名---\n");
            continue;           
        }
        else
        {
            sendto_server(name,len);    
        }
        //-------------------------------
        
        
        //---将要登陆的密码发送给服务器----
loop_passsword2:
        
        memset(password,0,10);
        fflush(stdin);//清输入缓存   
        printf("请输入要登陆的密码:");
        scanf("%s",password);       

        len=strlen(password);
        if(len == 0)
        {
            printf("---未输入要登陆的密码---\n");
            goto loop_passsword2;           
        }
        else
        {
            sendto_server(password,len);        
        }
        //----------------------------------------------------------
        
        
        
        //---等待服务器响应--------------------------------
        memset(flag,0,2);
        recvfrom_server(flag,1);
        
        if(flag[0] == 'n')
        {
            printf("---密码或用户名错误,请重新输入---\n");
            continue;
        }
        if(flag[0] == 'y')
        {
            printf("---登录成功---\n");
        }
        //--------------------------------------------------
        
        
        //---登录成功创建用户目录及聊天记录文档---
        //---创建用户目录-------------------------
        sprintf(default_path,"%s",name);
        //sprintf(default_path,"%s%s",REC_PATH,name);
        if(mkdir(default_path,0777)<0)
        {
            perror("mkdir");
        }
        //--------------------------------------
        
        //---创建聊天记录二进制文件-------------
        int chat_fd;
        sprintf(chatdata_path,"%s%s",default_path,"/chat_data");
        if((chat_fd=open(chatdata_path,O_CREAT|O_APPEND|O_RDWR,0666))<0)
        {
            perror("open");
        }
        //--------------------------------------
        
        
        //---创建聊天记录文本文件----------------
        sprintf(chatdata_txt_path,"%s%s",default_path,"/chat_data.txt");
        
        FILE *chat_fp;
        
        //追加打开一个文本文件,并在文件末尾写数据,若不存在则创建
        if((chat_fp=fopen(chatdata_txt_path,"at+")) == NULL)
        {
            printf("cannot open file\n");
            exit(1);
        }
        if(fputs("-----------新登录---------------",chat_fp) == EOF)//测试
        {
            printf("file write error\n");
        }
        if(fputs("\n",chat_fp) == EOF)
        {
            printf("file write error\n");
        }
        fclose(chat_fp);//记得关掉文件否则写入的信息不保存
        //-----------------------------------------
                
        printf("---当前用户:%s聊天信息保存在%s目录下---\n",name,default_path);
        //-----------------------------------------------------------------------------
        
        chat_client();//进入聊天模式

        return 0;
    }
}
//=====================================================================================



//===聊天===============================================================================
int chat_client(void)
{
    view_chat();//进入聊天模式后先查看以前的聊天信息
    
    //---创建子进程接受服务器的信息转发给显示端-------------------
    pthread_t pid;
    if(pthread_create(&pid,NULL,(void *)pthread_client,NULL) < 0)
    {
        perror("pthread_create");
        exit(1);
    }
    printf("---创建子进程接受服务器的信息转发给显示端---\n");
    //-----------------------------------------------------------
    
    
    
    //---主线程-接收键盘输入并发送给服务器-----------------------
    
    int sendbytes;
    char buf_chat[1024];
    //char option;
    //int i;
    chating=1;
    
    signal(SIGQUIT,callmenu);//ctrl+\ 呼唤菜单
    
    while(chating)
    {   
        //---从键盘接收输入聊天信息---
        memset(buf_chat,0,1024);
        fflush(stdin);//清输入缓存   
        printf("请输入聊天信息:");
        scanf("%s",buf_chat);   
        sendbytes=strlen(buf_chat);
        //-----------------------------
        
        //---发送聊天信息到服务器----------
        sendto_server(buf_chat,sendbytes);
        //---------------------------------
        
        #if 0
*******************************************************************     
        //===文件发送========================
        if(confirm==1)
        {
            option=buf[0];
            switch(option)
            {
            case 'y':
                {
                    yes=1;
                    continue;
                }
            case 'n':
                {
                    no=1;
                    continue;
                }
            case '/':
                {
                    memset(dest_path,0,50);

                    strcpy(dest_path,buf);
                    
                    for(i=0;i<50;i++)
                    {
                        if(dest_path[i]=='\n')
                            dest_path[i]=0;
                    }
                    inputdone=1;
                    continue;
                }
            default:
                {
                    printf("输入选项有误,请重新输入\n");
                    continue;
                }
                
            }
            
        }

        if(buf_chat[0] == '@')
        {
            for(i=1;((buf_chat[i]!=' ')&&(i<50));i++)
            {
                
            }
            
            if((i==1)||(i>=50)||(buf_chat[i+1]==' '))
            {
                printf("输入格式有误,请重新输入\n");
                continue;
            }
            
            strcpy(source_path,buf_chat+i+1);
            
            for(i=0;i<50;i++)
            {
                if(source_path[i]=='\n')
                    source_path[i]=0;
            }
        }

        //=================================================
************************************************************************        
        #endif
        
    }
    
    return 0;
}
//===================================================================





//===创建子进程接受服务器的信息转发给显示端====================================OK
void pthread_client(void)
{   
    int sendbytes,recvbytes;
    int i;
    int len;    
    char flag;
    char buf_recv[1024];
    
    struct message msg;
    msg.msg_type = 1;//消息类型 1; 或者getpid();都可以
    

    
    
    //char filename[50];    
    
    
    
    //---打开聊天记录文件----------------------------------------
    int chat_fd;
    if((chat_fd=open(chatdata_path,O_APPEND|O_WRONLY,0666))<0)//二进制文件
    {
        perror("open");
    }
    
    FILE *chat_fp;//文本文件
    //-----------------------------------------------------------
    
    
    chating=1;//表示聊天模式
        
    while(chating)
    {
        
        //---接收服务器发来的信息-----------------------------
        memset(buf_recv,0,1024);//清空数据收发缓存
        recvbytes = recvfrom_server(buf_recv,1024);
        //-----------------------------------------------------
        
        
        //---确认服务器是否还在运行----------------------------
        if(recvbytes == 0) //收到0字节表示与服务器断线
        {   
            printf("\n");
            printf("---与服务器断线---\n");
            printf("\n");           
            log_out();
        }
        //-----------------------------------------------------
        
        
        //---截取服务器发来的字符串信息中的特殊标识位(!t)-----------
        flag=buf_recv[0];
        
        if(flag == '!')
        {
        
            if(buf_recv[1]=='c')//查看聊天记录
            {
                printf("---查看聊天记录---\n");
                view_chat();
            }   
            
            if(buf_recv[1]=='m')//修改密码
            {
                printf("---修改密码中---\n");
                printf("请输入旧密码:");
                
            }   
            
            if(buf_recv[1]=='o')//在其他地方登陆
            {
                printf("---您已在其他地方登陆---\n");
                log_out();
            }
            
            else if(buf_recv[1]=='t')//服务器要踢掉本用户
            {
                printf("---您已被服务器踢下线---\n");
                sendto_server(":t",2);//告诉服务器退出对应处理客户端的子线程
                log_out();//退出客户端
            }
            
            else if(buf_recv[1]=='q')//注销登录
            {
                chating = 0;
                log_out();//退出客户端
            }   
            
            if(buf_recv[1]=='j')//修改密码
            {
                printf("---接收文件中---\n");
                //recv_file();
            }
            
            //---保存文件来源file_from_socket-------------------------------------
            else if((buf_recv[1]>='0')&&(buf_recv[1]<='9'))
            {
                
                memset(file_from_socket,0,6);
                for(i=1;i<1024;i++)
                {
                    if(buf_recv[i]!='!')
                    {
                        file_from_socket[i-1]=buf_recv[i];
                    }
                    else if(buf_recv[i]=='!')
                    {
                        printf("---文件来源file_from_socket=%s---\n",file_from_socket+i+1);
                        //---将服务器发来的信息转发给显示窗口---------------------
                        memset(msg.msg_text,0,1024);
                        strcpy(msg.msg_text,buf_recv);
                        sendbytes = strlen(msg.msg_text);   
                        if(msgsnd(qid_msg, (void *)&msg, sendbytes, 0) == -1)
                        {
                            perror("msgsnd");
                            exit(1);
                        }
                        //------------------------------------------------------
                        break;
                    }
                }
                continue;
            }
            //-------------------------------------------------------------
            
            
            else if(buf_recv[1]=='y')
            {
                printf("对方已同意接收,正在发送...\n");
                //send_file(1);
            }
            
            else if(buf_recv[1]=='n')
            {
                printf("---对方拒绝接收,已取消---\n");
                //memset(source_path,0,50);
            }
            
            /*
            else if(buf_recv[1]=='f')
            {
                recv_file();
            }
            else if(buf_recv[1]=='g')
            {
                memset(filename,0,50);
                strcpy(filename,buf_recv+2);

                sprintf(dest_path,"%s/%s",default_path,filename);//默认接收到的文件目录

                recv_file();
            }
            else if(buf_recv[1]=='c')
            {
                while(changeflag);
            }
            */
            continue;
        }   
        //--------------------------------------------------------
        
        
        //---将服务器发来的信息转发给显示窗口---------------------
        memset(msg.msg_text,0,1024);
        strcpy(msg.msg_text,buf_recv);
        sendbytes = strlen(msg.msg_text);   
        if(msgsnd(qid_msg, (void *)&msg, sendbytes, 0) == -1)
        {
            perror("msgsnd");
            exit(1);
        }
        //------------------------------------------------------
        
        
        
        //---将聊天信息写入文件中--------------------------------
        if(pthread_mutex_lock(&mutex)<0)//上快速互斥锁
        {
            perror("pthread_mutex_lock");
        }
        
        len=strlen(buf_recv);
        if(write(chat_fd,buf_recv,len)<0)//写入二进制文件
        {
            perror("write");
        }
        
        
        //---追加打开一个文本文件,并在文件末尾写数据,若不存在则创建----------
        if((chat_fp=fopen(chatdata_txt_path,"at+")) == NULL)
        {
            printf("cannot open file\n");
            exit(1);
        }           
        if(fputs(buf_recv,chat_fp) == EOF)//写入文本文件
        {
            printf("file write error\n");
        }
        if(fputs("\n",chat_fp) == EOF)//加'\n'才会写进去
        {
            printf("file write error\n");
        }
        fclose(chat_fp);//写完要记得关闭文件否则文件为空
        //-----------------------------------------------------------------
        
        if(pthread_mutex_unlock(&mutex)!=0)//解快速互斥锁
        {
            perror("pthread_mutex_unlock");
        }       
        //-------------------------------------------------------
        
        //printf("---转发一条信息到显示端---\n");
    }
}
//======================================================




//===查看聊天记录===========================================
void view_chat(void)
{
    int readsize;
    int chat_fd;
    char buf[1024];
    memset(buf,0,1024);;

    struct message  msg1;
    msg1.msg_type=1;
    memset(msg1.msg_text,0,1024);
    
    if((chat_fd=open(chatdata_path,O_RDONLY,0666))<0)
    {
        perror("open");
    }
    
    system("clear");//清屏
    strcpy(msg1.msg_text,"————————————————聊天记录起始————————————————\n");

    if(msgsnd(qid_msg, (void *)&msg1, 1024, 0) == -1)
    {
        perror("msgsnd");
        exit(-1);
    }           
    

    while(1)
    {
        memset(msg1.msg_text,0,1024);
        
        if(pthread_mutex_lock(&mutex)<0)
        {
            perror("pthread_mutex_lock");
        }
        
        if((readsize=read(chat_fd,msg1.msg_text,1024))<0)
        {
            perror("read");
        }

        if(pthread_mutex_unlock(&mutex)!=0)
        {
            perror("pthread_mutex_unlock");
        }

        if(readsize==0)
        {
            break;
        }

        if(msgsnd(qid_msg, (void *)&msg1, 1024, 0) == -1)
        {
            perror("msgsnd");
            exit(-1);
        }           

    }

    memset(msg1.msg_text,0,1024);

    strcpy(msg1.msg_text,"————————————————聊天记录结束————————————————\n");
    
    if(msgsnd(qid_msg, (void *)&msg1, 1024, 0) == -1)
    {
        perror("msgsnd");
        exit(-1);
    }           
    
}
//====================================================================================





//===功能菜单===============================================
void callmenu(int signo)
{
    printf("---AAAAAAAAAAAAA---\n");

    //char option;
    //char c;
    
    //int menuflag=1;
    
    struct message  msg;
    msg.msg_type = 3;

    printf("---进入功能菜单模式---\n");
    printf("---请输入(:1-7)选择功能记得数字前加冒号哦!---\n");
    //fflush(stdout);//上面那句如果没有\n 需要清缓存,才能把上面那句打印出来
    
    strcpy(msg.msg_text,"fun_menu");        
    if(msgsnd(qid_msg, (void *)&msg, 1024, 0) == -1)
    {
        perror("msgsnd");
    }
    
    
    #if 0
********************************************************************    
    setbuf(stdin,NULL);
    while(menuflag)
    {   
        
        printf("Notes:进入功能菜单模式\n");

        strcpy(msg.msg_text,"menu");
        
        if(msgsnd(qid_msg, (void *)&msg, 1024, 0) == -1)
        {
            perror("msgsnd");
        }

        printf(":");
    
        option=getchar();
        setbuf(stdin,NULL);
        
        switch (option)
        {
        case '1'://查询在线好友
            {
                c=getchar();//清理输入缓存中的数据
                sendto_server(":1",2);
                break;
            }
        case '2'://查询所有好友
            {
                c=getchar();//清理输入缓存中的数据
                sendto_server(":2",2);
                break;
            }
        case '3'://修改密码
            {
                c=getchar();//清理输入缓存中的数据
                sendto_server(":3",2);
                //change_passwd();
                break;
            }
        case '4'://查看当前聊天记录
            {
                c=getchar();//清理输入缓存中的数据
                view_chat();
                break;
            }
        case '5'://群组聊天
            {
                c=getchar();//清理输入缓存中的数据
                //group();
                menuflag=0;//退出菜单
                printf("---进入群组聊天模式\n");
                continue;
            }
        case '6'://退出菜单
            {
                menuflag=0;//退出菜单进入聊天模式
                printf("---退出菜单进入聊天模式---\n");
                continue;
            }
        case '7'://注销用户
            {
                menuflag=0;
                chating=0;//退出聊天模式
                sendto_server(":q",2);//通知服务器本注销用户
                printf("---注销用户---\n");
                continue;
            }
        default:
            {
                printf("输入选项有误,请重新输入\n");
                break;
            }
        }
    }   
*************************************************************************   
    #endif
}
//================================================================






//===发送信息给服务器=========================OK
int sendto_server(char *buf,int length)
{
    int sendbytes;
    sendbytes = send(client_sockfd,buf,length,0);
    if(sendbytes == -1)
    {
        perror("send");
        exit(1);
    }
    //printf("\n");
    //printf("---发送一条[%d]字节的信息到服务器---\n",sendbytes);
    //printf("信息内容:%s\n",buf);
    //printf("\n");
    return sendbytes;
}
//=========================================






//===从服务器接收信息=======================OK
int recvfrom_server(char *buf,int length)
{
    int recvbytes;
    recvbytes = recv(client_sockfd,buf,length,0);
    if(recvbytes == -1)
    {
        perror("recv");
        exit(1);
    }
    //printf("\n");
    //printf("---收到一条[%d]字节的信息自服务器---\n",recvbytes);
    //printf("信息内容:%s\n",buf);
    //printf("\n");
    return recvbytes;
}
//=========================================

display.c

//===包含的头文件=========
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <sys/types.h>
#include <string.h>
#include <pthread.h>
//=======================



//===定义的宏============
//#define MSG_MAXSIZE 1024//消息最大字节数
//=======================


//===定义的全局变量=========
struct message
{
    long msg_type;
    char msg_text[1024];
};
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//mutex互斥锁
//==================================



//===函数声明================
void tip_menu();
void fun_menu(void);
void group_menu(void);
void pthread_display_menu(void);
//===============================




//===主函数===========================
int main(int argc,char *argv[])
{
    //参数合法性判断
    if(argc<2)
    {
        printf("---请按以下示例输入---\n");
        printf("./display 11\n");
        exit(1);
    }
    
    //signal(SIGQUIT,tip_menu);//测试不OK改成子线程处理
    
    
    //---创建显示端-显示菜单-子线程---------------------
    int ret;
    pthread_t pid_display_menu;//显示端-显示菜单-子线程ID号
    ret = pthread_create(&pid_display_menu,NULL,(void *)pthread_display_menu,NULL);
    if(ret != 0)
    {
        perror("pthread_creat");
        exit(1);
    }
    printf("\n");
    printf("---显示端-显示菜单-子线程创建成功!---\n");
    printf("\n");
    //------------------------------------------------------
    
    
    //---创建消息队列--------------------------------------
    struct message msg;
    key_t key;
    int qid_msg;
    
    key = (key_t)atol(argv[1]);
    qid_msg = msgget(key,IPC_CREAT|0666);
    if(qid_msg == -1)
    {
        perror("msgget");
        exit(1);
    }
    printf("---创建消息队列成功qid_msg=%d---\n",qid_msg);
    //----------------------------------------------------------

    
    while(1)
    {
        //循环读取消息
        
        //printf("---准备从客户端接收消息---\n");
        
        memset(msg.msg_text,0,1024);//清空消息正文缓存
        
        //---读取消息队列中的第一个消息(阻塞)-------------
        if(msgrcv(qid_msg, (void *)&msg, 1024, 0, 0) == -1)  
        {
            printf("---客户端已退出---\n");
            exit(1);
        }
        //-------------------------------------------------
        
        if(!strncmp(msg.msg_text, "fun_menu",4))
        {
            fun_menu();
            continue;
        }
        
        if(!strncmp(msg.msg_text, "group",5))
        {
            group_menu();
            continue;
        }
        
        printf("%s\n", msg.msg_text);//将读取到的消息显示在显示窗口
    }
    
}
//====================================


//===显示端-显示菜单-子线程处理函数======================
void pthread_display_menu(void)
{
    char buf_option[10];
    while(1)
    {
        printf("\n\n");
        tip_menu();//显示提示菜单

        memset(buf_option,0,10);
        fflush(stdin);//清缓存
        scanf("%s",buf_option);
        printf("\n\n");
        
    }

}
//=============================================





//===提示菜单================================
void tip_menu()
{
    system("clear");//清屏
    printf("======操作提示======\n");
    printf("#msg(公聊)\n");
    printf("@ID:msg(私聊)\n");
    printf("$ID:file(传文件)\n");
    printf("ctrl+\\(唤出功能菜单)\n");
    printf("====================\n");
}
//========================================



//===功能菜单==================================
void fun_menu(void)
{
    system("clear");//清屏
    printf("======功能菜单======\n");
    printf("[1]查询在线好友\n");
    printf("[2]查询所有好友\n");
    printf("[3]修改密码\n");
    printf("[4]查看当前聊天记录\n");
    printf("[5]群组聊天\n");
    printf("[6]注销\n");
    printf("===================\n");
}
//========================================



//===群组菜单=============================
void group_menu(void)
{
    system("clear");//清屏
    printf("======群组======\n");
    printf("[1]ETC\n");
    printf("[2]嵌入式\n");
    printf("[3]返回\n");;
    printf("================\n");
}
//========================================


linklist.c

//===包含的头文件==========
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//=========================



//===定义的全局变量========
typedef int ElemType,Status;

struct user_msg
{
    char name[10];
    char passwd[10];
    int state;
    int channel;
    unsigned short mysockfd;
};

typedef struct LNode{
    struct user_msg user;
    struct LNode *next;
}LNode,*LinkList;
//=========================================




//===创建一个带头结点的链表=========================测试OK
void CreateList_L(LinkList *L)//头指针的地址传进来
{
    *L=(LinkList)malloc(sizeof(LNode));
    (*L)->next=NULL;
}
//=========================================



//===将用户信息插入到链表的结尾===========================测试OK
Status ListInsert_L (LinkList *L,struct user_msg user)//头指针的地址传进来
{
    LinkList p = *L,q;
    
    if(!p)  return -1;
    
    while(p!=NULL)
    {
        q=p;
        p=p->next;
    }
    p=(LinkList)malloc(sizeof(LNode));
    p->user=user;
    p->next=q->next;
    q->next=p;
    return 0;
}
//====================================================



//===查询是否存在该用户名==================================测试OK
int LocateName_L(LinkList L, char name[])
{
    LinkList p;
    p=L->next;//指向第一个节点
    while (p!=NULL)
    {
        if(!(strcmp(p->user.name,name)))
        {
            return 0;//存在该用户名
        }
        p=p->next;
    }
    return -1;//不存在该用户名
}
//==============================================================



//===将新用户信息添加到文件末尾========================测试OK
void uer2file(struct user_msg user)
{
    //===写入user_data二进制文件中===
    FILE *fp1;  
    //追加打开一个二进制文件,并在文件末尾写数据,若不存在则创建 
    if((fp1=fopen("user_data","ab+"))==NULL)
    {
        printf("cannot open file\n");
        exit(1);
    }
    
    if(fwrite(&user,sizeof(struct user_msg),1,fp1)!=1)
    {
        printf("file write error\n");
    }
    fclose(fp1);
    //===========================================
    
    
    
    //===写到user_view.txt文本中=================
    FILE *fp2;
    //追加打开一个文本文件,并在文件末尾写数据,若不存在则创建
    if((fp2=fopen("user_view.txt","at+")) == NULL)
    {
        printf("cannot open file\n");
        exit(1);
    }
    //写入用户名
    if(fputs("用户名:",fp2) == EOF)
    {
        printf("file write error\n");
    }
    if(fputs(user.name,fp2) == EOF)
    {
        printf("file write error\n");
    }
    if(fputs("\t",fp2) == EOF)
    {
        printf("file write error\n");
    }
    
    //写入密码
    if(fputs("密码:",fp2) == EOF)
    {
        printf("file write error\n");
    }
    if(fputs(user.passwd,fp2) == EOF)
    {
        printf("file write error\n");
    }
    
    if(fputs("\n",fp2) == EOF)
    {
        printf("file write error\n");
    }
    fclose(fp2);
    //=============================
} 
//====================================================



//===读取文件的内容到链表===================================
void file2list(LinkList *L)//头指针的地址传进来
{
    int i;
    FILE *fp2;
    struct user_msg user;
    //读写打开一个二进制文件,允许读,或在文件末追加数据,若不存在则创建
    fp2=fopen("user_data","ab+");
    for(i=0;i<50;i++)
    {
        if(fread(&user,sizeof(struct user_msg),1,fp2)!=1)
            break;
        ListInsert_L(L,user);
        
    }
    fclose(fp2);
}
//==========================================================


//===根据用户名查链表返回user结构体==================================//
struct user_msg LocateUser_L(LinkList L, char name[])
{
    LinkList p;
    p=L->next;
    while (p!=NULL)
    {
        if(!(strcmp(p->user.name,name)))
            break;
        p=p->next;
    }
    if(p!=NULL)
    {
        return(p->user); 
    }
    exit(-1);
} 
//========================================================//





//===更新user_data==========================================
void update_file(LinkList L)
{
    FILE *fp1;
    
    LinkList p;
    p=L->next;
    
    if((fp1=fopen("user_data","wb"))==NULL)
    {
        printf("cannot open file\n");
        return;
    }
    
    while (p!=NULL)
    {
        if(p->user.state==1)
        {
            p->user.state=0;
            uer2file(p->user);
            p->user.state=1;
        }
        else
        {
            uer2file(p->user);
        }
        
        p=p->next;
    }
}
//==========================================================



//===更新用户到链表==========================================
void update_list(LinkList *L,struct user_msg user1)
{
    LinkList p;
    p=(*L)->next;
    while (p!=NULL)
    {
        if(!(strcmp(p->user.name,user1.name)))
            break;
        p=p->next;
    }
    if(p!=NULL)
    {
        p->user=user1; 
    }
}
//===========================================================



//===根据socket确定用户名====================================
struct user_msg Locatesocket_L(LinkList L, int socket)
{
    LinkList p;
    p=L->next;
    while (p!=NULL)
    {
        if(p->user.mysockfd==socket)
            break;
        p=p->next;
    }
    if(p!=NULL)
    {
        return(p->user); 
    }
    exit(-1);
} 
//==========================================


//===根据用户名返回socket==================================
int LocateUsersoket_L(LinkList L, char name[])
{
    LinkList p;
    p=L->next;
    while (p!=NULL)
    {
        if(!(strcmp(p->user.name,name)))
            break;
        p=p->next;
    }
    if(p!=NULL)
    {
        return(p->user.mysockfd); 
    }
    else
    {
        return -1;
    }
}
//==========================================================

//===求取用户个数===========================
int Length_L(LinkList L)
{   
    int i=0;
    LinkList p;
    if(!L)
        return -1;
    p=L->next;
    while (p!=NULL)
    {
        p=p->next;
        i++;
    }
    return i;
}
//=========================================================


//===从链表中删除指定用户==================================
Status ListDelete_L(LinkList *L,char name[])
{
    LinkList p,q;
    
    p=(*L)->next;
    q=*L;
    while(p!=NULL)
    {   
        
        if(!(strcmp(p->user.name,name)))
            break;
        q=p;
        p=p->next;
        
    }
    
    if(p==NULL)
    {   
        return -1;
    }
    else//用户名在链表中
    {
        q->next=p->next;
        free(p);
        return 0;
    }
    
}
//=========================================================


//===显示全部用户信息=================================
int Disp_L(LinkList L)
{
    LinkList p;
    if(!L)
        return -1;
    p=L->next;
    printf("===============全部用户信息================\n");
    
    printf("name\tpasswd\tstate\tsocket\tchannel\n");
    while(p!=NULL)
    {
        printf("%s\t",p->user.name);
        printf("%s\t",p->user.passwd);
        printf("%d\t",p->user.state);
        printf("%d\t",p->user.mysockfd);
        printf("%d\n",p->user.channel);
        p=p->next;
    }
    printf("===========================================\n");
    
    return 0;
}
//==========================================================




//===获取当前在线用户并打印=================================
void Getstate_L(LinkList L)
{
    LinkList p;
    p=L->next;
    printf("=============在线用户信息==================\n");
    
    printf("name\tpasswd\tstate\tsocket\tchannel\n");
    while(p!=NULL)
    {
        if(p->user.state==1)
        {
            printf("%s\t",p->user.name);
            printf("%s\t",p->user.passwd);
            printf("%d\t",p->user.state);
            printf("%d\t",p->user.mysockfd);
            printf("%d\n",p->user.channel);
        }
        p=p->next;
    }
    printf("===========================================\n");
    
}
//==========================================================

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354