linux網(wǎng)絡(luò)編程 粗糙的聊天系統(tǒng)

整理一下老師講的。

頭文件link.h
#ifndef LINK_H
#define LINK_H

#include <stdio.h>

typedef struct Node
{
    int iId;
    int sockfd;
    struct Node *pNext;
}Node;//節(jié)點(diǎn)

typedef struct List
{
    int iLen;
    Node *pFirstNode;
}List;//用于儲(chǔ)存多個(gè)用戶的鏈表

Node *makeNode();
List *makeList();
void insertList(List *pList, Node *pNode);
Node *findNode(const List *pList, int iId);
void showList(const List *pList);

#endif

頭文件protocol.h
#ifndef PROTOCOL_H
#define PROTOCOL_H

#include <stdio.h>

typedef unsigned int uint;

enum ENUM_MSG_TYPE
{
    ENUM_MSG_MIN = 0,
    ENUM_MSG_PRIVATE_CHAT,   //私聊
    ENUM_MSG_GROUP_CHAT,     //群聊
    ENUM_MSG_REQUEST_USR_LIST,   //請(qǐng)求用戶列表
    ENUM_MSG_RESPONSE_USR_LIST,  //回復(fù)用戶列表
    ENUM_MSG_MAX = 0x0fffffff//保持占32位
};

typedef struct PDU
{
    uint iPDULen;    //整個(gè)協(xié)議數(shù)據(jù)單元的大小
    uint iType;      //消息類(lèi)型:群聊,私聊,獲取用戶列表
    uint iDataLen;   //消息的長(zhǎng)度
      int iFromId;     //消息的發(fā)送者
    int iToId;       //消息的接收者
    char caData[4];  //要發(fā)送的消息
}PDU;

PDU *makePDU(int iDataLen);
uint parsePDU(PDU *pPDU);

