基于TCP的机械臂角度控制及UDP的TFTP文件传输实现
TCP客户端控制机械臂角度
以下代码实现一个TCP客户端程序,通过键盘按键控制红色和蓝色机械臂的角度。按键规则为:w/W增大红色臂角度(上限90°),s/S减小红色臂角度(下限-90°),d/D增大蓝色臂角度(上限180°),a/A减小蓝色臂角度(下限0°)。程序首先建立与服务器的连接,然后发送初始角度值,循环读取键盘输入并调整角度后发送给服务器。
#include <myhead.h>
#define SERVER_IP "192.168.126.12"
#define SERVER_PORT 8888
#define CLIENT_IP "192.168.244.140"
#define CLIENT_PORT 9999
int main(int argc, const char *argv[]) {
// 创建套接字
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd == -1) {
perror("socket创建失败");
return -1;
}
printf("套接字fd = %d\n", sock_fd);
// 绑定客户端地址
struct sockaddr_in client_addr;
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(CLIENT_PORT);
client_addr.sin_addr.s_addr = inet_addr(CLIENT_IP);
if (bind(sock_fd, (struct sockaddr*)&client_addr, sizeof(client_addr)) == -1) {
perror("绑定失败");
return -1;
}
printf("绑定成功\n");
// 连接服务器
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
if (connect(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("连接失败");
return -1;
}
printf("连接成功\n");
// 初始化角度数据包: [0xff, 0x02, 0x00, angle, 0xff]
char red_arm[5] = {0xff, 0x02, 0x00, 0x00, 0xff};
unsigned char blue_arm[5] = {0xff, 0x02, 0x01, 0x00, 0xff};
// 发送初始角度(0°)
send(sock_fd, red_arm, sizeof(red_arm), 0);
sleep(1); // 防止粘包
send(sock_fd, blue_arm, sizeof(blue_arm), 0);
char key_input = 0;
while (1) {
system("stty -icanon");
key_input = getchar();
switch (key_input) {
case 'w':
case 'W':
red_arm[3] += 1;
if (red_arm[3] > 90) red_arm[3] = 90;
send(sock_fd, red_arm, sizeof(red_arm), 0);
break;
case 's':
case 'S':
red_arm[3] -= 1;
if (red_arm[3] < -90) red_arm[3] = -90;
send(sock_fd, red_arm, sizeof(red_arm), 0);
break;
case 'd':
case 'D':
blue_arm[3] += 1;
if (blue_arm[3] > 180) blue_arm[3] = 180;
send(sock_fd, blue_arm, sizeof(blue_arm), 0);
break;
case 'a':
case 'A':
blue_arm[3] -= 1;
if (blue_arm[3] < 0) blue_arm[3] = 0;
send(sock_fd, blue_arm, sizeof(blue_arm), 0);
break;
}
}
close(sock_fd);
return 0;
}
基于UDP的TFTP文件传输
此程序实现了经典的TFTP文件传输协议(基于UDP),提供文件下载和上传功能。用户通过简单菜单选择操作,输入文件名后程序自动与指定TFTP服务器(端口69)通信。代码包含错误处理、数据包编号校验以及ACK应答机制,确保传输可靠性。
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>
#define LOG_ERROR(msg) do {\
perror(msg);\
printf("出错位置: %d行 %s %s\n", __LINE__, __func__, __FILE__);\
} while(0)
#define TFTP_PORT 69
#define PKT_SIZE 516
int handle_download(int udp_fd, struct sockaddr_in srv_addr);
int handle_upload(int udp_fd, struct sockaddr_in srv_addr);
int main(int argc, const char *argv[]) {
if (argc < 2) {
printf("用法: %s <服务器IP>\n", argv[0]);
return -1;
}
int udp_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_fd < 0) {
perror("socket创建失败");
return -1;
}
struct sockaddr_in server_info;
server_info.sin_family = AF_INET;
server_info.sin_port = htons(TFTP_PORT);
server_info.sin_addr.s_addr = inet_addr(argv[1]);
char option;
while (1) {
system("clear");
printf("===================\n");
printf(" 1. 下载文件\n");
printf(" 2. 上传文件\n");
printf(" 3. 退出\n");
printf("===================\n");
scanf("%c", &option);
while(getchar() != '\n');
switch (option) {
case '1':
handle_download(udp_fd, server_info);
break;
case '2':
handle_upload(udp_fd, server_info);
break;
case '3':
goto exit_program;
default:
printf("无效选项\n");
}
printf("按回车继续...");
while(getchar() != '\n');
}
exit_program:
close(udp_fd);
return 0;
}
int handle_upload(int udp_fd, struct sockaddr_in srv_addr) {
char fname[20] = {0};
printf("输入上传文件名: ");
fgets(fname, 20, stdin);
fname[strlen(fname)-1] = '\0';
int file_fd = open(fname, O_RDONLY);
if (file_fd < 0) {
if (errno == ENOENT) {
printf("文件不存在\n");
return -2;
}
LOG_ERROR("打开文件失败");
return -1;
}
char packet[PKT_SIZE] = {0};
int pkt_len = sprintf(packet, "%c%c%s%c%s%c", 0, 2, fname, 0, "octet", 0);
if (sendto(udp_fd, packet, pkt_len, 0, (struct sockaddr*)&srv_addr, sizeof(srv_addr)) < 0) {
LOG_ERROR("发送WRQ失败");
return -1;
}
unsigned short block_num = 0;
int recv_len;
socklen_t addr_len = sizeof(srv_addr);
while (1) {
bzero(packet, PKT_SIZE);
recv_len = recvfrom(udp_fd, packet, PKT_SIZE, 0, (struct sockaddr*)&srv_addr, &addr_len);
if (recv_len < 0) {
LOG_ERROR("接收失败");
return -1;
}
if (packet[1] == 4) { // ACK包
if (block_num == ntohs(*(unsigned short*)(packet+2))) {
packet[1] = 3; // 改为DATA包
block_num++;
*(unsigned short*)(packet+2) = htons(block_num);
int data_size = read(file_fd, packet+4, PKT_SIZE-4);
if (data_size < 0) {
LOG_ERROR("读文件失败");
return -1;
} else if (data_size == 0) {
printf("上传完成\n");
break;
}
if (sendto(udp_fd, packet, data_size+4, 0, (struct sockaddr*)&srv_addr, sizeof(srv_addr)) < 0) {
LOG_ERROR("发送DATA失败");
return -1;
}
} else {
printf("网络异常,上传失败\n");
break;
}
} else if (packet[1] == 5) {
printf("错误: %s\n", packet+4);
break;
}
}
close(file_fd);
return 0;
}
int handle_download(int udp_fd, struct sockaddr_in srv_addr) {
char fname[20] = {0};
printf("输入下载文件名: ");
fgets(fname, 20, stdin);
fname[strlen(fname)-1] = '\0';
char packet[PKT_SIZE] = {0};
int pkt_len = sprintf(packet, "%c%c%s%c%s%c", 0, 1, fname, 0, "octet", 0);
if (sendto(udp_fd, packet, pkt_len, 0, (struct sockaddr*)&srv_addr, sizeof(srv_addr)) < 0) {
LOG_ERROR("发送RRQ失败");
return -1;
}
int file_created = 0;
int file_fd;
unsigned short expected_block = 1;
socklen_t addr_len = sizeof(srv_addr);
while (1) {
bzero(packet, PKT_SIZE);
int recv_len = recvfrom(udp_fd, packet, PKT_SIZE, 0, (struct sockaddr*)&srv_addr, &addr_len);
if (recv_len < 0) {
LOG_ERROR("接收失败");
return -1;
}
if (packet[1] == 3) { // DATA包
if (!file_created) {
file_fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0664);
if (file_fd < 0) {
LOG_ERROR("创建文件失败");
return -1;
}
file_created = 1;
}
if (htons(expected_block) == *(unsigned short*)(packet+2)) {
if (write(file_fd, packet+4, recv_len-4) < 0) {
LOG_ERROR("写文件失败");
break;
}
packet[1] = 4; // 改为ACK包
sendto(udp_fd, packet, 4, 0, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
if (recv_len < PKT_SIZE) {
printf("下载完成\n");
break;
}
expected_block++;
}
} else if (packet[1] == 5) {
printf("错误: %s\n", packet+4);
break;
}
}
if (file_created) close(file_fd);
return 0;
}