聊天室项目

1、简述:
采用了cs结构
服务器端四步走:socket,bind,listen,accept
客户端两步走:socket,connect
简单模型:客户端a消息发送到服务器;服务器找到客户端b的socket,在把消息发送给客户端b;
设计数据库操作存储的一些操作比较简单这里不提
2、看代码:
(1)、服务器端代码

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sqlite3.h>


#define PORT  9999

struct User
{
    char name[20];   // 用户名
    int  socket;     // 和用户通信的socket
};



// 协议
struct Msg
{
    char information[1024]; //发送消息
    char msg[1024];  // (姓名)消息内容

    int id;
    char sex[20];
    int age;
    int password;

    char toname[20];
    char fromname[20];
    int  cmd;        // 消息类型

    char online_name[20]; //在线姓名;
};

struct User user[2000] = {0};


//打开数据库函数,客户端用户登录数据传递过来,服务器端打开数据库比较查询是否匹配此登录用户信息;
int Database_enter(int client_socket,struct Msg *msg)
{
    sqlite3 *database;
    int ret = sqlite3_open( "information.db", &database );
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_open");
        return -1;
    }
    printf("数据库打开成功\n");


    // 3、char ***resultp: char *resultp[] = {"id", "name", "sex", "age", "1", "zhang", "M", "12","2".....};
    // 4、nrow: 多少行数据
    // 5、ncolumn: 多少列数据
    char ** resultp;
    char *errmsg = NULL;
    int nrow;
    int ncolumn;
    //进行什么操作;
    char *sql = "select * from information";

    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg );

    if(ret != SQLITE_OK)
    {
        printf("数据库操作失败 %s\n",errmsg);
        return -1;
    }

    //查询之后,把客户端传递过来的数据和这张表里的数据想比较,先比较唯一的name;再比较password;
    int i;
    for(i = 0 ;i < (nrow+1)*ncolumn;i++)
    {
        if (strncmp (resultp[i],msg->msg,strlen(msg->msg)) == 0)
        //if (strncmp (resultp[i],msg->msg,strlen(msg->msg)-1) == 0)
        {
            if(atoi(resultp[i+1]) == msg->password) //强制转换;
            {
                printf("服务器端已经通过登录验证\n");
                msg->cmd = 1002;

                int k;
                for(k = 0 ;k < 2000; k++)
                {
                    if(user[k].socket == 0)
                    {
                        strcpy(user[k].name, msg->msg);
                        user[k].socket = client_socket;
                        printf("名字:%s\n",user[k].name);
                        break;
                    }
                }

                break;
            }
            else
            {
                printf("服务器端发现你的密码不正确哦!\n");
                msg->cmd = -1;
                break;
            }
        }
    }
    if(i == (nrow+1)*ncolumn)
    {
        msg->cmd = -2;
        printf("服务器端没找到该用户\n");
    }


    sqlite3_free_table(resultp);  //之前的char**resultp (数组)等于malloc了一个空间来储存数据库的数据;所以需要释放;
    sqlite3_close(database);
    printf("%d,%d\n",msg->cmd,sizeof(msg));
}

//私聊数据库函数
int Database_chat2(int client_socket,struct Msg *msg)
{
    sqlite3 *database;
    int ret = sqlite3_open( "information.db", &database );
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_open");
        return -1;
    }
    printf("数据库打开成功\n");


    // 3、char ***resultp: char *resultp[] = {"id", "name", "sex", "age", "1", "zhang", "M", "12","2".....};
    // 4、nrow: 多少行数据
    // 5、ncolumn: 多少列数据
    char ** resultp = NULL;
    char *errmsg = NULL;
    int nrow;
    int ncolumn;
    //进行什么操作;
    char *sql = "select * from information";

    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg );

    if(ret != SQLITE_OK)
    {
        printf("数据库操作失败 %s\n",errmsg);
        return -1;
    }

    //查询之后,把客户端传递过来的数据和这张表里的数据想比较,先比较唯一的name;再比较password;
    int i;
    for(i = 0 ;i < (nrow+1)*ncolumn;i++)
    {
        if (strncmp (resultp[i],msg->toname,strlen(msg->toname)) == 0)
        //  if (strncmp (resultp[i],msg->toname,strlen(msg->toname)-1) == 0)
        {
            printf("222222\n");
            msg->cmd = 11;
            return 11;
        }
    }

    if(i == (nrow+1)*ncolumn)
    {
        msg->cmd = -5;
        return -5;
    }

    sqlite3_free_table(resultp);  //之前的char**resultp (数组)等于malloc了一个空间来储存数据库的数据;所以需要释放;
    sqlite3_close(database);


}