#endif
客戶端與服務(wù)器都要和這個(gè)文件一起編譯 protocol.c
#include "protocol.h"
#include <stdlib.h>
#include <string.h>
PDU *makePDU(int iDataLen)//創(chuàng)建數(shù)據(jù)協(xié)議存儲(chǔ)端
{
    PDU *pPDU = NULL;
    uint iLen = sizeof(PDU) - 4*sizeof(char) + iDataLen;
    //四個(gè)成員的大小-4個(gè)可變長(zhǎng)得到前面三個(gè)的長(zhǎng)度+要發(fā)送數(shù)據(jù)的長(zhǎng)度=整個(gè)結(jié)構(gòu)體的大小
    pPDU = (PDU *)malloc(iLen);
    if (NULL != pPDU)
    {
        memset(pPDU, 0, iLen);//初始化
        pPDU->iPDULen = iLen;//協(xié)議數(shù)據(jù)單元的大小
        pPDU->iType = ENUM_MSG_MIN;//消息類(lèi)型初始值
        pPDU->iDataLen = iDataLen;
    }
    return pPDU;
}
uint parsePDU(PDU *pPDU)
{
    uint iType = ENUM_MSG_MIN;
    if (NULL != pPDU)
    {
        iType = pPDU->iType;
    }
    return iType;
}
服務(wù)器 server.c
/*socket()*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include "link.h"
#include "protocol.h"

List *pList = NULL;

void sendPDUToClient(int sockfd, const PDU *pPDU)
{
    if (sockfd < 0 || NULL == pPDU)
    {
        return;
    }
    int ret = -1;
    int iLeft = pPDU->iPDULen;//剩余單元的大小
    int iSended = 0;//已發(fā)送的人數(shù)
    while (iLeft)
    {
        ret = write(sockfd, (char *)pPDU+iSended, iLeft);
        if (-1 == ret)
        {
            if (EINTR == errno)
            {
                continue;
            }
            perror("write");
            return;
        }
        iSended += ret;
        iLeft -= ret;
    }
}

void sendUsrListToClient(int sockfd, const List *pList)//把使用者鏈表發(fā)送給客戶端
{
    if (sockfd < 0 || NULL == pList)
    {
        return;
    }
    int iDataLen = pList->iLen * sizeof(int);
    PDU *pPDU = makePDU(iDataLen);//一點(diǎn)一點(diǎn)拼接字節(jié)
    if (NULL == pPDU)
    {
        return;
    }
    Node *pNode = pList->pFirstNode;
    int i = 0;
    while (NULL != pNode)
    {
        memcpy(pPDU->caData+i*sizeof(int)
               , &(pNode->iId), sizeof(int));
        i++;
        pNode = pNode->pNext;
    }
    pPDU->iType = ENUM_MSG_RESPONSE_USR_LIST;   
    sendPDUToClient(sockfd, pPDU);//發(fā)送信息給客戶端
}

void updateClientsUsrList(const List *pList)
{
    if (NULL == pList)
    {
        return;
    }
    PDU *pPDU = makePDU(sizeof(int));
    if (NULL == pPDU)
    {
        return;
    }
    memcpy(pPDU->caData, &(pList->pFirstNode->iId)
           , sizeof(int));
    pPDU->iType = ENUM_MSG_RESPONSE_USR_LIST;
    Node *pNode = pList->pFirstNode->pNext;
    while (NULL != pNode)
    {
        printf("id:%d, sockfd:%d\n", pNode->iId, pNode->sockfd);
        sendPDUToClient(pNode->sockfd, pPDU);
        pNode = pNode->pNext;
    }
}

PDU *readDataFromClient(int sockfd)
{
    int ret = -1;
    uint iPDULen = 0;
    ret = read(sockfd, &iPDULen, sizeof(uint));
    if (-1 == ret)
    {
        perror("read");
        return NULL;
    }
    PDU *pPDU = makePDU(iPDULen-3*sizeof(int));
    if (NULL == pPDU)
    {
        return NULL;
    }
    pPDU->iPDULen = iPDULen;
    int iLeft = iPDULen - sizeof(int);
    int iReaded = sizeof(int);
    while (iLeft)
    {
        ret = read(sockfd, (char *)pPDU+iReaded, iLeft);
        if (-1 == ret)
        {
            if (EINTR == errno)
            {
                continue;
            }
            perror("while read");
            return NULL;
        }
        iLeft -= ret;
        iReaded += ret;
    }
    return pPDU;
}

void handleGroupChat(PDU *pPDU)//群發(fā)
{
    if (NULL != pPDU)
    {
        Node *pNode = pList->pFirstNode;
        while (NULL != pNode)
        {
            sendPDUToClient(pNode->sockfd, pPDU);
            pNode = pNode->pNext;
        }
    }
}

void handlePrivateChat(PDU *pPDU)//私聊
{
    if (NULL != pPDU)
    {
        Node *pNode = NULL;
        pNode = findNode(pList, pPDU->iToId);
        if (NULL != pNode)
        {
            sendPDUToClient(pNode->sockfd, pPDU);
        }
    }
}

void *handleClient(void *arg)//處理客戶端發(fā)來(lái)的信息
{
    int clientSockfd = (int)arg;
    PDU *pPDU = NULL;
    uint iType = 0;
    while (1)
    {
        pPDU = readDataFromClient(clientSockfd);
        if (NULL != pPDU)
        {
            iType = parsePDU(pPDU);
            switch (iType)
            {
            case ENUM_MSG_REQUEST_USR_LIST:
                break;
            case ENUM_MSG_GROUP_CHAT:
                handleGroupChat(pPDU);
                break;
            case ENUM_MSG_PRIVATE_CHAT:
                handlePrivateChat(pPDU);
                break;
            default:
                break;
            }
        }
    }
}

int main(void)
{
        pList = makeList();
    if (NULL == pList)
    {
        return -1;
    }
    int iBaseId = 1001;

    int sockfd = -1;
    //創(chuàng)建socket描述符,用于監(jiān)聽(tīng)接受客戶端的連接
    //AF_INET:ipv4
    //SOCK_STREAM:tcp協(xié)議
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        perror("socket");
        return -1;
    }
    int ret = -1;
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;   //ipv4
    serverAddr.sin_port = htons(8888); //port
    //server ip
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    //根據(jù)自己電腦的地址,ifconfig查詢
    bzero(&(serverAddr.sin_zero), 8);
    //將sockfd和地址進(jìn)行綁定    
    ret = bind(sockfd, (struct sockaddr *)&serverAddr
                   , sizeof(serverAddr));
    if (-1 == ret)
    {
        perror("bind");
        return -1;
    }
    //監(jiān)聽(tīng)客戶端的連接
    //sockfd:監(jiān)聽(tīng)的socket描述符
    //5:表示未經(jīng)過(guò)處理的連接請(qǐng)求隊(duì)列可以容納的最大數(shù)目
    ret = listen(sockfd, 5);
    if (-1 == ret)
    {
        perror("listen");
        return -1;
    }
    struct sockaddr_in clientAddr;
    int iLen = sizeof(clientAddr);
    int clientSockfd = -1;
    Node *pNode = NULL;
    pthread_t thread;
    while (1)
    {
        //阻塞等待客戶端的連接,直到有客戶端連接
        //成功返回socket描述符,
        //該socket描述符用于服務(wù)器與客戶端進(jìn)行通信
        //失敗返回-1
        //sockfd:監(jiān)聽(tīng)的socket描述符
        //clientAddr:用于保存客戶端的地址等信息
        //iLen:地址長(zhǎng)度變量指針
        clientSockfd = accept(sockfd
                        , (struct sockaddr *)&clientAddr
                        , &iLen);
        if (-1 == clientSockfd)
        {
            perror("accept");
            return -1;
        }
        printf("there is a client connected...\n");

        pthread_create(&thread, NULL, handleClient
                       , (void *)clientSockfd);

        pNode = makeNode();
        if (NULL == pNode)
        {
            continue;
        }
        pNode->iId = iBaseId;
        pNode->sockfd = clientSockfd;
        iBaseId++;
        insertList(pList, pNode);
        sendUsrListToClient(clientSockfd, pList);
        updateClientsUsrList(pList);
    }

    return 0;
}
客戶端 client.c
/*socket()*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include "link.h"
#include "protocol.h"

#define MAX_DATA_LEN 64
List *pList = NULL;

uint g_iId = 0;

void getUsrListFromPDU(PDU *pPDU)
{
    if (NULL == pPDU || NULL == pList)
    {
        return;
    }
    int iNum = pPDU->iDataLen / sizeof(int);
    Node *pNode = NULL;
    int i = 0;
    while (iNum)
    {
        pNode = makeNode();
        if (NULL == pNode)
        {
            return;
        }
        memcpy(&(pNode->iId), pPDU->caData+i*sizeof(int)
               , sizeof(int));
        insertList(pList, pNode);
        i++;
        iNum--;
    }
    if (0 == g_iId)
    {
        pNode = pList->pFirstNode;
        while (NULL != pNode->pNext)
        {
            pNode = pNode->pNext;
        }
        g_iId = pNode->iId;
    }
    showList(pList);
}

PDU *readDataFromServer(int sockfd)
{
    int ret = -1;
    uint iPDULen = 0;
    ret = read(sockfd, &iPDULen, sizeof(uint));
    if (-1 == ret)
    {
        perror("read");
        return NULL;
    }
    PDU *pPDU = makePDU(iPDULen-3*sizeof(int));
    if (NULL == pPDU)
    {
        return NULL;
    }
    pPDU->iPDULen = iPDULen;
    int iLeft = iPDULen - sizeof(int);
    int iReaded = sizeof(int);
    while (iLeft)
    {
        ret = read(sockfd, (char *)pPDU+iReaded, iLeft);
        if (-1 == ret)
        {
            if (EINTR == errno)
            {
                continue;
            }
            perror("while read");
            return NULL;
        }
        iLeft -= ret;
        iReaded += ret;
    }
    return pPDU;
}

void showChatData(PDU *pPDU)
{
    if (NULL != pPDU)
    {
        char caMsg[MAX_DATA_LEN] = {'\0'};
        memcpy(caMsg, pPDU->caData, pPDU->iDataLen);
        printf("%d says: %s\n", pPDU->iFromId, caMsg);
    }
}

void *readThread(void *arg)
{
    int sockfd = (int)arg;
    PDU *pPDU = NULL;
    uint iType = 0;
    while (1)
    {
        pPDU = readDataFromServer(sockfd);
        iType = parsePDU(pPDU);
        switch (iType)
        {
        case ENUM_MSG_RESPONSE_USR_LIST:
            getUsrListFromPDU(pPDU);
            break;
        case ENUM_MSG_GROUP_CHAT:
        case ENUM_MSG_PRIVATE_CHAT:
            showChatData(pPDU);
            break;
        default:
            break;
        }
    }
    return NULL;
}

void sendPDUToServer(int sockfd, const PDU *pPDU)
{
    if (sockfd < 0 || NULL == pPDU)
    {
        return;
    }

    int ret = -1;
    int iLeft = pPDU->iPDULen;
    int iSended = 0;
    while (iLeft)
    {
        ret = write(sockfd, (char *)pPDU+iSended, iLeft);
        if (-1 == ret)
        {
            if (EINTR == errno)
            {
                continue;
            }
            perror("write");
            return;
        }
        iSended += ret;
        iLeft -= ret;
    }
}

void readDataFromSTDIN(char caData[MAX_DATA_LEN])
{
    memset(caData, '\0', MAX_DATA_LEN);
    int ret = read(STDIN_FILENO, caData, MAX_DATA_LEN);
    if (-1 == ret)
    {
        perror("read");
        exit(0);
    }
    if ('\0' != caData[MAX_DATA_LEN-1]
        && '\n' != caData[MAX_DATA_LEN-1])
    {
        while ('\n' != getchar())
        {}
    }
    caData[MAX_DATA_LEN-1] = '\0';
}

int main(void)
{
    pList = makeList();
    if (NULL == pList)
    {
        return -1;
    }

    int sockfd = -1;
    //創(chuàng)建sockfd用于和服務(wù)器通信
    //AF_INET: ipv4
    //SOCK_STREAM:tcp
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        perror("socket");
        return -1;
    }
    int ret = -1;
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;   //ipv4
    serverAddr.sin_port = htons(8888); //port
    //server ip
    serverAddr.sin_addr.s_addr = inet_addr("192.168.16.193");
    bzero(&(serverAddr.sin_zero), 8);

    //連接服務(wù)器
    ret = connect(sockfd, (struct sockaddr *)&serverAddr
                  , sizeof(serverAddr));
    if (-1 == ret)
    {
        perror("connect");
        return -1;
    }

    pthread_t thread;
    ret = pthread_create(&thread, NULL
                         , readThread, (void*)sockfd);
    if (0 != ret)
    {
        printf("%s\n", strerror(ret));
        return -1;
    }

    //write data to server
    char caData[MAX_DATA_LEN] = {'\0'};
    int iLen = 0;
    PDU *pPDU = NULL;
    int iToId = 0;
    char caMsg[MAX_DATA_LEN] = {'\0'};
    while (1)
    {
        //1002 hello
        readDataFromSTDIN(caData);
        memset(caMsg, '\0', MAX_DATA_LEN);
        sscanf(caData, "%d%s", &iToId, caMsg);
      
        iLen = strlen(caMsg);//取長(zhǎng)度
        if (iLen > 0)
        {
            pPDU = makePDU(iLen);
            memcpy(pPDU->caData, caMsg, iLen);
            pPDU->iFromId = g_iId;
            if (0 == iToId)
            {
                pPDU->iType = ENUM_MSG_GROUP_CHAT;
            }
            else
            {
                pPDU->iToId = iToId;
                pPDU->iType = ENUM_MSG_PRIVATE_CHAT;
            }
            sendPDUToServer(sockfd, pPDU);
        }
    }

    return 0;
}

使用時(shí)修改一下客戶端里的ip地址與開(kāi)服務(wù)器的人機(jī)器對(duì)應(yīng),就可以實(shí)現(xiàn)簡(jiǎn)單對(duì)話和群聊。很多知識(shí)點(diǎn)不熟練,雖然看懂了但自己寫(xiě)還是很費(fèi)勁。話說(shuō)很多公司就專(zhuān)門(mén)需要做這方面的人啊,真希望我自己可以從頭到尾寫(xiě)一個(gè)出來(lái)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容