简单搭建一个基于Linux的网络聊天室

文章详细介绍了如何搭建TCP服务器,包括创建链表管理用户信息,实现TCP服务器相关API,以及当客户端连接时创建新线程处理通信。同时,给出了客户端的实现,客户端通过创建读写线程与服务器交互。最后提到了使用Cmake进行项目编译的配置代码。
摘要由CSDN通过智能技术生成

1 搭建TCP服务器

1.1 先创建一个链表保存用户的相关信息,如果使用了数据库就可以这些数据保存在数据库里面。

1.1.1 list.h
#ifndef _LIST_H
#define _LIST_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct Node
{
    int id;
    int sockfd;
    struct Node *next;
}Node;
/*创建链表头节点*/
Node *list_head(void);
int list_empty(Node *head);
/*链表的长度*/
int list_len(Node *head);
/*增加数据*/
int list_add(Node *head,int id,int sockfd);
/*按照sockfd删除链表节点*/
int list_delete(Node *head,int sockfd);
/*根据ID号查找sockfd*/
int list_select_sockfd(Node *head,int id);
/*根据sockfd查找id*/
int list_seletc_id(Node *head,int sockfd);
/*将用户ID放在buf里面*/
int list_user(Node *head,int *id,char *buf);
/*遍历链表*/
int list_show(Node *head);
/*获取随机ID*/
int list_id(Node *head,int n);

#endif

1.1.2 list.c
#include "../inc/list.h"
/*创建链表头节点*/
Node *list_head(void)
{
    Node *head=(Node *)malloc(sizeof(Node));
    if(head==NULL)
    {
    perror("malloc");
    return NULL;
    }
    head->id=-1;
    head->sockfd=-1;
    head->next=NULL;
    return head;
}
/*判断链表为空*/
int list_empty(Node *head)
{
    Node *q=head;
    if(q->next==NULL)
      return 1;
    return -1;

}
/*链表的长度*/
int list_len(Node *head)
{
    int len=0;
    Node *q=head->next;
    if(list_empty(head)==1)
        return 0;
    while(q!=NULL)
    {
        len++;
        q=q->next;
    }
    return len;
    
    
}
/*增加数据*/
int list_add(Node *head,int id,int sockfd)
{
    Node *q=(Node *)malloc(sizeof(Node));
    if(q==NULL)
        return -1;
    Node *p=head;
    while(p->next!=NULL)
    {
        p=p->next;
    }
    p->next=q;
    q->next=NULL;
    q->id=id;
    q->sockfd=sockfd;
    return 1;

}
/*按照sockfd删除链表节点*/
int list_delete(Node *head,int sockfd)
{
    Node *q=head;
    while(q->next!=NULL)
    {
        if(q->next->sockfd==sockfd)
            break;
        q=q->next;
    }
    Node *p=q->next;
    q->next=p->next;
    free(p);
    return 1;
}
/*根据ID号查找sockfd*/
int list_select_sockfd(Node *head,int id)
{
    Node *q=head->next;
    while(q!=NULL)
    {
        if(q->id==id)
        break;
        q=q->next;
    }
    return q->sockfd;
}
/*根据sockfd查找id*/
int list_seletc_id(Node *head,int sockfd)
{
    Node *q=head->next;
    while(q!=NULL)
    {
        if(q->sockfd==sockfd)
        break;
        q=q->next;
    }
    return q->id;
}
/*将用户ID放在buf里面*/
int list_user(Node *head,int *id,char *buf)
{
    int i=0;
    Node *q=head->next;
    while(q!=NULL)
    {
        if(q->id!=*id)
        {
            *(buf+i)=q->id+48;
        }
        q=q->next;
    }
    return 1;
}
/*遍历链表*/
int list_show(Node *head)
{
    Node *q=head->next;
    if(list_empty(head)==1)
    {
        return -1;
    }
    while(q!=NULL)
    {
        printf("用户id为:%d sockfd:%d",q->id,q->sockfd);
        q=q->next;
    }
    return 1;
}
/*获取随机ID*/
int list_id(Node *head,int n)
{
    Node *q=head->next;
    for(int i=0;i<n;i++)
    {
        q=head->next;
        while(q!=NULL)
        {
            if(i==q->id)
                break;
            q=q->next;
        }
        if(q==NULL)
          return i;
    }
    return 1;
}

