自测之Lesson15:TCP&UDP网络编程

Posted GGBeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自测之Lesson15:TCP&UDP网络编程相关的知识,希望对你有一定的参考价值。

题目:编写一个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网络编程的主要内容,如果未能解决你的问题,请参考以下文章

自测之Lesson10:管道

自测之Lesson6:低级I/O库

自测之Lesson11:消息和消息队列

网络:传输层协议:TCP&&UDP

网络:传输层协议:TCP&&UDP

网络:传输层协议:TCP&&UDP