1. 程式人生 > >Linux區域網多人聊天軟體

Linux區域網多人聊天軟體

功能介紹

  1. 客戶端:登陸及註冊;列出當前線上使用者列表、傳送聊天訊息、傳輸檔案等。
  2. 服務端:記錄註冊及線上使用者連結串列、記錄使用者聊天資料、顯示使用者的登陸退出等。

主要知識點:socket套接字、連結串列使用者管理、執行緒建立管理、IO複用、select監聽套接字、檔案讀寫等。

程式下載

服務端主函式程式碼:client.c

/*************************************************************************
    > File Name: client.c
    > Author: mrhjlong
    > Mail: 
[email protected]
> Created Time: 2016年08月01日 星期一 14時40分39秒 ************************************************************************/
#include "userlist.h" void *func_ttl(void *arg) { int fd = (int)arg; while(1) { cli_TTL(fd); sleep(20); } return NULL; } int
main(int argc, char *argv[]) { int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) err_sys("socket error"); struct sockaddr_in des_addr; des_addr.sin_family = AF_INET; des_addr.sin_port = htons(9999); inet_pton(AF_INET, "127.0.0.1", &des_addr.sin_addr); //連線客戶端
int ret = connect(fd, (struct sockaddr *)&des_addr, sizeof(des_addr)); if(ret == -1) err_sys("connect error"); //登陸和註冊登陸 ret = cli_REG_LOG(fd); if(ret == -1) //放棄登陸或註冊,直接退出 { close(fd); return 0; } //建立執行緒傳送保活資訊 pthread_t pid; ret = pthread_create(&pid, NULL, func_ttl, (void *)fd); if(ret != 0) err_sys("pthread create error"); MSG msgdata; fd_set read_set; int n; int flag = 0; char recv_buf[BUFSIZE] = {0}; char fname[50] = {0}; while(1) { FD_ZERO(&read_set); if(flag == 0) { printf("Input a command: s-send | f-file | l-list | q-quit...\n"); FD_SET(0, &read_set); } FD_SET(fd, &read_set); select(fd + 1, &read_set, NULL, NULL, NULL); if(FD_ISSET(0, &read_set)) //輸入響應 { bzero(&msgdata, sizeof(msgdata)); fgets(msgdata.cmd, 50, stdin); if(strcmp(msgdata.cmd, "q\n") == 0) //退出 { pthread_cancel(pid); //關閉執行緒 flag = 1; shutdown(fd, SHUT_WR); FD_CLR(0, &read_set); continue; } else if(strcmp(msgdata.cmd, "l\n") == 0) //傳送命令,列出當前線上使用者 { cli_LIST(fd); continue; } else if(strcmp(msgdata.cmd, "s\n") == 0) { cli_SEND(fd, &msgdata); } else if(strcmp(msgdata.cmd, "f\n") == 0) //傳送FILE命令 { bzero(fname, 50); ret = cli_FILE(fd, &msgdata, fname); if(ret == 0) flag = 1; continue; } else { printf("#################WARNING#################\n"); printf("Input error! Please try again!\n"); printf("#########################################\n"); continue; } } if(FD_ISSET(fd, &read_set)) //接收資訊 { bzero(recv_buf, BUFSIZE); bzero(&msgdata, sizeof(msgdata)); n = recv(fd, recv_buf, BUFSIZE, 0); if(n == 0) //關閉 { printf("closed!\n"); break; } read_XML(recv_buf, &msgdata); if(strcmp(msgdata.cmd, "LISTD") == 0) { printf("**************Online users:**************\n"); printf("%s\n", msgdata.text); printf("*****************************************\n"); continue; } else if(strcmp(msgdata.cmd, "RECV") == 0) { printf("*****************RECV...*****************\n"); printf("FROM:%s\n", msgdata.name); printf("MSG:%s\n", msgdata.text); printf("*****************************************\n"); } else if(strcmp(msgdata.cmd, "NOUSR") == 0) { printf("#################WARNING#################\n"); printf("User:%s is offline! Please try later.\n", msgdata.text); printf("#########################################\n"); flag = 0; } else if(strcmp(msgdata.cmd, "LEAVE") == 0) { pthread_cancel(pid); //關閉執行緒 printf("cmd closed!\n"); break; } else if(strcmp(msgdata.cmd, "FCNT") == 0) //建立傳輸檔案連線,傳送 { cli_FCNT(&msgdata, fname); flag = 0; } else if(strcmp(msgdata.cmd, "FLSN") == 0) //建立傳輸檔案監聽, 接收資訊 { cli_FLSN(fd, &msgdata); } } } close(fd); return 0; }

