网络编程课例简析

来源:互联网 发布:网络电影演员火不了 编辑:程序博客网 时间:2024/06/05 02:28

服务器与客户端简例

这个小例子是教师在课上讲过后让我们课后理解的题目,感觉含括了很多基础的用法,于是便单独拿出来自己看懂理解。(老师的这个驼峰命名法看得我有点难受)


public.h

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>typedef unsigned int uint;#define NAME_LEN 32#define PWD_LEN 32#define PER_MAX_IO_BYTES 4096

客户端信息储存 (文件保存)

file.h

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include "public.h"#include "list.h"#define USRINFO_PATH  "./usr.info"int openFile(const char *pathname, int flags);void saveUsrInfoToFile(const char *pathname, const List *list);void getUsrInfoFromFile(const char *pathname, List *list);

file.c

#include "file.h"//打开保存文件 usr.infoint openFile(const char *pathname, int flags){    int fd = -1;    fd = open(pathname, flags|O_CREAT, 0664);    if (-1 == fd)    {        perror("open");        exit(EXIT_FAILURE);    return fd;}//保存数据到 usr.infovoid saveUsrInfoToFile(const char *pathname                       , const List *list){    if (NULL == list)    {        return;    }    int fd = openFile(USRINFO_PATH, O_RDWR);    Node *node = list->pFirstNode;    while (NULL != node)    {        write(fd, node, sizeof(Node));        node = node->pNext;    }    close(fd);}//从 usr.info 中提取数据void getUsrInfoFromFile(const char *pathname                        , List *list){    if (NULL == list)    {        return;    }    int fd = openFile(USRINFO_PATH, O_RDWR);    Node *node = NULL;    while (1)    {        node = makeNode();        ret = read(fd, node, sizeof(Node));        if (-1 == ret || 0 == ret)        {            free(node);            break;        }        node->sockfd = -1;        node->pNext = NULL;        insertList(list, node);    }    close(fd);}

客户端信息储存 (链表)

list.h

//链表结点typedef struct Node{    uint uiId;    char caPwd[PWD_LEN];    int sockfd;    struct Node *pNext;}Node;//链表表头typedef struct List{    int iLen;    Node *pFirstNode;}List;Node *makeNode();List *makeList();void insertList(List *list, Node *node);void showList(const List *list);Node *findNodeById(uint id, const List *list);

list.c

#include "list.h"//创建客户端结点Node *makeNode(){    Node *node = (Node *)malloc(sizeof(Node));    if (NULL == node)    {        printf("malloc node failed\n");        exit(EXIT_FAILURE);    }    memset(node, 0, sizeof(Node));    return node;}//创建客户端链表(表头)List *makeList(){    List *list = (List *)malloc(sizeof(List));    if (NULL == list)    {        printf("malloc list failed\n");        exit(EXIT_FAILURE);    }    memset(list, 0, sizeof(List));    return list;}//插入客户端结点void insertList(List *list, Node *node){    if (NULL != list && NULL != node)    {        node->pNext = list->pFirstNode;        list->pFirstNode = node;        list->iLen++;    }}//打印链表void showList(const List *list){    if (NULL == list)    {        return;    }    Node *node = list->pFirstNode;    while (NULL != node)    {        printf("id:%u, pwd:%s, sockfd:%d\t", node->uiId, node->caPwd, node->sockfd);        node = node->pNext;    }    putchar('\n');}//根据 ID 查找客户端结点Node *findNodeById(uint id, const List *list){    Node *node = NULL;    if (NULL != list)    {        node = list->pFirstNode;        while (NULL != node)        {            if (id == node->uiId)            {                break;            }            node = node->pNext;        }    }    return node;}

创建协议

protocol.h

#include "public.h"enum ENUM_MSG_TYPE{    ENUM_MSG_TYPE_MIN = 0,    ENUM_MSG_TYPE_REGIST_REQUEST,       //注册请求    ENUM_MSG_TYPE_REGIST_RESPOND,       //注册回复    ENUM_MSG_TYPE_LOGIN_REQUEST,        //登录请求    ENUM_MSG_TYPE_LOGIN_RESPOND,        //登录回复    ENUM_MSG_TYPE_PRIVATE_CHAT_REQUEST, //私聊请求    ENUM_MSG_TYPE_PRIVATE_CHAT_RESPOND, //私聊回复    ENUM_MSG_TYPE_GROUP_CHAT_REQUEST,   //群聊请求    ENUM_MSG_TYPE_GROUP_CHAT_RESPOND,   //群聊回复    ENUM_MSG_TYPE_EXIT_REQUEST,         //退出请求    ENUM_MSG_TYPE_EXIT_RESPOND,         //退出回复    ENUM_MSG_TYPE_MAX = 0x00ffffff};#define LOGIN_OK "ok"#define LOGIN_FAILED "failed"//协议数据单元typedef struct PDU{    uint uiPDULen;        //消息的总的大小    uint uiMsgType;       //消息的类型    uint uiFrom;          //消息发送者的 id    uint uiTo;            //消息接收者的 id    uint uiMsgLen;        //实际消息的大小    char caMsg[4];        //实际消息(弹性数组)}PDU;PDU *makePDU(uint uiMsgLen);void sendPDU(int sockfd, PDU *pdu);PDU *recvPDU(int sockfd);

