【20230421华清远见+网络编程】

本文展示了如何使用C语言实现UDP服务器和客户端的通信,包括套接字创建、绑定、发送和接收数据。同时,给出了TFTP服务器进行文件下载的示例,涉及数据包的组装与ACK确认。
摘要由CSDN通过智能技术生成

UDP服务器客户端脱离笔记重新搭建

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

#define ERR_MSG(msg) do{\
    fprintf(stderr, "line:%d ", __LINE__);\
    perror(msg);\
}while(0)
                                                                                                                                                  
#define SER_PORT 6666               //1024~49151
#define SER_IP  "192.168.31.45"    //IP地址,本机IP ifconfig

#define CLI_PORT 9999
#define CLI_IP  "192.168.31.169"

int main(int argc, const char *argv[])
{
    //创建报式套接字
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("socket create success  sfd=%d __%d__\n", sfd, __LINE__);

    
    
    //绑定--->非必须绑定
    //如果不绑定,操作系统会自动绑定本机IP,从49152~65535范围内随机一个未被使用的端口号
    //填充客户端自身的地址信息结构体, AF_INET : man 7 ip
    struct sockaddr_in cin;
    cin.sin_family      = AF_INET;
    cin.sin_port        = htons(CLI_PORT);  //客户端自身的端口号,需要从1024~49151中选择没有被使用过的
    cin.sin_addr.s_addr = inet_addr(CLI_IP);    //客户端的本机IP

    if(bind(sfd, (struct sockaddr*)&cin, sizeof(cin)) < 0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("bind success __%d__\n", __LINE__);
    ///

    //填充服务器的地址信息结构体
    //供给下面的sendto使用,
    struct sockaddr_in sin;
    sin.sin_family      = AF_INET;  //必须填AF_INET;
    sin.sin_port        = htons(SER_PORT);  //服务器绑定的端口,网络字节序
    sin.sin_addr.s_addr = inet_addr(SER_IP);    //服务器绑定的IP,本机IP ifconfig

    char buf[128] = "";
    ssize_t res = 0;

    struct sockaddr_in rcvAddr;     //存储获取到的数据包是从谁那里来的
    socklen_t addrlen = sizeof(rcvAddr);

    while(1)
    {
        bzero(buf, sizeof(buf));
        //发送
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf)-1] = 0;

        //发送给服务器,所以上面的代码,需要填充好服务器的地址信息结构
        //想要发送给谁,就填谁的地址信息结构体
        if(sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
        {
            ERR_MSG("sendto");
            return -1;
        }
        printf("发送成功\n");

        //接收数据
        //res = recvfrom(sfd, buf, sizeof(buf), 0, NULL, NULL);
        //res = recv(sfd, buf, sizeof(buf), 0);
        res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&rcvAddr, &addrlen);
        if(res < 0)
        {
            ERR_MSG("recvfrom");
            return -1;
        }
        printf("[%s : %d] : %s\n", \
                inet_ntoa(rcvAddr.sin_addr), ntohs(rcvAddr.sin_port), buf);


    }
    //关闭所有文件描述符
    close(sfd);
    return 0;
}
                                                                                                                                             

tftp服务器下载

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>


#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d ",__LINE__);\
	perror(msg);\
}while(0)

#define PORT 69//端口号1024~49151
#define IP "192.168.0.133"        //win10 IP

int main(int argc, const char *argv[])
{
	//创建套接字
	int cfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(cfd < 0){
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success cfd = %d__%d__\n",cfd,__LINE__);

	//填充服务器
	struct sockaddr_in sin;
	sin.sin_family 			= AF_INET;
	sin.sin_port    		= htons(PORT);
	sin.sin_addr.s_addr 	= inet_addr(IP);//win10服务器

	//发送下载请求
	char buf[516] = "";    //文件大小 512
	bzero(buf,sizeof(buf));

	//功能:将数据打印到指定的内存空间中,常用于组包
	int size = sprintf(buf, "%c%c%s%c%s%c", 0x00, 0x01, argv[1], 0, "octet", 0);

	//sendto 将数据发送给服务器,所以需要填充服务器的地址信息结构体
	if(sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0){
		ERR_MSG("sendto");
		return -1;
	}
	printf("sendto success\n");

	//保存服务器端的新的端口号
	struct sockaddr_in win;           //服务器IP新的端口号
	socklen_t addrlen = sizeof(win);
	ssize_t res = 0;
	char *num = buf+4;       //新指针用于判断数据包
	int fd;//打开的文件描述符
	fd = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, 0666);
	unsigned int n, m, b, h = 0;


	while(1){
		bzero(buf,sizeof(buf));
		//接收服务器端的数据包,IP和端口号
		res = recvfrom(cfd, buf, sizeof(buf), 0,(struct sockaddr *)&win, &addrlen);
		if(res < 0){
			perror("recvfrom");
			return -1;
		}
		//判断接收到的是数据包吗?
		//是
		//提取数据包中的数据(buf+4),另存到文件中
		//给服务器回复ACK--->  组ack包,快编号与接收到的数据包快遍号一致
		if(buf[1] == 0x03){
			//m = strlen(num)+2;//数据包大小
			m = strlen(num);//数据包大小
			n = write(fd, num, m);//写入文件
			char ack[5] = "";    //反馈服务器包ack
			int n_ack = sprintf(ack,"%c%c%c%c", 0x00, 0x04, buf[2], buf[3]);//ack包组成
			if(sendto(cfd, ack, n_ack, 0,(struct sockaddr *)&win, sizeof(win)) < 0){
				ERR_MSG("sendto win10");
				return -1;
			}
			bzero(buf,sizeof(buf));
		}
		//判接收到的是错误包,打印错误码+错误信息,退出
		else if(buf[1] == 0x05){     //出错 或者 文件小于512
			printf("出错码是:%c%c\t,错误信息是:%s\n", buf[2], buf[3], num);
			break;
		}else if(n < 512){
			write(fd,buf,strlen(num));
			printf("总写入字节数是:%ld\n", res);
			break;
		}
	}
	//关闭套接字
	close(cfd);
	return 0;
}

tftp服务器上传

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值