//数据库写入注册数据函数
int Database_insert( struct Msg *msg)
{
    sqlite3 *database;

    //打开数据库,数据库指针指向你要打开的数据库,studtet.db是你创建的数据库文件

    int ret = sqlite3_open( "information.db", &database );
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_open");
        return -1;
    }
    printf("数据库打开成功\n");

    //下边一大段是创建一个表
    char *errmsg = NULL;

    //sql语句
    char *sql = "create table if not exists information (name TEXT, PASSWORD INTEGER, primary key(name))";    

    //执行sql语句的函数;
    ret = sqlite3_exec(database ,sql, NULL ,NULL ,&errmsg);
    if(ret != SQLITE_OK)
    {
        printf("数据库创建操作失败 %s\n",errmsg);
        return -1;
    }

    //往表里边插入数据;
    //insert into student values(1, 'Zhang', 'M', 18);

    char buf[100];
    sprintf(buf," insert into information values('%s',%d)",msg->msg ,msg->password );
    //设置主键之后,如果插入的id已经存在那么就会返回错误,一班来说返回的错误都是插入重复这里有点取巧;
    ret = sqlite3_exec(database ,buf, NULL ,NULL ,&errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库插入操作失败:%s\n", errmsg);
        return -2;
    }
    printf("数据库已经成功插入了你的注册信息\n");
    sqlite3_close(database);
    return 1;
}


// 初始化套接字,返回监听套接字
int init_socket()
{
        //1、创建socket
    int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_socket == -1)
    {
        perror ("socket");
        return -1;
    }

    // 2、命名套接字,绑定本地的ip地址和端口
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family  = AF_INET;     // 设置地址族
    addr.sin_port    = htons(PORT); // 设置本地端口
    addr.sin_addr.s_addr = htonl(INADDR_ANY);   // 使用本地的任意IP地址

    int  ret = bind(listen_socket,  (struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror ("bind");
        return -1;
    }

    // 3、监听本地套接字
    ret = listen(listen_socket, 5);
    if (ret == -1)
    {
        perror ("listen");
        return -1;
    }

    printf ("等待客户端连接.......\n");
    return listen_socket;
}

// 处理客户端连接,返回与连接上的客户端通信的套接字
int  MyAccept(int listen_socket)
{
    // 4、接收连接
    // 监听套接字不能用来与客户端进行通信,它的职责是监听客户端的连接
    // accpet 处理客户端的连接,如果成功接收,会返回一个新的套接字,用来与客户端进行通信
    // accept的第三个参数 是一个传入传出参数
    struct sockaddr_in client_addr; // 用来保存客户端的ip和端口信息
    int len = sizeof(client_addr);
    int client_socket = accept(listen_socket,   (struct sockaddr *)&client_addr,  &len);
    if (client_socket == -1)
    {
        perror ("accept");
    }

    printf ("成功接收一个客户端: %s\n", inet_ntoa(client_addr.sin_addr));

    return client_socket;
}


//注册函数
void reg(int client_socket, struct Msg *msg)
{
    printf ("%s 正在进行注册\n", msg->msg);

    //下边的代码就是应该把注册的消息写进数据库;
    int ret = Database_insert( msg );
    if(ret == -2)
    {
        printf("该用户id已经被注册 \n");
        msg->cmd = -2;
    }
    else
    {
        msg->cmd = 1001;
    }
    //msg->cmd = 1001;
    /* {
        // 将用户套接字进行保存,保存在数组里
        int i = 0;
        for (i = 0; i < 20; i++)
        {
            if (user[i].socket == 0)
            {
                strcpy (user[i].name , msg->msg);
                user[i].socket = client_socket;
                break;
            }
        }

        if (i == 2000)
        {
            msg->cmd = -1;
        }
        else
        {
            msg->cmd = 1001;
        }
    }
     */

    write (client_socket, msg, sizeof(struct Msg));
}



//登录验证函数;
 void enter(int client_socket, struct Msg *msg)
 {
    Database_enter(client_socket,msg );
    write(client_socket, msg ,sizeof(struct Msg));
    printf("11111111\n");
 }



// 群发
void chat(int client_socket, struct Msg *msg)
{
    printf ("%s 发一次群消息\n", msg->msg);

    // 将用户进行保存
    int i = 0;
    for (i = 0; i < 2000; i++)
    {
        if (user[i].socket != 0)
        {
            write (user[i].socket, msg, sizeof(struct Msg));    
        }
    }
}

// 私聊
void chat2(int client_socket, struct Msg *msg)
{
    printf ("%s 要 给 %s 发一条消息\n", msg->fromname, msg->toname);

    //判断数据库是够有此用户
    int ret  = Database_chat2(client_socket,msg);
    if(ret == -5)
    {
        printf("此用户不存在\n");
        write(client_socket,msg,sizeof(struct Msg));
    }
    //用户才在于数据库,查找是否在线?
    else if(ret == 11)
    {
        printf("数据库存在此用户,正在判断该用户是否在线\n");

        //判断用户是否在线;
        int i = 0;
        for (i = 0; i < 2000; i++)
        {
            if (user[i].socket != 0 && strncmp(user[i].name, msg->toname,strlen(msg->toname))==0)
            //  if (user[i].socket != 0 && strncmp(user[i].name, msg->toname,strlen(msg->toname)-1)==0)
            {
                printf("11111111\n");
                msg->cmd = 10;
                write(client_socket,msg,sizeof(struct Msg));
                printf("服务器判断出该用户在线,正在给该用户发送消息....\n");
                msg->cmd = 3;
                write (user[i].socket, msg, sizeof(struct Msg));
                printf("1111111111\n");
                break;
            }
        }

        if(i == 2000)
        {
            printf("服务器显示该用户不在线\n");
            msg->cmd = -6;
            write(client_socket,msg,sizeof(struct Msg));

        }
    }


}


