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

时间:2022-11-18 03:52:21

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