1.2 创建TCP服务器的相关API

1.2.1 server.h
#ifndef _SERVER_H
#define _SERVER_H

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int sockfd_init(int port,int n);
int accept_init(int listen_t);


#endif
1.2.2 server.c
#include "../inc/server.h"
int sockfd_init(int port,int n)
{
    struct sockaddr_in server;
    int server_len=sizeof(server);

    int listen_t=socket(AF_INET,SOCK_STREAM,0);

    server.sin_family=AF_INET;
    //指定固定IP
    server.sin_addr.s_addr=inet_addr("192.168.6.251");
    server.sin_port=htons(port);
    if(bind(listen_t,(struct sockaddr *)&server,server_len)==-1)
    {
        perror("bind");
    }
    listen(listen_t,n);
    return listen_t;
}
int accept_init(int listen)
{
    struct sockaddr_in client;
    socklen_t caddrlen=sizeof(client);
    int sockfd=accept(listen,(struct sockaddr *)&client,&caddrlen);
    if(sockfd==-1)
    {
        perror("accept");
        return -1;
    }
    printf("用户端口号为:%d  用户IP为%s\n",ntohs(client.sin_port),inet_ntoa(client.sin_addr));
    return sockfd;
}

1.3 当每个客户端进行连接就新建一个线程

1.3.1 pthread_fun.h
#ifndef _PTHREAD_FUN_H
#define _PTHREAD_FUN_H
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "list.h"

typedef struct  head_sockfd
{
    int sockfd;
    Node *head;
    pthread_mutex_t *mutex;
}head_sockfd;
void *my_read(void *arg);
void *my_write(void *arg);



#endif
1.3.2 pthread_fun.c
#include "../inc/pthread_fun.h"



void *my_read(void *arg)
{
   char buf[512]={0};
   char mesbuf[512]={0};
   char send_buf[1024]={0};
   head_sockfd *hs=NULL;
   hs=(head_sockfd *)arg;
   printf("hs->sockfd:%d\n",hs->sockfd);
   int len=0;
   int id=0;
   int client_id=0;
   int client_sockfd=0;
   while(1)
   {
        bzero(buf,sizeof(buf));
        bzero(mesbuf,sizeof(mesbuf));
        bzero(send_buf,sizeof(send_buf));

        len=read(hs->sockfd,buf,sizeof(buf));
        /*判断是否为退出指令*/
        if(strstr(buf,"exit")!=NULL||len==0)
        {
            /*加锁*/
            pthread_mutex_lock(hs->mutex);
            /*删除用户*/
            list_delete(hs->head,hs->sockfd);
             /*解锁*/
            pthread_mutex_unlock(hs->mutex);
            pthread_exit(0);
            /* 销毁互斥锁 */
            pthread_mutex_destroy(hs->mutex);
        }
        /*判断是否为显示在线用户的指令*/
        else if(strstr(buf,"show")!=NULL)
        { 
            /*根据sockfd获取ID*/
            client_id=list_seletc_id(hs->head,hs->sockfd);
            /*获取在线用户保存在send_buf里面*/
            int flage=list_user(hs->head,&client_id,send_buf);
            if(flage==0)
            {
                write(hs->sockfd,"no",2);
            }
            else
            {
                printf("show:%s\n",send_buf);
                write(hs->sockfd,send_buf,strlen(send_buf));
            }
        }
        /*群聊信息处理*/
        else if(buf[1]!=' ')
        {
            Node *q=hs->head->next;
            /*根据sockfd获取用户ID*/
            client_id=list_seletc_id(hs->head,hs->sockfd);
            while(q!=NULL)
            {
               if(q->id!=client_id)
               {
                  sprintf(send_buf,"%d %s",client_id,buf);
                  write(q->sockfd,send_buf,strlen(send_buf));
               }
               q=q->next;
            }
        }
        /*私聊信息处理*/
        else
        {
         sscanf(buf,"%d %s",&id,mesbuf);
         strcpy(mesbuf,buf+2);
         /*根据目标ID获取目标sockfd*/
         client_sockfd=list_select_sockfd(hs->head,id);
         client_id=list_seletc_id(hs->head,hs->sockfd);
         if(client_sockfd==-1)
         {
             write(hs->sockfd,"no",2);
         }else{
            sprintf(send_buf,"%d %s",client_id,mesbuf);
            write(client_sockfd,send_buf,strlen(send_buf));
         }
        }
   }
   return NULL;
}