//显示在线好友列表;
void display(int client_socket,struct Msg *msg)
{
    //服务器端把user[]里边的在线遍历出来发送给客户端;
    int i;
    msg->cmd = 4;
    for(i = 0;i < 2000;i++)
    {
        if(user[i].socket != 0)
        {
            //遍历一次写给客户端一次;
            strncpy(msg->online_name ,user[i].name,strlen(user[i].name));
            write(client_socket, msg ,sizeof(struct Msg));
            printf("44444444\n");
        }
    }

}

//注销用户信息
int logout(int client_socket, struct Msg *msg)
{


    sqlite3 * database;

    // 打开数据库
    int ret = sqlite3_open("information.db", &database);
    if (ret != SQLITE_OK)
    {
        printf ("打开数据库失败\n");
        return -1;
    }
    printf ("打开数据库成功\n");

    char *errmsg = NULL;
    char buf[100];
    sprintf (buf, "delete from information where name = '%s'", msg->msg);
    ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库操作失败:%s\n", errmsg);
        return -1;
    }

    printf("注销成功\n");

    write(client_socket,msg, sizeof(struct Msg));
    printf("msg.cmd %d\n",msg->cmd);
    sqlite3_close(database);
}


// 把 负责处理客户端通信的函数改成线程的工作函数
void* hanld_client(void* v)
{
    int client_socket = (int)v;
    struct Msg msg;
    while(1)
    {
        // 从客户端读一个结构体数据
        int ret = read(client_socket, &msg, sizeof(msg));
        //printf("2:%d,%d\n",msg.cmd,sizeof(msg));
        if (ret == -1)
        {
            perror ("read");
            break;
        }

        // 代表客户端退出
        if (ret == 0)
        {
            printf ("客户端退出\n");
            break;
        }


        switch (msg.cmd)
        {
            case 1 :    // 客户但进行注册
                reg(client_socket, &msg);
                break;
            case 2 :    // 客户端进行群聊天
                chat(client_socket, &msg);
                break;
            case 3 :    // 客户端进行私聊
                chat2(client_socket, &msg);
                break;

            case 4:   //客户进行登录;
                enter(client_socket,&msg);
                break;

            case 5: //显示在线好友列表;
                display(client_socket,&msg);
                break;

            case 6: //注销用户;
                logout(client_socket,&msg);
                break;
        }

    }


    close (client_socket);
}

int main() 
{
    // 初始化套接字
    int listen_socket = init_socket();

    while (1)
    {
            // 获取与客户端连接的套接字
        int client_socket = MyAccept(listen_socket);

        // 创建一个线程去处理客户端的请求,主线程依然负责监听
        pthread_t id;
        pthread_create(&id, NULL, hanld_client,  (void *)client_socket);

        pthread_detach(id); // 线程分离
    }
    close (listen_socket);

    return 0;
}

(2)、客户端代码

这里写代码片#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sqlite3.h> 

#define PORT  9999
void enter( int );

char myName[20];
char Sex[20];

struct Msg
{
    char information[1024]; //发送消息;
    char msg[1024];  // (姓名)消息内容
    int id;
    char sex[20];
    int age;
    int password;

    char toname[20];  //消息发给谁
    char fromname[20]; //消息从谁的那边发出来
    int  cmd;        // 消息类型

    char online_name[20]; //在线姓名;
};


// 主界面;
void interface()
{
    printf ("\t\t 1、注册\n");
    printf ("\t\t 2、登录\n");

}

void interface3()
{
    //printf ("\t\t 1、注册\n");
    printf ("\t\t 2、登录\n");

}



 //登录后的界面;
void interface2()
{
    printf ("\t\t 1、发送文件\n");
    printf ("\t\t 2、群聊\n");
    printf ("\t\t 3、私聊\n");
    printf ("\t\t 4、注销当前用户\n");
    printf ("\t\t 5、显示好友列表\n");
    printf ("\t\t 6、查看本地聊天记录\n");
}

//读写分离的读线程的回调函数;
 void * readMsg(void *v)
 {
     int socketfd = (int)v;
     struct Msg msg;
     while (1)
     {
         read(socketfd, &msg, sizeof(msg));
         //printf("test msg.cmd %d,%d\n",msg.cmd,sizeof(msg));
         switch (msg.cmd)
         {
             case -5:  //私聊用户不存在;
                printf("你想私聊的用户不存在\n");
                break;

             case 10:   //用户存在并且在线;
                printf("用户存在并且在线\n");
                break;

            case -6: //用户不在线;
                printf("用户存在但是不在线\n");
                break;

             case 2:   // 群聊
                printf ("收到一条消息: %s\n", msg.information);
                break;

             case 3:   // 私聊
                printf ("%s 给你发一条消息:%s\n", msg.fromname, msg.information);
                break;

             case 4:   // 显示在线好友列表;
                printf("在线的好友有:%s\n",msg.online_name);
                printf("3333333\n");
                break;

            case 6: //用户注销;
                printf("注销成功!\n");
                return;
                break;
         }

     }
 }


 //注册函数
