1. 程式人生 > >自測之Lesson15:TCP&UDP網絡編程

自測之Lesson15:TCP&UDP網絡編程

抓包 res file 字節 add [] net 標準輸入 iad

題目:編寫一個TCP通信的程序。

實現代碼:

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

#define PORT 0xaaaa

// 服務端
void startServe()
{
        int iRet;

        // socket()
        int fd;                                         // 文件描述符
        fd = socket(PF_INET, SOCK_STREAM, 0);           // 創建文件描述符,並確定是用TCP還是UDP等
        if (fd < 0) {
                perror("fail socket");
                return;
        }
    
        // bind()
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(PORT);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);       // 即使目標地址不是我,只要發到該計算機上,我就能接收

        iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); // 將文件描述符與本機地址綁定
        if (iRet < 0) {
                perror("fail bind");
                close(fd);                              // 釋放資源
                return;
        }    
    
        // listen()
        iRet = listen(fd, 5);                   // 最多監聽5個連接請求
        if (iRet < 0) {
                perror("fail listen");
                close(fd);
                return;
        }
        printf("Server start OK, wait connect...\n");

        // accept()
        char szBuf[1024];
        char szMsg[] = "Welcome...";
        struct sockaddr_in clientAddr;                  // 客戶端地址
        socklen_t addrlen = sizeof(clientAddr);
        while(1) {
                int newFd;                              // 此newFd用於與客戶端通信
                newFd = accept(fd, (struct sockaddr*)&clientAddr, &addrlen);
                if (newFd < 0) {
                        perror("fail accept");
                        break;
                }

                char *pClientAddr = inet_ntoa(clientAddr.sin_addr);     // 整數IP轉字符串IP
                int clientPort = ntohs(clientAddr.sin_port);            // 網絡字節序轉主機字節序
                printf("Connect from %s:%d\n", pClientAddr, clientPort);
                memset(szBuf, 0, 1024);
                iRet = read(newFd, szBuf, 1024);
                if (iRet < 0) {
                        perror("fail read");
                        break;
                }
                printf("Recv:%s\n", szBuf);
                write(newFd, szMsg, strlen(szMsg));
                close(newFd);           // 關閉當前accept創建的文件描述符
        }
        close(fd);
        return;
}

// 客戶端
void startClient()
{
        int iRet;

        // socket()
        int fd;
        fd = socket(PF_INET, SOCK_STREAM, 0);
        if(fd < 0) {
                perror("fail socket");
                return;
        }

        // connect()
        struct sockaddr_in srvAddr;
        srvAddr.sin_family = AF_INET;
        srvAddr.sin_addr.s_addr = inet_addr("192.168.85.128");          // 服務端的ip地址
        srvAddr.sin_port = htons(PORT);                                 // 服務端的端口號
        iRet = connect(fd, (struct sockaddr*)&srvAddr, sizeof(srvAddr));
        if (iRet != 0) {
                perror("fail connect");
                return;
        }
        printf("Connect success\n");
        fprintf(stderr, "Send:");

        // read() & write()
        char szBuf[1024];
        memset(szBuf, 0, 1024);
        read(STDIN_FILENO, szBuf, 1024);                // 從標準輸入 輸入消息
        write(fd, szBuf, strlen(szBuf));
        char szRcv[1024];

        memset(szRcv, 0, 1024);
        read(fd, szRcv, 1024);
        printf("[CLIENT]Rcv:%s\n", szRcv);

        close(fd);
        return;
}

int main(int argc, char **argv)
{
        if (argc != 2 ||
            (strcmp(argv[1], "s") && strcmp(argv[1], "c"))) {
                printf("Usage: %s [ s | c ]\n", argv[0]);
                printf("\ts: start server\n");
                printf("\tc: start client\n");
                return 0;
        }
        if (argv[1][0] == ‘s‘) {
                startServe();
        }
        else if (argv[1][0] == ‘c‘) {
                startClient();
        }
        return 0;
}

/* ReadMe */
/*
 * 先啟動服務端 --> ./a.out s
 * 再啟動客戶端 --> ./a.out c
 */

題目:編寫一個UDP通信的程序。

  

實現代碼:

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

#define SRV_PORT 0xaaaa                 // 服務端 端口號
#define CLI_PORT 0xbbbb                 // 客戶端 端口號
#define IP_ADDRESS "10.162.73.120"

void startServer()
{
        int iRet;
    
        // socket()
        int fd; 
        fd = socket(PF_INET, SOCK_DGRAM, 0); 
        if (fd < 0) {
                perror("fail socket");
                return;
        }
    
        // bind()
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.sin_port = htons(SRV_PORT);
        iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
        if (iRet != 0) {
                perror("fail bind");
                return;
        }

        // recvfrom() & sendto()
        struct sockaddr_in cliAddr;
        socklen_t addrLen = sizeof(cliAddr);
        char szRcv[1024];
        char szSnd[1024];
        while(1) {
                // recvfrom()
                memset(szRcv, 0, 1024);
                iRet = recvfrom(fd, szRcv, 1024, 0, (struct sockaddr*)&cliAddr, &addrLen);
                if (iRet < 0) {
                        perror("fail recvfrom");
                        close(fd);
                        break;
                }
                char *pcliAddr = inet_ntoa(cliAddr.sin_addr);
                int cliPort = ntohs(cliAddr.sin_port);
                printf("Recv from client[%s:%d]\n", pcliAddr, cliPort);
                printf("Recv:%s\n", szRcv);

                // sendto()
                fprintf(stderr, "Send:");
                memset(szSnd, 0, 1024);
                read(STDIN_FILENO, szSnd, 1024);
                iRet = sendto(fd, szSnd, strlen(szSnd), 0, (struct sockaddr*)&cliAddr, addrLen);

        }
        close(fd);
}