1.4 主函数

1.4.1 main.c
#include "../inc/server.h"
#include "../inc/list.h"
#include "../inc/pthread_fun.h"
#include <pthread.h>
#define N 5
Node *head=NULL;
char id[2]={0,'\0'};
/* 互斥锁定义 */
pthread_mutex_t lock; 
int main(int argc,char *argv[])
{
    if(argc<2)
    {
        printf("输入格式错误:例如 a.out 8888\n");
        return -1;
    }
    int sockfd;
    head=list_head();
   
    int listen_t=sockfd_init(atoi(argv[1]),N);
    int i=0;
    while(1)
    {
        i=list_id(head,N);
        printf("I:%d\n",i);
        id[0]=i+48;
        /*等待客户端进行连接*/
        sockfd=accept_init(listen_t);
        /*互斥锁初始化*/
        if(pthread_mutex_init(&lock, NULL)!=0)
        {
            perror("mutex");
        }
         if(sockfd==-1)
            continue;
        write(sockfd,id,sizeof(id));
        printf("sock:%d\n",sockfd);
        /*为新用户开辟空间*/
        head_sockfd *hs=(head_sockfd *)malloc(sizeof(head_sockfd));
        hs->head=head;
        hs->sockfd=sockfd;
        hs->mutex=&lock;
        /*加锁*/
        pthread_mutex_lock(&lock);
        /*在用户链表添加用户*/
        list_add(head,i,sockfd);
        /*解锁*/
        pthread_mutex_unlock(&lock);
        pthread_t pthread_read;
        pthread_create(&pthread_read,NULL,my_read,hs);
        /*线程分离,为了避免僵尸线程*/
        pthread_detach(pthread_read);
        printf("1111\n");
    }
    
    return 0;
}




1.5 运行

2 搭建TCP客户端

2.1  客户端读与写分别创建一个线程

2.1.1 pthread_fun.h
#ifndef _PTHREAD_FUN_H
#define _PTHREAD_FUN_H
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

void *cl_write(void *arg);
void *cl_read(void *arg);

#endif
2.1.2 pthread_fun.c
#include "../inc/pthread_fun.h"
char read_buf[1024]={0};
char write_buf[1024]={0};

void show(int a)
{
    printf("                 欢迎用户%d登陆\n",a);
    printf("              私聊模式    :id 数据\n");
    printf("              查看用户    :show\n");
    printf("              退出登陆    :exit\n");
    printf("              群聊模式    :数据\n");
}

