自測之Lesson15:TCP&UDP網絡編程
阿新 • • 發佈:2018-03-14
抓包 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網絡編程