protocol.c

#include "protocol.h"//根据 协议 和 信息数据大小 创建 PDU 数据包PDU *makePDU(uint uiMsgLen){    //打包后信息数据的总大小    uint uiPDULen = sizeof(PDU)-4*sizeof(char)+uiMsgLen;    PDU *pdu = (PDU *)malloc(uiPDULen);    if (NULL == pdu)    {        printf("malloc pdu failed\n");        exit(EXIT_FAILURE);    }    memset(pdu, 0, uiPDULen);    pdu->uiPDULen = uiPDULen;    pdu->uiMsgLen = uiMsgLen;    return pdu;}//把数据信息写入到创建好的 PDU 数据包中并发送void sendPDU(int sockfd, PDU *pdu){    if (NULL == pdu)    {        return;    }    int iSended = 0;    int iLeft = pdu->uiPDULen;    int ret = -1;    //写入数据并发送    while (iLeft)    {        if (iLeft > PER_MAX_IO_BYTES)        {            ret = write(sockfd, (char *)pdu+iSended, PER_MAX_IO_BYTES);        }        else        {            ret = write(sockfd, (char *)pdu+iSended, iLeft);        }        if (-1 == ret)        {            perror("send pdu write");            break;        }        iSended += ret;        iLeft -= ret;    }}//接收 PDU 数据包PDU *recvPDU(int sockfd){    uint uiPDULen = 0;    int ret = -1;    //接收数据    ret = read(sockfd, &uiPDULen, sizeof(uint));    if (0 == ret || -1 == ret)    {        return NULL;    }    //从 PDU 中提取信息大小    uint uiMsgLen = uiPDULen-(sizeof(PDU)-4*sizeof(char));    PDU *pdu = makePDU(uiMsgLen);    //已接收的数据大小    int iRecved = sizeof(uint);    //未接收的数据大小    int iLeft = uiPDULen-sizeof(uint);    //把接收的 PDU 中的 msg 转存入到新建的 PDU 中    while (iLeft)    {        if (PER_MAX_IO_BYTES < iLeft)        {            ret = read(sockfd, (char*)pdu+iRecved, PER_MAX_IO_BYTES);        }        else        {            ret = read(sockfd, (char*)pdu+iRecved, iLeft);          }        if (-1 == ret || 0 == ret)        {            break;        }        iRecved += ret;        iLeft -= ret;    }    return pdu;}

服务器

server.h

#include "public.h"void setBaseId();int makeSocket();void makeBind(int sockfd);void makeListen(int sockfd);void acceptClient(int sockfd);

server.c