/*写线程,从终端获取数据转发给服务器*/
void *cl_write(void *arg)
{
    int sockfd=*((int *)arg);
    while(1)
    {
    bzero(write_buf,sizeof(write_buf));
    /*从键盘获取数据*/
    fgets(write_buf,sizeof(write_buf),stdin);
    write(sockfd,write_buf,strlen(write_buf));
    if(strstr(write_buf,"exit")!=NULL)
    {
        exit(0);
    }
    }
}
/*从服务器读取数据然后进行数据解析*/
void *cl_read(void *arg)
{
    int sockfd=*((int *)arg);
    int id=0;
    char buf[1024]={0};
    int len=0;
    read(sockfd,read_buf,sizeof(read_buf));
    len=(int)read_buf[0]-48;
    show(len);//显示界面
    while(1)
    {
        bzero(read_buf,sizeof(read_buf));
        bzero(buf,sizeof(buf));
        len=read(sockfd,read_buf,sizeof(read_buf));
        if(len==0)
        {
            exit(0);
        }
        else if(strstr(read_buf,"no")!=NULL)
        {
            printf("没有此用户或者其他用户\n");
        }
        else if(read_buf[1]!=' ')
        {
            for(int i=0;i<strlen(read_buf);i++)
            printf("用户%d:在线\n",read_buf[i]-48);
        }
        else
        {
            /*从接收到的数据里面解析出ID号和数据*/
            sscanf(read_buf,"%d %s",&id,buf);
            /*将数据保存到buf里面,scanf解析数据会出现错误*/
            strcpy(buf,read_buf+2);
            printf("用户%d:发送过来的数据为:%s\n",id,buf);
        }
    }

}

2.2 创建客户端的相关接口

2.2.1 client.h
#ifndef _CLIENT_H
#define _CLIENT_H

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int connect_init(int port,const char *ip);


#endif
2.2.2 client.c
#include "../inc/client.h"

int connect_init(int port,const char *ip)
{
    struct sockaddr_in server;
    socklen_t saddrlen=sizeof(server);
    /*创建cockfd套接字*/
    int connect_t=socket(AF_INET,SOCK_STREAM,0);
    if(connect_t<0)
    {
        perror("socket");
        return -1;
    }
    server.sin_family=AF_INET;
    server.sin_port=htons(port);//主机字节序转换为网络字节序
    server.sin_addr.s_addr=inet_addr(ip);//将字符串形式的IP转换为网络二进制形式
    /*与服务器之间进行连接*/
    if(connect(connect_t,(struct sockaddr *)&server,saddrlen)<0)
    {
        perror("connect");
        return -1;
    }
    return connect_t;
}

2.3 主函数

2.3.1 main.c
#include "../inc/client.h"
#include "../inc/pthread_fun.h"

#include <pthread.h>
#define BUFLEN 1024
void *cl_write(void *arg);
void *cl_read(void *arg);

int main(int argc,char *argv[])
{
    if(argc<3)
    {
        printf("输入格式错误:例如 a.out 8888 192.168.6.251\n");
        return -1;
    }
    int time=0;
    void *thread_result;
    pthread_t client_read;
    pthread_t client_write;
    int connect_t=0;
    while(1)
    {
     connect_t=connect_init(atoi(argv[1]),argv[2]);
    if(connect_t>0)
    {
        break;
    }
    else if(time==0&&connect_t==-1)
    {
        printf("正在与服务器进行连接!!!!\n");
    }
    else if(time==10)
    {
        printf("连接失败 请重试\n");
        return 0;
    }
    sleep(1);
    time++;
    }
    
    /*发送数据到服务器线程*/
    pthread_create(&client_write,NULL,cl_write,&connect_t);
    /*从服务器接收数据线程*/
    pthread_create(&client_read,NULL,cl_read,&connect_t);
   
    /*等到线程退出返回先关信息,然后释放空间*/
    pthread_join(client_read,&thread_result);
    pthread_join(client_write,NULL);
    return 0;
}

2.4 运行

3 如果用Cmake进行编译,下面附上Cmake的代码

3.1 server

cmake_minimum_required(VERSION 2.8)

project(server)

configure_file(
    "${PROJECT_SOURCE_DIR}/config.h.in"
    "${PROJECT_SOURCE_DIR}/build/config.h"
)

option(SERVER_OK "服务器开关" ON)

if(SERVER_OK)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

set(LIST ${PROJECT_SOURCE_DIR}/testFunc/src/list.c)
set(SERVER ${PROJECT_SOURCE_DIR}/testFunc/src/server.c)
set(PTHREAD_FUN ${PROJECT_SOURCE_DIR}/testFunc/src/pthread_fun.c)

add_library(list STATIC ${LIST})
add_library(server STATIC ${SERVER})
add_library(pthread_fun STATIC ${PTHREAD_FUN})