服務端主函式程式碼:server.c

/*************************************************************************
    > File Name: server.c
    > Author: mrhjlong
    > Mail: [email protected] 
    > Created Time: 2016年08月01日 星期一 14時13分10秒
 ************************************************************************/

#include "userlist.h"

int main(void)
{
    struct list_head list;  //新建線上使用者連結串列
    INIT_LIST_HEAD(&list);  //初始化連結串列頭

    struct list_head usrList;   //新建已註冊使用者連結串列 
    INIT_LIST_HEAD(&usrList);

    FILE *fp = fopen("regUser.txt", "r");
    if(fp == NULL)
        err_sys("open regUser.txt error!\n");
    //獲取已註冊使用者連結串列
    get_user_list(fp, &usrList);    

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
        err_sys("socket error");

    int optval = 1;
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
    if(ret == -1)
        err_sys("setsockopt error!");

    //伺服器地址埠設定
    struct sockaddr_in my_addr;
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(9999);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //繫結伺服器
    ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
    if(ret != 0)
        err_sys("bind error");
    //監聽
    ret = listen(sockfd, 10);
    if(ret != 0)
        err_sys("listen error");
    printf("listening...\n");

    fd_set read_set;
    MSG msgdata;
    char recv_buf[BUFSIZE] = {0};
    int n;
    struData_t *p = NULL;
    struct list_head *pos = NULL;

    struct timeval timeout;
    LD ldata;

    while(1)
    {
        FD_ZERO(&read_set);
        FD_SET(sockfd, &read_set);
        FD_SET(0, &read_set);
        //遍歷連結串列,新增套接字到select
        list_for_each(pos, &list)
        {
            p = list_entry(pos, struData_t, list);
            FD_SET(p->sockfd, &read_set);
        }
        //設定select
        timeout.tv_sec = 3;     //阻塞3秒
        timeout.tv_usec = 0;
        if(pos->prev == &list)
            ret = select(sockfd + 1, &read_set, NULL, NULL, &timeout);
        else
        {
            p = list_entry(pos->prev, struData_t, list);
            ret = select(p->sockfd + 1, &read_set, NULL, NULL, &timeout);
        }
        //超時返回,檢測客戶保活資訊
        if(ret == 0)        
        {
            chk_ttl(&list);
            continue;
        }

        //服務端退出
        if(FD_ISSET(0, &read_set))
        {
            char cmd_quit[50] = {0};
            fgets(cmd_quit, 50, stdin);
            if(strcmp(cmd_quit, "q\n") == 0)    //輸入q退出
            {
                ser_quit(&usrList, &list);
                printf("Closing server!\n");
                break;
            }
            else if(strcmp(cmd_quit, "s\n") == 0)   //輸入s顯示聊天記錄
            {
                FILE *fp = fopen("Chatlog.txt", "r");
                char buffer[100] = {0};
                if(fp == NULL)
                    printf("open Chatlog.txt error!\n");

                printf("*****************Chat record:******************\n");
                fgets(buffer, 100, fp);
                while(strlen(buffer) >= 8)
                {
                    printf("%s", buffer);
                    bzero(buffer, 100);
                    fgets(buffer, 100, fp);
                }
                printf("***********************************************\n");
            }
            else if(strcmp(cmd_quit, "l\n") == 0)   //輸入l獲取已註冊使用者資訊
            {
                FILE *fp = fopen("regUser.txt", "r");
                if(fp == NULL)
                    err_sys("open regUser.txt error!\n");
                //獲取已註冊使用者連結串列
                get_user_list(fp, &usrList);
            }
            else
                printf("Input error! Please try again!\n");

            continue;
        }

        //監聽套接字響應
        if(FD_ISSET(sockfd, &read_set)) 
        {   
            ldata.sockfd = sockfd;
            ldata.usrList = &usrList;
            ldata.list = &list;
            //建立處理註冊和登陸操作的執行緒
            pthread_t pid;
            int n = pthread_create(&pid, NULL, listen_reg_log, (void *)&ldata);
            if(n != 0)
                err_sys("pthread create error");
            n = pthread_detach(pid);
            if(n != 0)
                err_sys("pthread detach error");

            usleep(200);    //等待執行緒accept,否則會重複建立執行緒
            ret--;
        }
        //客戶端套接字響應,接收到資訊
        if(ret > 0)                         
        {
            //遍歷連結串列,找到響應的套接字
            list_for_each(pos, &list)       
            {
                p = list_entry(pos, struData_t, list);
                if(FD_ISSET(p->sockfd, &read_set))
                    break;
            }

            bzero(recv_buf, BUFSIZE);
            n = recv(p->sockfd, recv_buf, BUFSIZE, 0);
            //客戶端主動關閉,服務端釋放記憶體
            if(n == 0)      
            {
                printf("User:%s is leaving...\n", p->client_name);
                close(p->sockfd);
                list_del(pos);
                free(p);
                continue;
            }

            bzero(&msgdata, sizeof(msgdata));
            read_XML(recv_buf, &msgdata);   //解析XML資料

            if(strcmp(msgdata.cmd, "LIST") == 0)        //LIST命令處理
            {
                ser_LIST(p->sockfd, &list, &msgdata);
                continue;
            }
            else if(strcmp(msgdata.cmd, "TTL") == 0)    //TTL命令處理
            {
                time(&(p->ttl));    //重置保活時間
                continue;
            }
            else if(strcmp(msgdata.cmd, "SEND") == 0)   //SEND命令處理
            {
                ser_SEND(p->sockfd, p->client_name, &list, &msgdata);
                continue;
            }
            else if(strcmp(msgdata.cmd, "FILE") == 0)
            {
                ser_FILE(p->sockfd, p->client_name, &list, &msgdata);
            }
        }   
    }
    close(sockfd);
    return 0;
}

