1. 将UDP的服务器客户端重新搭建
服务器端代码:
#include
/*--------------------UDP 服务器端---------------------*/
#define ERR_MSG(msg) do{\
fprintf(stderr,"_%d_",__LINE__);\
perror(msg);\
return -1;\
}while(0)
#define IP "192.168.0.105" //查看本机IP地址 ifconfig
#define PORT 7777
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket error");
}
printf("sfd = %d\n",sfd);
//2. 填充服务器的地址信息结构体 AF_INET: man 7 ip
struct sockaddr_in sin;
sin.sin_family = AF_INET; //IPv4
sin.sin_port = htons(PORT); //端口号
sin.sin_addr.s_addr = inet_addr(IP); //IP地址
//3. 将套接字和网络信息结构体绑定
if(-1 == bind(sfd,(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("bind error");
}
printf("bind success\n");
//4.接收,发送收据
char buf[128]="";
int res=0;
struct sockaddr_in cin; //用来存储客户端数据包
socklen_t addrlen = sizeof(cin);
while(1)
{
//接收收据
memset(buf,0,sizeof(buf));
res = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&cin,&addrlen); //注意没有新的文件描述符产生
if(-1 == res)
{
ERR_MSG("recv error");
}
printf("接收到来自[%s : %d] :%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
//发送数据
strcat(buf,"***");
if(-1 == sendto(sfd,buf,sizeof(buf),0,(struct sockaddr *)&cin,sizeof(cin)))
{
ERR_MSG("sendto error");
}
printf("发送信息到[%s : %d] :%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
}
//5.关闭套接字
close(sfd);
return 0;
}
客户端代码:
#include
/*--------------------UDP 客户端---------------------*/
#define ERR_MSG(msg) do{\
fprintf(stderr,"_%d_",__LINE__);\
perror(msg);\
return -1;\
}while(0)
#define IP "192.168.0.105" //服务器端 IP地址
#define PORT 7777 //服务器端 端口号
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0); //UDP协议: SOCK_DGRAM
if(sfd < 0)
{
ERR_MSG("socket error");
}
printf("sfd = %d\n",sfd);
//绑定客户端的地址信息--->非必须绑定
//若不绑定则会自动绑定本机IP以及随机端口
//2. 填充要发送的服务器的地址信息结构体,给下面的sendto函数使用
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填IPv4
sin.sin_port = htons(PORT); //服务器绑定的端口号(网络字节序)
sin.sin_addr.s_addr = inet_addr(IP); //服务器绑定的IP地址(网络字节序)
//3.接收,发送收据
char buf[128]="";
ssize_t res=0;
struct sockaddr_in rcvaddr; //存储数据包
socklen_t addrlen = sizeof(rcvaddr);
while(1)
{
//发送数据
memset(buf,0,sizeof(buf)); //缓存清0
printf("请输入>>>"); //通过终端输入
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
if(-1 == sendto(sfd,buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin)))
{
ERR_MSG("sendto error");
}
printf("发送信息到[%s : %d] :%s\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),buf);
//接收收据
memset(buf,0,sizeof(buf));
if(-1 == recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&rcvaddr,&addrlen))
// if(-1 == recvfrom(sfd,buf,sizeof(buf),0,NULL,NULL))
// if(-1 == recv(sfd,buf,sizeof(buf),0))
// if(-1 == read(sfd,buf,sizeof(buf)))
{
ERR_MSG("recv error");
}
printf("接收到来自[%s : %d] :%s\n",inet_ntoa(rcvaddr.sin_addr),ntohs(rcvaddr.sin_port),buf);
}
//4.关闭套接字
close(sfd);
return 0;
}
运行结果如下:
ubuntu@ubuntu:01udp$ ./ser.c
sfd = 3
bind success
接收到来自[192.168.0.105 : 40788] :hello
发送信息到[192.168.0.105 : 40788] :hello***
接收到来自[192.168.0.105 : 40788] :summer
发送信息到[192.168.0.105 : 40788] :summer***
ubuntu@ubuntu:01udp$ ./cli.c
sfd = 3
请输入>>>hello
发送信息到[192.168.0.105 : 7777] :hello
接收到来自[192.168.0.105 : 7777] :hello***
请输入>>>summer
发送信息到[192.168.0.105 : 7777] :summer
接收到来自[192.168.0.105 : 7777] :summer***
请输入>>>
代码如下:
#include
/*===============================基于UDP 的tftp客户端===========================*/
#define ERR_MSG(msg) do{\
fprintf(stderr,"_%d_",__LINE__);\
perror(msg);\
return -1;\
}while(0)
#define IP "192.168.0.104" //查看服务器的IP地址 windows下ipconfig
#define PORT 69 //tftp专用
int download(int sfd,struct sockaddr_in sin);
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket error");
}
//绑定客户端的地址信息-->非必须绑定
//若不绑定则会自动绑定本机IP以及随机端口
//2. 填充服务器的地址信息结构体,给下面的sendto函数使用
struct sockaddr_in sin;
sin.sin_family = AF_INET; //
sin.sin_port = htons(PORT); //端口号
sin.sin_addr.s_addr = inet_addr(IP); //IP地址
char c = 0;
while(1)
{
printf("\n----------------------------------\n");
printf(" 1.上传 \n");
printf(" 2.下载 \n");
printf(" 3.退出 \n");
printf("********************************* \n");
printf("请选择>>> ");
c = getchar();
while(getchar() != 10);
switch(c)
{
case '1': //上传
break;
case '2': //下载
download(sfd,sin);
break;
case '3': //退出
goto END;
default:
printf("输入有误,请重新输入\n");
}
}
END:
//5.关闭套接字
close(sfd);
return 0;
}
int download(int sfd,struct sockaddr_in sin)
{
char filename[30] = "";
printf("请输入文件名>>>");
scanf("%s",filename);
while(getchar() != 10);
//组下载请求协议包
//操作码 文件名 \0 模式 \0
//2Byte n Bytes String 1Byte n Bytes String 1Byte
char buf[516] = "";
unsigned short *p1 = (unsigned short*)buf;
*p1 = htons(1); //p1操作码
char *p2 = buf+2;
strcpy(p2,filename);
char *p3 =p2+strlen(filename);
*p3 = 0;
char *p4 = p3+1;
strcpy(p4,"octet");
int size = 4+strlen(p2)+strlen(p4);
//向服务器发送下载请求
if(-1 == sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("sendto error");
}
printf("发送下载请求成功\n");
unsigned short code=0; //操作码
unsigned short block=0; //块编号
char data[512]=""; //数据
int count=0;
int len=0;
int fd;
unsigned short *p = p1+1; //指向块编号
socklen_t addrlen = sizeof(sin);
while(1)
{
//接收数据包
if(-1 == (len=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen))) //需接收新的端口号(临时端口号12345)
{
ERR_MSG("recv error");
}
//解析接收到的数据包
code = ntohs(*p1); //解析操作码
block = ntohs(*p); //解析块编号
strcpy(data,buf+4); //解析数据
if(code == 3 && block==count+1) //下载数据校验
{
count++;
if(count==1)
{
fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
if(-1 == fd)
{
ERR_MSG("file open error");
}
}
if(-1 == write(fd,data,len-4))
{
ERR_MSG("write error");
}
//回复ACK
*p1 = htons(4); //ACK操作码
*p = htons(block); //ACK块编号
if(-1 == sendto(sfd,buf,4,0,(struct sockaddr*)&sin,addrlen))
{
ERR_MSG("sendto error");
}
//判断数据是否小于512,若<512,则传输结束
if(len-4 < 512)
{
close(fd);
printf("已完成下载\n");
break;
}
}
}
}
运行结果如下:
ubuntu@ubuntu:day3$ gcc 05udp_tftp.c
ubuntu@ubuntu:day3$ ./a.out
----------------------------------
1.上传
2.下载
3.退出
*********************************
请选择>>> 2
请输入文件名>>>1_udpSer.c
发送下载请求成功
已完成下载
----------------------------------
1.上传
2.下载
3.退出
*********************************
请选择>>> 3
ubuntu@ubuntu:day3$