else()

set(SRC_LIST ${PROJECT_SOURCE_DIR}/testFunc/src/main.c)

include_directories(${PROJECT_SOURCE_DIR}/testFunc/inc)

find_library(LIST1 list ${PROJECT_SOURCE_DIR}/lib)
find_library(SERVER1 server ${PROJECT_SOURCE_DIR}/lib)
find_library(PTHREAD_FUN1 pthread_fun ${PROJECT_SOURCE_DIR}/lib)

add_compile_options(-Wall)

add_executable(main ${SRC_LIST})

target_link_libraries(main -pthread)
target_link_libraries(main ${LIST1})
target_link_libraries(main ${SERVER1})
target_link_libraries(main ${PTHREAD_FUN1})

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
endif()

3.2 client

cmake_minimum_required(VERSION 2.8)

project(client)

configure_file (
  "${PROJECT_SOURCE_DIR}/config.h.in"
  "${PROJECT_BINARY_DIR}/build/config.h"
  )

#增加编译选项
option(MYDEBUG "enable debug compilation" OFF)


if(MYDEBUG)
    set(CLIENT ${PROJECT_SOURCE_DIR}/myfun/src/client.c)

    set(PTHREAD_FUNC ${PROJECT_SOURCE_DIR}/myfun/src/pthread_fun.c)

    add_library(client STATIC ${CLIENT})

    add_library(pthread_func STATIC ${PTHREAD_FUNC})

    set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
else()
    set(SRC_LIST ${PROJECT_SOURCE_DIR}/myfun/src/main.c)
    include_directories(${PROJECT_SOURCE_DIR}/myfun/inc)

    find_library(CLIENT1 client ${PROJECT_SOURCE_DIR}/lib)
    find_library(PTHREAD_FUNC pthread_func ${PROJECT_SOURCE_DIR}/lib)
   
    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

    add_compile_options(-Wall)

    add_executable(main ${SRC_LIST})
   

    target_link_libraries(main -pthread)

    target_link_libraries(main ${CLIENT1})

    target_link_libraries(main ${PTHREAD_FUNC})


endif()

可能会因为环境的不同,从而会出现不同的错误。

Linux网络聊天室项目是一个基于Linux操作系统的聊天室应用程序。通过使用C语言编写服务器和客户端代码,可以实现多个用户之间的即时通信。该项目使用MySQL数据库来存储聊天室的相关信息,通过与数据库的交互来实现用户注册、登录、发送消息等功能。 要运行该项目,您需要先编译服务器端代码和客户端代码。编译服务器端的命令是: gcc server.c mysql.c -lmysqlclient -lpthread -o s 。 在编译完成后,您还需要在MySQL数据库中创建一个名为"chatroom"的数据库,并在其中创建一个名为"infor"的数据表。要进行此操作,您需要使用MySQL客户端工具,并执行相应的SQL语句。具体的数据库和数据表的创建可以在您的项目代码中找到。 网络聊天室项目通常包括以下几个主要功能: 1. 用户注册和登录:用户可以注册一个账号,并使用该账号登录到聊天室。 2. 消息发送和接收:用户可以向其他在线用户发送消息,并接收其他用户发送的消息。 3. 在线用户列表:显示当前在线的用户列表,以便用户选择与之进行聊天。 4. 聊天记录保存:将用户之间的聊天记录进行保存,以便后续查看。 在项目的代码中,您可以找到一些特定的函数和方法,例如print_all_data、search、Sconnect等。这些函数和方法用于与MySQL数据库进行交互,执行数据的插入、查询、删除等操作。 在项目的实现过程中,您可能还会使用到多线程编程技术,以实现多个用户的并发连接和通信。 总的来说,Linux网络聊天室项目是一个基于Linux操作系统的聊天室应用程序,通过使用C语言编写服务器和客户端代码,并结合MySQL数据库来实现用户注册、登录、发送消息等功能。您可以根据具体的需求和代码实现来进一步了解和定制该项目。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值