相關推薦

Linux區域網聊天軟體

功能介紹 客戶端:登陸及註冊;列出當前線上使用者列表、傳送聊天訊息、傳輸檔案等。 服務端:記錄註冊及線上使用者連結串列、記錄使用者聊天資料、顯示使用者的登陸退出等。 主要知識點:socket套接字、連結串列使用者管理、執行緒建立管理、IO複用、selec

Linux下基於Socket網絡通信的聊天

data break arp pre font linu print lose types.h 服務端 #include <stdio.h> #include <sys/types.h> #include <sys/socket.h>

linux c++select聊天程式

比較簡單的多人聊天程式,可直接執行。 主要是實現功能。沒有介面也沒有多餘功能,只是實現群聊天的功能。c/s模式。 server端用select多路複用來做,可以接受多個客戶端連線。client端啟動2個執行緒控制傳送和接受資料。 ======================

linux c 基於UDP的聊天程式

client.c #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #

linux環境下的聊天程式設計

STEP 1:老師給出了原始的程式,是功能不完善的,也就是讓我們把發信息的使用者名稱和時間啥的顯示出來,這樣不是比較合理嘛;原程式是可以實現傳text的,就是沒有使用者名稱而已。 先跑一下(切換到你放這三檔案的目錄哦~):gcc -o chat chat.c 回車 然後:.

Node + H5 + WebSocket + Koa2 實現簡單的聊天

接收 var msg ont for document back next bsp 服務器代碼 ( 依賴於 koa2, koa-websocket ) /* 實例化外部依賴 */ let Koa = require("koa2"); let WebSocket = r

NIO 聊天

fig pin 仿真 all listen HR code cas ole 一前言 在家休息沒事,敲敲代碼,用NIO寫個簡易的仿真聊天室。下面直接講聊天室設計和編碼。對NIO不了解的朋友,推薦一個博客,裏面寫的很棒: https://javadoop.com/ 裏面

