题目:编写一个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; }