int reg(int socketfd)
{
    struct Msg msg;
    //msg.cmd = 1;

    while(1)
    {
        msg.cmd = 1;

        //memset(&msg,0,sizeof(msg));//清零
        printf ("请输入用户名:\n");
        scanf("%s",&(msg.msg));

        printf ("请输入用户的密码 :\n");
        scanf("%d",&(msg.password));


        write (socketfd, &msg, sizeof(msg));

        //来自服务器端的回复:到底有没有注册成功,成功了返回一个标号,要是失败了,那么为什么失败,返回另外一个标号,最后失败的需要全部重新注册

        read (socketfd, &msg, sizeof(msg));

        if (msg.cmd == 1001)
        {
            printf ("你已注册成功,欢迎加入聊天室\n");

            /* int a;
            while (1)
            {
                printf("\n");
                interface3();
                printf("\n");
                printf("\n");
                sleep(1);
                printf("\t\t*_*输入功能:\n");
                scanf("%d",&a);
                printf("\n");
                while(getchar()!= '\n');
                switch(a)
                {
                    case 2:     //登录;
                        enter(socketfd);
                        break;

                }

                system("clear");
            }
            */





            break;
        }
        //用户已经被注册过了,重新注册

        if (msg.cmd == -2)
        {
            printf ("该用户名已经被注册,请重新进行注册\n");
            memset(&msg,0,sizeof(msg));//清零
            continue;
        }
        //聊天室的登录人数上限已经到达注册失败;
        else if(msg.cmd == -1)
        {
            printf ("聊天室的上限已经到达,请重新进行注册\n");
            continue;
        }

    }

    sleep(1);




}


 //群聊函数,
void chat(int socketfd)
{
    struct Msg msg;
    msg.cmd = 2;
    printf ("请输入要发送的内容: \n");
    //fgets(msg.information, 1024, stdin);
    scanf("%s",msg.information);
    write (socketfd, &msg, sizeof(msg));
}


 //私聊;
void chat2(int socketfd)
{
    struct Msg msg;
    msg.cmd = 3;
    printf ("请输入要发送的对象名称: \n");
    //fgets(msg.toname, 20, stdin);
    scanf("%s",msg.toname);

    printf ("请输入要发送的内容: \n");
    //fgets(msg.information, 1024, stdin);
    scanf("%s",msg.information);

    printf("输入自己的名字 : \n");
    //fgets(msg.fromname, 20, stdin);
    scanf("%s",msg.fromname);

    write (socketfd, &msg, sizeof(msg));

     //调用存储聊天记录的函数;
    int ret = insert_msg( &msg );
    if(ret == 1)
    {
        printf("成功保存本地记录\n");
    } 
}


 //注销用户;
void logout(int socketfd)
{
    char ch[2];
    printf("是否注销此用户: y/n \n");
    scanf("%c",ch);
    if(ch[0] == 'y')
    {
        struct Msg msg;
        msg.cmd = 6;
        printf("输入您的用户名:\n");
        scanf("%s",msg.msg);

        write(socketfd, &msg,sizeof(struct Msg)); 
    }
    else
    {
        return ;
    }


}


//显示在线好友列表;
void display(int socketfd)
{
    struct Msg msg;
    printf("我想知道在线的好友列表\n");
    msg.cmd = 5;
    write(socketfd, &msg, sizeof(struct Msg)); 
}  


//登录函数;
void enter(int socketfd)
{
    struct Msg msg;
    msg.cmd = 4;
    printf("请输入用户名 :\n");
    scanf("%s",&(msg.msg));

    printf("请输入用户密码:\n");
    scanf("%d",&(msg.password));

    write (socketfd, &msg, sizeof(msg)); //消息写过去,等待回复,例如什么用户不存在,密码错误;
    if(msg.cmd == 5)
    {

    }
    read(socketfd ,&msg ,sizeof(msg)); //读取来自服务器的返回的消息,进行客户端屏幕打印;
    //printf("%d,%d\n",msg.cmd,sizeof(msg));
    if(msg.cmd == 1002)
    {
        printf("恭喜你登录成功\n");


        //登录完成之后开始读写分离;
        sleep(1);

        pthread_t id;
        pthread_create(&id, NULL, readMsg,  (void *)socketfd);

        pthread_detach(id); // 线程分离 


        int a;
        while (1)
        {
            interface2();
            printf("\n");
            printf("\n");
            sleep(1);
            printf("\t\t*_*输入功能:\n");
            scanf("%d",&a);
            printf("\n");
            while(getchar()!= '\n');
            switch(a)
            {

                case 1:   //发送文件
                    send_file(socketfd);
                    break;
                case 2:     // 群聊天
                    chat(socketfd);
                    break;
                case 3:     // 私聊
                    chat2(socketfd);
                    break;
                case 4 :   //注销用户;
                    logout(socketfd);
                    return;
                case 5:  //显示在线好友列表;
                    display(socketfd);
                    sleep(1);
                    break;

                case 6: //查看本地聊天记录;
                    look_msg(socketfd);
                    sleep(1);
                    break;

            }

            system("clear");
        }


    }
    else if(msg.cmd == -1)
    {
        printf("密码不正确\n");
        usleep(5*100000);
    }
    else if(msg.cmd == -2)
    {
        printf("该用户不存在\n");
        sleep(0.5);
    }



 }