void startClient()
{
        int iRet;

        // socket()
        int fd;
        fd = socket(PF_INET, SOCK_DGRAM, 0);
        if (fd < 0) {
                perror("fail socket");
                return;
        }

        // bind()
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.sin_port = htons(CLI_PORT);
        iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
        if (iRet != 0) {
                perror("fail bind");
                return;
        }

        // recvfrom() & sendto()
        struct sockaddr_in srvAddr;
        socklen_t addrLen = sizeof(srvAddr);
        // 對端的地址信息,用於sendto()函數
        srvAddr.sin_family = AF_INET;
        srvAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
        srvAddr.sin_port = htons(SRV_PORT);
        char szRcv[1024];
        char szSnd[1024];
        while(1) {
                // sendto()
                fprintf(stderr, "Send:");
                memset(szSnd, 0, 1024);
                read(STDIN_FILENO, szSnd, 1024);
                sendto(fd, szSnd, strlen(szSnd), 0, (struct sockaddr*)&srvAddr, addrLen);

                // read()
                memset(szRcv, 0, 1024);
                read(fd, szRcv, 1024);          // 上面的sendto()已經獲得對端地址,此處可簡寫
                printf("Recv:%s\n", szRcv);
        }
        close(fd);
}

int main(int argc, char **argv)
{
        if (argc != 2 ||
                (strcmp(argv[1], "c") && strcmp(argv[1], "s")))
        {
                printf("Usage:%s [ s | c ]\n", argv[0]);
                printf("\ts: start to server\n");
                printf("\tc: start to client\n");
                return 0;
        }
        if (argv[1][0] == ‘s‘) {
                startServer();
        }
        else if (argv[1][0] == ‘c‘) {
                startClient();
        }
        return 0;
}

  

題目:編寫一個抓包程序,要求抓取封裝TCP報文段的包,並打印出包的頭部信息。

實現代碼:

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

#define PORT 0Xaaaa                     // 此端口僅用於bind(),而該程序可抓發送到任意端口的包

typedef struct _ipHeader {
//      unsigned char ucVer:4;
//      unsigned char ucHeadLen:4;
        unsigned char ucVerHeadLen;             // 不應該在此處用位域,可在後面提取位數
        unsigned char ucTos;
        unsigned short usLen;
        unsigned short usIdent;
//      unsigned short usFlag:3;
//      unsigned short usOffset:13;
        unsigned short usFlagOffset;
        unsigned char ucTTL;
        unsigned char ucProtocol;
        unsigned short usChkSum;
//      unsigned int uiSrcIp;
//      unsigned int uiDestIp;
        struct in_addr SrcIp;
        struct in_addr DestIp;
        char data[0];
} IP_HEADER;


typedef struct _tcpHeader {
        unsigned short SrcPort;
        unsigned short DestPort;
        unsigned int Seq;
        unsigned int Ack;
//      unsigned short HeadLen:4;
//      unsigned short Save:6;
//      unsigned short URG:1;
//      unsigned short ACK:1;
//      unsigned short PSH:1;
//      unsigned short RST:1;
//      unsigned short SYN:1;
//      unsigned short FIN:1;
        unsigned short HeadLenFlag;             // 包括首部長度、保留、URG標誌等字段
        unsigned short Window;
        unsigned short ChkSum;
        unsigned short UrgPoint;
        char data[0];
} TCP_HEADER;


void printIpHeader(char szBuf[])
{
        IP_HEADER *pHeader = (IP_HEADER*)szBuf;
        printf("\n================IP HEADER================\n");
        printf("\tVersion:%d\n", (pHeader->ucVerHeadLen) >> 4);
        printf("\tHeadLen:%d\n", (pHeader->ucVerHeadLen) & 0x0f);
        printf("\tSOUR IP:%s\n", inet_ntoa(pHeader->SrcIp));
        printf("\tDEST IP:%s\n", inet_ntoa(pHeader->DestIp));
        printf("=========================================\n");
}

void printTcpHeader(char szBuf[])
{
        TCP_HEADER *pHeader = (TCP_HEADER*)szBuf;
        printf("\n===============TCP HEADER================\n");
        printf("\tSOUR PORT:%d\n", ntohs(pHeader->SrcPort));
        printf("\tDEST PORT:%d\n", ntohs(pHeader->DestPort));
        printf("=========================================\n");
}

void startCapturePacket()
{
        int iRet;

        int fd;
        fd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP/* = NUM‘6‘ */);      // 抓取封裝TCP報文段的IP數據報
        if (fd < 0) {
                perror("fail socket");
                return;
        }

        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(PORT);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);

        iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
        if (iRet < 0) {
                perror("fail bind");
                close(fd);
                return;
        }

        char szBuf[1024];
        while(1) {
                memset(szBuf, 0, 1024);
                read(fd, szBuf, 1024);                  // 將抓到的包整個存到szBuf中,此處szBuf的大小不是很合適
                printIpHeader(szBuf);                   // 打印IP頭部部分信息
                printTcpHeader(szBuf);                  // 打印TCP報文端頭部部分信息
        }
        close(fd);
        return;
}



int main()
{
        startCapturePacket();
        return 0;
}

  

自測之Lesson15:TCP&UDP網絡編程