#include "server.h"#include "list.h"#include "protocol.h"#include "file.h"#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <pthread.h>List *g_pList = NULL;uint g_uiBaseId = 1000;//处理注册请求static void handleRegistRequest(int sockfd, PDU *pdu){    //产生节点用于保留客户端的注册信息并存入链表    //eg: id 和 密码    Node *node = makeNode();    node->sockfd = -1;    node->uiId = g_uiBaseId;    strncpy(node->caPwd, pdu->caMsg, PWD_LEN);    insertList(g_pList, node);    //将链表中的数据写入文件    saveUsrInfoToFile(USRINFO_PATH, g_pList);    //给客户端产生一个注册回复    PDU *respdu = makePDU(0);    respdu->uiMsgType = ENUM_MSG_TYPE_REGIST_RESPOND;    respdu->uiTo = g_uiBaseId;    g_uiBaseId++;    sendPDU(sockfd, respdu);    free(respdu);}//处理登录请求static void handleLoginRequest(int sockfd, PDU *pdu){    if (NULL == pdu)    {        return;    }    Node *node = g_pList->pFirstNode;    //从列表中查找并对比登录客户端信息    while (NULL != node)    {        if (node->uiId == pdu->uiFrom && 0 == strncmp(node->caPwd, pdu->caMsg, PWD_LEN))        {            if (-1 == node->sockfd)            {                node->sockfd = sockfd;            }            else            {                node = NULL;            }            break;        }        node = node->pNext;    }    PDU *respdu = NULL;    //发送登录成功(失败)信息到客户端    if (NULL != node)    {        respdu = makePDU(strlen(LOGIN_OK));        strncpy(respdu->caMsg, LOGIN_OK, strlen(LOGIN_OK));    }    else    {        respdu = makePDU(strlen(LOGIN_FAILED));        strncpy(respdu->caMsg, LOGIN_FAILED, strlen(LOGIN_FAILED));    }    respdu->uiMsgType = ENUM_MSG_TYPE_LOGIN_RESPOND;    sendPDU(sockfd, respdu);    free(respdu);}//处理私聊处理static void handlePrivateChatRequest(int sockfd, PDU *pdu){    if (NULL == pdu)    {        return;    }    Node *node = g_pList->pFirstNode;    //查找目标客户端并发送    while (NULL != node)    {        if (node->uiId == pdu->uiTo)        {            sendPDU(node->sockfd, pdu);            break;        }        node = node->pNext;    }}//处理群聊请求static void handleGroupChatRequest(int sockfd, PDU *pdu){    if (NULL == pdu)    {        return;    }    Node *node = g_pList->pFirstNode;    //遍历客户端列表并发送信息    while (NULL != node)    {        if (node->sockfd > 0)        {            sendPDU(node->sockfd, pdu);        }        node = node->pNext;    }}//处理退出请求static void handleExitRequest(int sockfd, PDU *pdu){    if (NULL == pdu)    {        return;    }    Node *node = findNodeById(pdu->uiFrom, g_pList);    if (NULL != node)    {        node->sockfd = -1;    }}//处理客户端的请求static void *handleClient(void *arg){    int sockfd = (int)arg;    PDU *pdu = NULL;    while (1)    {        //接收客户端的数据        pdu = recvPDU(sockfd);        if (NULL == pdu)        {            pthread_exit(NULL);        }        //判断消息类型        //根据消息类型的不同做出不同的处理        switch (pdu->uiMsgType)        {        //处理客户端的注册请求        case ENUM_MSG_TYPE_REGIST_REQUEST:            handleRegistRequest(sockfd, pdu);            break;          //处理客户端的登录请求        case ENUM_MSG_TYPE_LOGIN_REQUEST:            handleLoginRequest(sockfd, pdu);            break;          //处理客户端的私聊请求        case ENUM_MSG_TYPE_PRIVATE_CHAT_REQUEST:            handlePrivateChatRequest(sockfd, pdu);            break;          //处理客户端的群聊请求        case ENUM_MSG_TYPE_GROUP_CHAT_REQUEST:            handleGroupChatRequest(sockfd, pdu);            break;          //处理客户端的退出请求        case ENUM_MSG_TYPE_EXIT_REQUEST:            handleExitRequest(sockfd, pdu);            printf("线程退出\n");            pthread_exit(NULL);            break;          default:            break;        }        free(pdu);    }}//设置新注册用户的起始idvoid setBaseId(){    Node *node = g_pList->pFirstNode;    int sign = 0;    //若有新客户端注册则更新 g_uiBaseId,方便给新客户端分配 ID    while (NULL != node)    {        if (g_uiBaseId < node->uiId)        {            g_uiBaseId = node->uiId;            sign = 1;        }        node = node->pNext;    }    if (1 == sign)    {        g_uiBaseId++;    }}//创建 sockfd 通信int makeSocket(){    //产生socket用于监听客户端的连接    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (-1 == sockfd)    {        perror("socket");        exit(EXIT_FAILURE);    }    return sockfd;}//绑定端口void makeBind(int sockfd){    struct sockaddr_in servAddr;    servAddr.sin_family = AF_INET;    servAddr.sin_port = htons(8888);    servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");    bzero(servAddr.sin_zero, 8);    //将sockfd和特定的ip及端口绑定    //表示通过该scokfd来监听从绑定的ip连接过来的    //作用于指定端口的客户端    int ret = -1;    ret = bind(sockfd, (struct sockaddr *)&servAddr, sizeof(servAddr));    if (-1 == ret)    {        perror("bind");        exit(EXIT_FAILURE);    }}//监听 sockfd 通信void makeListen(int sockfd){    //设置该sockfd每次能够处理的最大客户端数    int ret = listen(sockfd, 10);    if (-1 == ret)    {        perror("listen");        exit(EXIT_FAILURE);    }}//接受客户端连接请求void acceptClient(int sockfd){    struct sockaddr_in clientAddr;    int clientSockfd = -1;    int iLen = sizeof(clientAddr);    pthread_t thread;    while (1)    {        printf("等待客户端的连接...\n");        //阻塞等待客户端的连接        //若有客户端连接过来,        //则会自动将客户端的相应信息存入clientAddr中        //然后往下执行        //若有客户端连接服务器成功        //则产生一个新的socket        //该新的socket用于服务器和客户端数据交换        clientSockfd = accept(sockfd, (struct sockaddr *)&clientAddr, &iLen);        if (-1 == clientSockfd)        {            perror("accept");            break;        }        //inet_ntoa:将整形表示的ip        //          转换成点分十进制表示的ip        printf("ip为%s的客户端连接到服务器\n", inet_ntoa(clientAddr.sin_addr));        printf("产生的新的用于数据交换的sockfd:%d\n", clientSockfd);        //每来一个客户端的连接        //创建一个新的线程来专门处理该客户端        pthread_create(&thread, NULL, handleClient, (void *)clientSockfd);    }}