//聊天记录存到本地;
int insert_msg(struct Msg* msg)
{    
    sqlite3 *database;
    int ret = sqlite3_open( "msg.db", &database );
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_open");
        return -1;
    }
    printf("数据库打开成功\n"); 

    //下边一大段是创建一个表
    char *errmsg = NULL;
    char *sql = "create table if not exists msg (fromname TEXT, information TEXT,toname TEXT)";     //sql语句   
    ret = sqlite3_exec(database ,sql, NULL ,NULL ,&errmsg);//执行sql语句的函数;
    if(ret != SQLITE_OK)
    {
        printf("数据库创建操作失败 %s\n",errmsg);
        return -1;
    }

    //往表插入数据;
    //insert into student values(1, 'Zhang', 'M', 18);
    char buf[100];
    printf("%s\n",msg->fromname);
    sprintf(buf," insert into msg values('%s','%s' ,'%s')",msg->fromname ,msg->information ,msg->toname );
    //设置主键之后,如果插入的id已经存在那么就会返回错误,一班来说返回的错误都是插入重复这里有点取巧;
    ret = sqlite3_exec(database ,buf, NULL ,NULL ,&errmsg);
    if (ret != SQLITE_OK)
    {
        printf ("数据库插入操作失败:%s\n", errmsg);
        return -1;
    }
    printf("聊天记录成功保存到本地数据库*_*\n");
    sqlite3_close(database);
    return 1;
}

//查看本地聊天记录;
int look_msg(int socketfd)
{
    //本地创建一张数据库表,存放本地的消息;
    //查看不就简单了,直接打开本地的数据库就好了;
    sqlite3 *database;
    int ret = sqlite3_open( "msg.db", &database );
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_open");
        return -1;
    }
    printf("数据库打开成功\n"); 

    char ** resultp;
    char *errmsg = NULL;
    int nrow;
    int ncolumn;
    //进行什么操作;
    char *sql = "select * from msg";

    ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg );

    if(ret != SQLITE_OK)
    {
        printf("数据库操作失败 %s\n",errmsg);
        return -1;
    }

    int i;
    printf("nrow = %d,ncolumn = %d",nrow, ncolumn);
    for(i = 0; i < (nrow +1)* ncolumn;i++)
    {
        if(i % ncolumn == 0)
        {
            printf("\n");
        }
        printf("%-18s" ,resultp[i]);
    }
    printf("\n");

    sqlite3_free_table(resultp);  //之前的char**resultp (数组)等于malloc了一个空间来储存数据库的数据;所以需要释放;
    sqlite3_close(database);
}


//传送文件;
send_file(int socketfd)
{

 }


// 客户端向服务器发送数据处理函数;
void ask_server(int socketfd)
{
    int a ;
    while (1)
    {

        //char ch[2];
        int a;
        while (1)
        {
            printf("\n");
            interface();
            printf("\n");
            printf("\n");
            sleep(1);
            printf("\t\t*_*输入功能:\n");
            scanf("%d",&a);
            printf("\n");
            while(getchar()!= '\n');
            switch(a)
            {
                case 1:     //注册;
                    reg(socketfd);
                    printf("注册注册注册\n");
                    break;
                case 2:     //登录;
                    enter(socketfd);
                    break;

            }

            system("clear");
        }
    }
}