h5移動端聊天室|仿微信界面聊天室|h5聊天

dde ... html5開發 技術 show scrollby anim 世界 info 今年的FIFA世界杯甚是精彩,最近興致高漲就利用HTML5開發了一個手機端仿微信界面聊天室,該h5聊天室采用750px全新伸縮flex布局,以及使用rem響應式配合fontsize

基於tcp和線程的聊天室-C語言

同時 reat 錯誤 con play 分享圖片 tdi %s 線程編程 之前在學習關於網絡tcp和多線程的編程,學了知識以後不用一下總絕對心虛,於是就編寫了一個基於tcp和多線程的多人聊天室。 具體的實現過程:   服務器端:綁定socket對象->設置監聽數-

【WebSocket】---聊天系統

tom after channel 加載 cal parse ssi 獲得 路徑 多人聊天系統 功能說明:多人聊天系統,主要功能點: 1、當你登陸成功後,可以看到所有在線用戶(實際開發可以通過redis實現,我這邊僅僅用map集合) 2、實現群聊功能,

nio 代碼實現簡易聊天

false size spl closed write content 輸入 utf oom 這幾天在學習nio相關知識。實現了一個簡單的多人聊天程序。 服務端代碼; 1 import java.io.IOException; 2 import java.net

【181027】VC++簡單聊天室原始碼

一個簡單的但支援多人同時聊天的VC++聊天室原始碼,支援私聊、支援真實IP相互傳送檔案,使用者名稱可以重複,當用戶進入聊天室時其它使用者會得到通知,伺服器端採用埠模型完成,客戶端採用WSAAynscSelect模型,底層採用xml對傳輸協議進行封裝。伺服器啟動後自動監聽客戶端是否執行,聊天過程中

1024_(即時通訊)使用node.js和socket.io實現聊天

使用node.js和socket.io實現多人聊天室 2015年01月24日 03:24:54 遠古大猛獁 閱讀數:2775 轉自: http://www.cnblogs.com/flyoung2008/archive/2012/07/19/2600132.html

Netty聊天

在簡單聊天室的程式碼中修改ChatServerHandler類,就可以模擬多人聊天的功能 package com.cppdy.server; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext;

Java通過Socket來實現簡單聊天

Socket可以實現網路上兩個程式通過雙向通道進行資料的交換,此外它是Java中網路TCP/IP協議的封裝,例如可以進行網路通訊等等,下面我們就來簡單寫一下多人聊天室。 首先來分析一下要實現的流程 首先建立一個伺服器端,構建ServerSocket並繫結埠 建立sock

Python網路程式設計----實現簡單的聊天

還是用UDP,socket作為主體來實現,之前我們已經實現過單對單socket通訊,這次想實現群發功能 原理其實就是一臺伺服器在負責分配轉發資料,來達成廣播的效果,這些思路其實也差不多 但是多人聊天沒有這麼強的規整性,你可能沒等到A的訊息,就要去和B說話了,多執行緒就可以

基於flask框架,使用websocket實現聊天室功能

後端程式碼:   # web_socket 的收發機制 # web_socket --> web + socket --> http協議 + socket # web_socket協議就是ws協議 # 基於flask框架為web_socket提供服務 from flas

Go語言專案實戰:聊天

功能需求 實現單撩 實現群撩 實現使用者上線的全網通知 實現使用者暱稱 實現聊天日誌的儲存和檢視 服務端實現 type Client struct { conn net.Conn name string addr string } var ( //

使用netty實現一個聊天室--failed: Error during WebSocket handshake: Unexpected response code: 200

初次接觸netty , 本文主要內容如下: 遇到的小bug 聊天室後端程式碼: 聊天室前端程式碼: 遇到的小bug 在使用netty進行websocket程式設計(實現一個簡單的聊天室)時,我遇到了這樣一個奇怪

JAVA_網路程式設計_TCP_Socket通訊_聊天室_聊天/私聊_實現

Socket程式設計 連線(連線是通過輸入輸出流來傳送資料)   建立伺服器 package com.hp.tcp; import java.io.DataInputStream; import java.io.DataOutputStream; import