smain.c

#include "server.h"#include "list.h"#include "file.h"extern List *g_pList;int main(void){    //产生一个链表,用于保存客户端的信息    g_pList = makeList();    //从文件中获得之前注册的用户数据    getUsrInfoFromFile(USRINFO_PATH, g_pList);    showList(g_pList);    //设置新注册用户的起始id    setBaseId();    int sockfd = makeSocket();    makeBind(sockfd);    makeListen(sockfd);    acceptClient(sockfd);    return 0;}

客户端

client.h

#include "public.h"int makeSocket();void connectToServer(int sockfd);int loginOrRegistFace();void loginOrRegist(int sockfd);void chat(int sockfd);void exitPrograms(int sockfd);

client.c

#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>  //htons#include "client.h"#include "protocol.h"uint g_uiId = 0;//处理接收的聊天信息static void handleChat(PDU *pdu){    if (NULL == pdu)    {        return;    }    printf("%u says: \n", pdu->uiFrom);    write(STDOUT_FILENO, pdu->caMsg, pdu->uiMsgLen);}//循环接收服务器的信息并处理void *handleServer(void *arg){    int sockfd = (int)arg;    PDU *pdu = NULL;    while (1)    {        //接收服务器的消息        pdu = recvPDU(sockfd);        if (NULL == pdu)        {            printf("和服务器已断开\n");            exit(0);        }        //根据消息的类型做出不同的处理        switch (pdu->uiMsgType)        {            //处理服务器返回的注册回复信息            case ENUM_MSG_TYPE_PRIVATE_CHAT_REQUEST:            case ENUM_MSG_TYPE_GROUP_CHAT_REQUEST:                handleChat(pdu);                break;            default:                break;        }        free(pdu);    }}//创建 sockfd 通信int makeSocket(){    //AF_INET:ipv4    //SOCK_STREAM:使用可靠传输-->tcp    //SOCK_DGRAM:非可靠传输-->udp    //0: 使用传输默认的协议    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (-1 == sockfd)    {        perror("socket");        exit(EXIT_FAILURE);    }    return sockfd;}//连接客户端到服务器void connectToServer(int sockfd){    struct sockaddr_in servAddr;    servAddr.sin_family = AF_INET;    //htons:表示将主机字节序转换为网络字节序    //字节序:大端字节序,小端字节序    //端口用来标识应用    servAddr.sin_port = htons(8888);    //设置要连接的服务器的ip地址    //inet_addr:将点分十进制表示的ip转换成整数表示    servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");    //将指定地址的开始往后的多少个字节置为0    bzero(servAddr.sin_zero, 8);    int ret = -1;    //连接服务器    ret = connect(sockfd, (struct sockaddr *)&servAddr, sizeof(servAddr));    if (-1 == ret)    {        perror("connect");        exit(EXIT_FAILURE);    }    printf("connect to server success\n");}//登录菜单int loginOrRegistFace(){    printf("  欢迎\n");    printf("1,登录\n");    printf("2,注册\n");    printf("0,退出\n");    printf("请输入选项:\n");    int num = 0;    scanf("%d", &num);    return num;} //客户端注册static void regist(int sockfd){    PDU *pdu = makePDU(PWD_LEN);    pdu->uiMsgType = ENUM_MSG_TYPE_REGIST_REQUEST;    printf("请输入注册需要的密码:\n");    scanf("%s", pdu->caMsg);    sendPDU(sockfd, pdu);    free(pdu);    pdu = recvPDU(sockfd);    if (ENUM_MSG_TYPE_REGIST_RESPOND == pdu->uiMsgType)    {        g_uiId = pdu->uiTo;        printf("获得注册的id: %u\n", g_uiId);    }    else    {        printf("注册失败\n");    }    free(pdu);}//客户端登录static int login(int sockfd){    int id = 0;    PDU *pdu = makePDU(PWD_LEN);    printf("请输入id:\n");    scanf("%u", &pdu->uiFrom);    id = pdu->uiFrom;    printf("请输入密码:\n");    scanf("%s", pdu->caMsg);    pdu->uiMsgType = ENUM_MSG_TYPE_LOGIN_REQUEST;    sendPDU(sockfd, pdu);    free(pdu);    pdu = recvPDU(sockfd);    if (ENUM_MSG_TYPE_LOGIN_RESPOND         && 0 == strncmp(LOGIN_OK, pdu->caMsg, pdu->uiMsgLen))    {        printf("登录成功\n");        g_uiId = id;        return 1;    }    printf("登录失败\n");    write(STDOUT_FILENO, pdu->caMsg, pdu->uiMsgLen);    putchar('\n');    return -1;}//退出客户端void exitPrograms(int sockfd){    PDU *pdu = makePDU(0);    pdu->uiFrom = g_uiId;    pdu->uiMsgType = ENUM_MSG_TYPE_EXIT_REQUEST;    sendPDU(sockfd, pdu);    free(pdu);    printf("发送退出请求\n"); }//登录菜单选项处理void loginOrRegist(int sockfd){    int num = -1;    int ret = -1;    while (1)    {        num = loginOrRegistFace();        switch (num)        {        case 1:            ret = login(sockfd);            break;        case 2:            regist(sockfd);            break;        case 0:            exitPrograms(sockfd);            exit(EXIT_SUCCESS);        default:            printf("输入有误!!!\n");            break;        }        if (1 == ret)        {            break;        }    }}//聊天菜单static int chatFace(){    printf("  ^_^\n");    printf("1,私聊\n");    printf("2,群聊\n");    printf("0,返回\n");    printf("请输入选项:\n");    int num = 0;    scanf("%d", &num);    return num;}//私聊信息的输入和发送static void privateChat(int sockfd){    printf("请输入聊天的对象:\n");    uint perid = 0;    scanf("%u", &perid);    char caMsg[PER_MAX_IO_BYTES] = {'\0'};    printf("请输入聊天信息:\n");    read(STDIN_FILENO, caMsg         , PER_MAX_IO_BYTES);    PDU *pdu = makePDU(strlen(caMsg));    pdu->uiFrom = g_uiId;    pdu->uiTo = perid;    strncpy(pdu->caMsg, caMsg, strlen(caMsg));    pdu->uiMsgType = ENUM_MSG_TYPE_PRIVATE_CHAT_REQUEST;    sendPDU(sockfd, pdu);    free(pdu);}//群聊信息的输入和发送static void groupChat(int sockfd){    char caMsg[PER_MAX_IO_BYTES] = {'\0'};    printf("请输入聊天信息:\n");    read(STDIN_FILENO, caMsg, PER_MAX_IO_BYTES);    PDU *pdu = makePDU(strlen(caMsg));    pdu->uiFrom = g_uiId;    strncpy(pdu->caMsg, caMsg, strlen(caMsg));    pdu->uiMsgType = ENUM_MSG_TYPE_GROUP_CHAT_REQUEST;    sendPDU(sockfd, pdu);    free(pdu);}//聊天菜单选项处理void chat(int sockfd){    pthread_t thread;    pthread_create(&thread, NULL, handleServer, (void *)sockfd);    int num = -1;    while (1)    {        num = chatFace();        switch (num)        {            case 1:                privateChat(sockfd);                break;            case 2:                groupChat(sockfd);                break;            case 0:                pthread_cancel(thread);                return;            default:                break;        }    }}

cmain.c

#include "client.h"int main(void){    int sockfd = makeSocket();    connectToServer(sockfd);    char sign = '\0';    while (1)    {        //login or regist        loginOrRegist(sockfd);        chat(sockfd);        printf("退出程序?Y/y\n");        getchar();        sign = getchar();        getchar();        if ('y' == sign || 'Y' == sign)        {            exitPrograms(sockfd);            break;        }    }    return 0;}
原创粉丝点击