int main()
{   
    // 创建与服务器通信的套接字
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd == -1)
    {
        perror ("socket");
        return -1;
    }

    // 连接服务器
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family  = AF_INET;     // 设置地址族
    addr.sin_port    = htons(PORT); // 设置本地端口
    inet_aton("127.0.0.1",&(addr.sin_addr));


    // 连接服务器,如果成功,返回0,如果失败,返回-1
    // 成功的情况下,可以通过socketfd与服务器进行通信
    int ret = connect(socketfd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror ("connect");
        return -1;
    }

    printf ("成功连上服务器\n");

    //开始进行操作;


    ask_server(socketfd);

    // 关闭套接字
    close(socketfd);

    return 0;
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
package windows; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Vector; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.params.ClientPNames; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; /** * 核心功能封装类 * @author 冯晋强 * */ public class TmoocOperate { static String sessionid; static CloseableHttpResponse response = null; static CloseableHttpClient httpclient = null; //静态块 static{ // 创建提交数据对象 httpclient = HttpClients.createDefault(); } /** * 处理在线疑答贴子列表源码数据 * @author 刑保政 */ public static String[][] splist(String Str) { Str = Str.substring(Str.lastIndexOf("<ul>") + 4, Str.lastIndexOf("</ul>")).replaceAll("\\s", ""); String[] lis = Str.split("</li>");// 所有记录的数组 String[] jilu = null;// 单条记录的数组 String[][] allMsg = new String[lis.length - 1][4]; // 创建一个二维数组保存处理后的数据,其中每个一维数组中包含一个记录,每个二维数组中包含每条数据的信息 // allMsg[i][0]:标题; allMsg[i][1]:时间 allMsg[2]:处理状态 for (int i = 0; i < lis.length - 1; i++) {// 遍历所有记录,取出每一条记录 String ss = lis[i];// 取出每一条记录 jilu = ss.split("(</a></span>|</span>)"); // 每条记录分割成3部分 jilu[0]:标题 jilu[2]:时间 jilu[3]:处理状态 for (int j = 0; j < jilu.length; j++) {// 由于数据中还含有部分额外代码,遍历所有记录筛选数据 String msg = jilu[j];// 单条记录中的每一个数据 System.out.println(msg); msg = msg.substring(msg.lastIndexOf(">") + 1, msg.length()); String uid = jilu[0].substring(jilu[0].lastIndexOf("(") + 1, jilu[0].lastIndexOf(")")); allMsg[i][j] = msg; allMsg[i][3] = uid; } } return allMsg; } /** * 处理在线疑答帖子内容源码数据 * @author 刑保政 */ public static String[] splist1(String all) { String queStr = all.substring(all.indexOf("<div class=\"quesdetail\""),all.indexOf("<div id=\"answers\"")); // .replaceAll("\\s","");//处理源码筛选字符串,只获取含有问题标题的内容 // System.out.println(queStr);//测试标题部分字符串 String[] queArr = queStr.split("(</span|</pre|</div>)"); for(int i=0;i<queArr.length;i++){ String msg = queArr[i]; msg = msg.substring(msg.lastIndexOf(">")+1, msg.length()); if(i == 5){ msg = msg.substring(msg.indexOf(":")+1).trim(); } queArr[i] = msg; } String[] que = new String[4];//保存最终标题内容的数组 que[0] = queArr[1]; que[1] = queArr[3]; que[2] = queArr[4]; que[3] = queArr[5]; for(int i=0;i<2;i++){ que[i] = que[i].replaceAll("<","<"); que[i] = que[i].replaceAll(">",">"); que[i] = que[i].replaceAll(" "," "); que[i] = que[i].replaceAll("&","&"); que[i] = que[i].replaceAll(""","\""); que[i] = que[i].replaceAll("©","@"); que[i] = que[i].replaceAll("®","商标"); } return que; } // 回复帖子方法包 public static boolean SetTitle1(String message,String uid) { try { // url提交地址 HttpPost httpPost = new HttpPost("http://tts8.tmooc.cn/onlinefaq/anwser"); // 组合数据包 List<NameValuePair> nvps = new ArrayList<NameValuePair>(); nvps.add(new BasicNameValuePair("questionId", uid)); nvps.add(new BasicNameValuePair("context", message)); // 设置数据包 httpPost.setEntity(new UrlEncodedFormEntity(nvps,HTTP.UTF_8)); // 提交数据包 response = httpclient.execute(httpPost); // 读取返回数据信息 String str = EntityUtils.toString(response.getEntity()); System.out.println(str); if(str.indexOf("true")!=-1){ return true; }else{ return false; } } catch (ClientProtocolException e) { System.out.println("数据包提交失败"); e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return false; } // 发送帖子方法包 public static boolean SetTitle(String tilte, String message) { try { // url提交地址 HttpPost httpPost = new HttpPost("http://tts8.tmooc.cn/onlinefaq/add"); // 组合数据包 List<NameValuePair> nvps = new ArrayList<NameValuePair>(); nvps.add(new BasicNameValuePair("title", tilte)); nvps.add(new BasicNameValuePair("context", message)); // 设置数据包 httpPost.setEntity(new UrlEncodedFormEntity(nvps,HTTP.UTF_8)); // 提交数据包 response = httpclient.execute(httpPost); // 读取返回数据信息 String str = EntityUtils.toString(response.getEntity()); System.out.println(str); if(str.indexOf("true")!=-1){ return true; }else{ return false; } } catch (ClientProtocolException e) { System.out.println("数据包提交失败"); e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return false; } /** * * @author 刑保政 */ public static Vector<Vector<String>> getAite(Vector<Vector<String>> vvs){ /** 保存所有姓名的集合,因为回帖人中有重复数据,所以这里用Set去除重复 */ Set<String> usersSet = new HashSet<String>(); /** 保存所有回帖内容的集合 */ List<String> texts = new ArrayList<String>(); /* 遍历回帖集合,分别取出每一条记录中的回帖人和回帖内容,并添加到 * 对应的集合中,方便下一步遍历筛选 */ for(int i=0;i<vvs.size();i++){ Vector<String> v = vvs.get(i); String user = v.get(1); String text = v.get(3); usersSet.add(user); texts.add(text); } // System.out.println(users);//测试回帖人集合 // System.out.println(texts);//测试回帖内容集合 String[] users = new String[usersSet.size()]; usersSet.toArray(users);//将usersSet回帖人集合转为数组方便遍历 // System.out.println(Arrays.toString(users));//测试回帖人数组 /* 遍历回帖人数组,取出每一个回帖人, * 遍历每一条回帖内容,判断内容是否以@+回帖人开头,(当前设置@只能在开头有用,中间的不予考虑) * 若是则提取该回帖人并添加到当前楼层集合中 */ for(int i=0;i<users.length;i++){ String user = users[i]; for(int j=0;j<texts.size();j++){ String text = texts.get(j); if(text.startsWith("@" + user)){ // System.out.println(text); vvs.get(j).add("@" + user); } } } return vvs;//将处理后的集合返回 } /** * * @author 刑保政 */ public static Vector<Vector<String>> getAnswer(String all){ all = all.substring(all.indexOf("<div id=\"answers\""), all.indexOf("<div id=\"appendQues\"")); //抽取数据片段(答案部分) // System.out.println(all); String[] ansArr = all.split("<div class=\"answer");//分割成存储单条回帖的数组 String[] answer = null;//每一条回帖数组 Vector<Vector<String>> vvs = new Vector<Vector<String>>();//创建二维集合保存信息 /* * 遍历回帖集合,取出每一条回帖记录,分割成一组回帖信息元素, * 进行处理后添加进二维集合 */ for(int i = 1;i<ansArr.length;i++){ String ansStr = ansArr[i]; answer = ansStr.split("(</span>|</pre>)"); vvs.add(new Vector<String>()); vvs.get(i-1).add(i + "");//楼层数 for(int j=0;j<answer.length-1;j++){//处理每条回帖的信息 String msg = answer[j].trim(); msg = msg.substring(msg.lastIndexOf(">")+1,msg.length()).trim(); if(j == 1){//对时间单独进行处理一下 msg = msg.substring(msg.indexOf("(")+1,msg.length()-1).trim(); } vvs.get(i-1).add(msg);//添加进二维集合 } } vvs = getAite(vvs); return vvs; } // 读取在线疑答帖子内容源码数据 public static String Get_title(String uid) { try { // url提交地址 HttpGet httpGet = new HttpGet("http://tts8.tmooc.cn/onlinefaq/detail/"+ uid); // 提交数据包 response = httpclient.execute(httpGet); // 取出cookie System.out.println(response.getFirstHeader("Cookie")); // 读取返回数据信息 String str = EntityUtils.toString(response.getEntity()); return str; } catch (ClientProtocolException e) { System.out.println("数据包提交失败"); e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } // 读取在线疑答帖子列表源码数据 public static String Get_tilte() { try { // url提交地址 HttpGet httpGet = new HttpGet("http://tts8.tmooc.cn/onlinefaq/questionList"); // 提交数据包 response = httpclient.execute(httpGet); System.out.println("第七次请求成功"); // 读取返回数据信息 String str = EntityUtils.toString(response.getEntity()); return str; } catch (ClientProtocolException e) { System.out.println("数据包提交失败"); e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } // 初始化登陆,判断是否需要登陆验证码 暂时未完善 public static void GetCode(String user) { CloseableHttpClient httpclient = null; CloseableHttpResponse response2 = null; try { // url提交地址 HttpPost httpPost = new HttpPost("http://tmooc.cn/login/loginTimes"); // 组合数据包 List<NameValuePair> nvps = new ArrayList<NameValuePair>(); nvps.add(new BasicNameValuePair("login_name", user)); // 设置数据包 httpPost.setEntity(new UrlEncodedFormEntity(nvps)); // 创建提交数据对象 httpclient = HttpClients.createDefault(); // 提交数据包 response2 = httpclient.execute(httpPost); // 读取返回数据信息 String str = EntityUtils.toString(response2.getEntity()); System.out.println(str); TmoocOperate.SetTitle("今天的表示没怎么听懂", "你们呢。。。"); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { response2.close(); httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } } // 登陆Tmooc方法 public static boolean Tmooc_Login(String user, String pass) { try { // url提交地址 HttpPost httpPost = new HttpPost("http://tmooc.cn/login"); // 组合数据包 List<NameValuePair> nvps = new ArrayList<NameValuePair>(); nvps.add(new BasicNameValuePair("login_name", user)); nvps.add(new BasicNameValuePair("password", Login_MD5(pass).toLowerCase())); nvps.add(new BasicNameValuePair("type", "P")); nvps.add(new BasicNameValuePair("uuid","E1CC4286A419C899CCBF6A04E5A1CF02")); // 设置数据包 httpPost.setEntity(new UrlEncodedFormEntity(nvps)); // 提交数据包 response = httpclient.execute(httpPost); // 读取返回数据信息 String str = EntityUtils.toString(response.getEntity()); //判断是否登陆成功 if (str.indexOf(user) != -1) { sessionid = str.substring(14, str.indexOf("|P#")); // 取出cookie方法 这里用不到 //Cookie = response2.getFirstHeader("Set-Cookie").toString(); // url提交地址 HttpGet httpGet = new HttpGet("http://tmooc.cn/login/hadlogin/"+ sessionid); // 提交数据包 response = httpclient.execute(httpGet); // url提交地址 httpGet = new HttpGet("http://tts8.tmooc.cn/user/myTTS?sessionId=" + sessionid + "&date="); // 提交数据包 response = httpclient.execute(httpGet); return true; } } catch (UnsupportedEncodingException e) { System.out.println("设置数据包出错"); e.printStackTrace(); } catch (ClientProtocolException e) { System.out.println("提交数据异常"); e.printStackTrace(); } catch (IOException e) { System.out.println("其他错误"); e.printStackTrace(); } return false; } //utf-8编码 public static String bm_utf8(String Str){ String bmjg = null; try { bmjg = URLEncoder.encode(Str, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return bmjg; } // 登陆tmooc时md5加密方法 public final static String Login_MD5(String s) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; try { byte[] btInput = s.getBytes(); // 获得MD5摘要算法的 MessageDigest 对象 MessageDigest mdInst = MessageDigest.getInstance("MD5"); // 使用指定的字节更新摘要 mdInst.update(btInput); // 获得密文 byte[] md = mdInst.digest(); // 把密文转换成十六进制的字符串形式 int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } } }
客户端,采用多线程。一个接收服务器消息,一个发送消息给服务器。 服务器,采用select()进行IO复用。 编译文件是Makefile。 (1)用户登录: 【1】client端接收用户名和密码->存于结构体中->将结构体发送给server端。 【2】server端接收client发送的结构体->打开存储用户名密码的文件->文件写入链表中->遍历链表验证用户信息。 【3】server端验证正确发送“登陆成功”消息,错误发回“登陆失败”消息。client端接收,“登陆成功”则进入聊天,“登陆失败”则跳出。 【4】若验证成功,server端产生一个新的套接字newfd,将它与用户名封装于同一个结构体中,存储在线用户的信息。 消息、存储在线用户信息结构体: typedef struct message { int type; //服务器用于判断该执行的功能 int fd; int mode; //标志位,表示用户的发言权限,1为正常,0为禁言 char name[NAMELEN]; char mima[NAMELEN]; char from[20]; char to[20]; //聊天时的收信人 char file_name[20]; //发送文件时的文件名 char mtext[100]; //聊天时发送的消息内容 struct message *next; }Mess; (2)一对多聊天: 【1】client端发送欲发送的信息给server端。 【2】server端遍历在线人信息链表,找到每个在线人的套接字描述符,将消息发送给链表中的每个人。 【3】可以通过输入“:)”, “:(”, “bye”来发送笑脸,悲伤脸和退出聊天;检测敏感词汇“fuck”、“shit”,禁止发送。 (3)一对一聊天: 【1】client端发送欲发送的信息和信息的接收者给server端。 【2】server端根据收到的接收者名字在在线人链表中查找该接收者的套接字描述符,找到后就将消息发送给该接收者。 【3】可以通过输入“:)”, “:(”, “bye”来发送笑脸,悲伤脸和退出聊天;检测敏感词汇“fuck”、“shit”,禁止发送。 (4)文件传输 【1】client端发送预发送的文件名和接收者名字到server端。 先打开(不存在则创建)一个文件,将文件内容读到缓冲区buffer,再将buffer的内容复制到结构体Mess中,最后将结构体发送给server端。 【2】server端先将接收到的文件重命名(因为相同文件目录下不能存在同名文件),再将收到的文件和新的文件名一同放入tab1中(并且在tab1开头写“#”)发送给client端。 【3】当client端收到以“#”开头的消息,执行文件接收,先创建一个文件,再写入相应内容。 (5)管理员模式 【1】禁言 【2】解禁
JavaWeb聊天室项目是基于JavaWeb技术开发的在线聊天系统。它允许用户通过浏览器或客户端应用程序进行实时的文字、图片、文件等多媒体信息的交流和分享。 该项目的主要功能包括用户注册登录、创建聊天室、加入聊天室、发送消息、接收消息等。下面是该项目的一般实现步骤: 1. 环境搭建:安装Java开发环境(如JDK)、Web服务器(如Tomcat)、数据库(如MySQL)等。 2. 创建数据库:设计并创建数据库表,包括用户表、聊天室表、消息表等。 3. 用户注册登录:实现用户注册和登录功能,包括用户信息的验证和存储。 4. 创建聊天室:用户可以创建自己的聊天室,并设置聊天室的名称、密码等。 5. 加入聊天室:用户可以通过输入聊天室名称和密码来加入已存在的聊天室。 6. 发送消息:用户可以在聊天室中发送消息,包括文字、图片、文件等。 7. 接收消息:用户可以实时接收其他用户发送的消息,并在界面上展示。 8. 聊天记录存储:将聊天室中的消息进行存储,以便用户可以查看历史消息。 9. 安全性处理:对用户输入进行合法性验证,防止恶意攻击和非法操作。 10. 界面设计:设计用户友好的界面,提供良好的用户体验。 以上是一个简单的JavaWeb聊天室项目的基本实现步骤,具体的实现方式和技术选型可以根据实际需求和个人喜好进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值