SOCKET-UDP编程,win和Linx传输文件

Cyshall 2017-12-12 04:01:39
问题:使用UDP写了个windows和Linux互传文件的小程序(使用TCP没有问题),Linux接受完文件后大小为0,实在找不到问题出在哪,还请各位大佬能帮我看下哪里出问题了,如有任何回答感激不尽!
-----------------------------------------------------------以下是代码------------------------------------------------------------------------
server.c

#include <stdio.h>
#include <stdlib.h>
#include "pub.h"

int main(int arg, char *args[])
{
if (arg < 2)
{
printf("格式;程序名 <端口>\n");
return EXIT_FAILURE;
}

int port = atoi(args[1]); //把端口号从字符型转换成int型
if (port == 0)
{
printf("端口无效!\n");
EXIT_FAILURE;
}

printf("开始接收数据...\n");
if (recv_work(port)) //服务器监听在指定端口
printf("接收数据成功!\n");
else
printf("接收数据失败!\n");
return EXIT_SUCCESS;
}


client.c

#include <stdio.h>
#include <stdlib.h>
#include "pub.h"

int main(int arg, char *args[])
{
if (arg < 4) //如果启动程序参数少于三个就退出程序并提示格式
{
printf("格式:程序名 <IP地址> <端口> <文件名>\n");
return EXIT_FAILURE;
}

int port = atoi(args[2]); //把端口转换成int型
if (port == 0)
{
printf("端口无效!\n");
EXIT_FAILURE;
}

printf("文件开始发送...\n");
//sand_work第一个参数是IP地址,第二个参数是端口号,第三个参数是文件名.返回值非0表示成功,0表示失败
if (send_work(args[1], port, args[3]))
{
printf("%s发送成功!\n", args[3]);
}
else
{
printf("%s发送失败!\n", args[3]);
}

return EXIT_SUCCESS;
}


主要代码:pub.c

#ifdef WIN
#include <WinSock2.h> //windows系统下头文件
#else
//Linux系统下头文件
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#define SOCKET int
#endif // WIN

#include "pub.h"
#include <stdio.h>

#define BUFSIZE 262144 //256K

//解析文件名
void getfilename(const char *filename, char *name)
{
int len = strlen(filename);
int i = 0;

//从后向前遍历
for (i = (len - 1); i > 0; --i)
{
if (filename[i] == '\\' || filename[i] == '/') //windows是用"\"分隔,Linux使用"/"分隔
{
break;
}
}
strcpy(name, &filename[i + 1]);
return;
}

int init_socket() //初始化socket
{
#ifdef WIN //Windows系统下需要初始化socket

WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);

if ((err = WSAStartup(wVersionRequested, &wsaData)) != 0)
{
return -1; //返回-1代表初始化socket 失败
}

if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
//版本不对
WSACleanup(); //释放相关资源
return -1; //返回-1初始化socket失败
}

#endif // WIN

return 0; //如果是在Linux下,直接返回0,什么也不干
}

//接收文件socket
SOCKET create_recv_socket(int port)
{
SOCKET st;
if ((st = socket(AF_INET, SOCK_DGRAM, 0)) == -1) //建立UDP连接
{
printf("创建socket失败%s!\n", strerror(errno));
return 0;
}

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(st, (struct sockaddr*)&addr, sizeof(addr)) == -1)
{
printf("绑定失败%s!\n", strerror(errno));
return 0;
}
return st;
}

//发送文件socket
SOCKET create_send_socket()
{
if (init_socket() == -1)
{
printf("初始化socket失败!\n");
}

SOCKET st;
if ((st = socket(AF_INET, SOCK_DGRAM, 0)) == -1) //建立UDP连接
{
printf("创建socket失败%s!\n", strerror(errno));
return 0;
}
return st; //创建成功返回socket
}

//第一个参数是IP地址,第二个参数端口号,第三个参数文件名
int send_work(const char *ipaddr, int port, const char *filename)
{
SOCKET st_send = create_send_socket();
SOCKET st_recv = create_recv_socket(port + 1);
if (st_send == 0) //连接失败
{
return 0; //连接失败返回0
}

if (st_recv == 0) //连接失败
{
return 0; //连接失败返回0
}

FILE *fd = fopen(filename, "rb"); //使用二进制只读的方式打开要传输的文件
if (fd == NULL)
{
printf("%s打开失败%s!\n", filename, strerror(errno));
return 0;
}

char *buf = malloc(BUFSIZE); //申请一个缓冲区用来存放要接收的文件数据
memset(buf, 0, BUFSIZE);
getfilename(filename, buf); //从完整的名字中解析出文件名称
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ipaddr);

size_t rc = sendto(st_send, buf, strlen(buf), 0, (struct sockaddr*)&addr, sizeof(addr)); //向服务端发送文件名
if (rc <= 0)
{
printf("发送失败%s!\n", strerror(errno));
}
else
{
struct sockaddr_in client_addr;
#ifdef WIN
int len = 0;
#else
unsigned int len = 0;
#endif // WIN

len = sizeof(client_addr);
memset(&client_addr, 0, sizeof(client_addr));
memset(buf, 0, BUFSIZE);

if (recvfrom(st_recv, buf, BUFSIZE, 0, (struct sockaddr*)&client_addr, &len) == -1) //接受来自server端的回复
{
printf("接受失败%s!\n", strerror(errno));
}
else
{
if (strncmp(buf, "ok", 2) == 0) //接收到服务回复"ok",代表服务端同意可以发送数据了
{
while (1)
{
memset(buf, 0, BUFSIZE);
rc = fread(buf, 1, BUFSIZE, fd);
if (rc <= 0)
{
if (rc < 0)
{
printf("读取文件失败%s!\n", strerror(errno));
break;
}
else
{
printf("已没有文件可读!\n");
}
}
rc = sendto(st_send, buf, rc, 0, (struct sockaddr*)&addr, sizeof(addr)); //把读取的文件通过socket发送给server
if (rc <= 0)
{
printf("发送文件失败%s!\n", strerror(errno));
break;
}
}
}
memset(buf, 0, BUFSIZE);
rc = sendto(st_send, buf, 128, 0, (struct sockaddr*)&addr, sizeof(addr)); //连续发送128个0代表发送文件结束
}
}
fclose(fd);
free(buf);

#ifdef WIN //windows下收尾工作
closesocket(st_send);
closesocket(st_recv);
WSACleanup();
#else //Linux下收尾工作
close(st_send);
close(st_recv);
#endif // WIN
return 1;
}
//服务端在指定端口监听
int recv_work(int port)
{
SOCKET st_send = create_send_socket();
SOCKET st_recv = create_recv_socket(port);
if (st_send == 0) //连接失败
{
return 0; //连接失败返回0
}

if (st_recv == 0) //连接失败
{
return 0; //连接失败返回0
}

#ifdef WIN
int len = 0;
#else
unsigned int len = 0;
#endif // WIN

char *buf = malloc(BUFSIZE); //申请空间存储接收的数据
FILE *fd = NULL;
struct sockaddr_in client_addr;
len = sizeof(client_addr);
memset(&client_addr, 0, sizeof(client_addr));
memset(buf, 0, BUFSIZE);
size_t rc = recvfrom(st_recv, buf, BUFSIZE, 0, (struct sockaddr*)&client_addr, &len); //接收文件,第一次接收的是文件名称
if (rc == -1)
{
printf("接收数据失败%s!\n", strerror(errno));
return 0;
}
else
{
printf("接收文件名:%s\n", buf);
fd = fopen(buf, "wb"); //使用二进制只写的方式打开文件
if (fd == NULL)
{
printf("打开文件失败%s!\n", strerror(errno));
}
else
{
client_addr.sin_port = htons(port + 1); //客户端接收数据端口号
memset(buf, 0, sizeof(BUFSIZE));
strcpy(buf, "ok");
rc = sendto(st_send, buf, sizeof(buf), 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); //回复客户端,同意接收文件
if (rc == -1)
{
printf("发送数据失败%s!\n", strerror(errno));
}

while (1)
{
memset(buf, 0, BUFSIZE);
rc = recvfrom(st_recv, buf, BUFSIZE, 0, (struct sockaddr*)&client_addr, &len); //循环接收来自client的数据

char tmp[128];
memset(tmp, 0, sizeof(tmp));
if (memcmp(buf, tmp, sizeof(tmp)) == 0) //比较接收的数据,如果是128个0则代表接收数据完毕
break;

if (rc == -1) //如果client连接断开表示传输完毕,或者网络意外中断,break退出循环
{
printf("接收数据失败%s!\n", strerror(errno));
break;
}
else
{
fwrite(buf, 1, rc, fd); //从client接收到多少字节的数据就写多少字节的数据
}
}
}
}
if (fd)
fclose(fd);
free(buf);
#ifdef WIN //win系统下收尾工作
closesocket(st_send);
closesocket(st_recv);
WSACleanup();
#else //Linux系统下收尾工作
close(st_send);
close(st_recv);
#endif
return 1;
}
...全文
207 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2017-12-14
  • 打赏
  • 举报
回复
引用 6 楼 Cyshall 的回复:
[quote=引用 5楼老马何以识途 的回复:]首先,你設了這麽多調試信息,具體出錯在哪裏應該清楚了吧?或者是發送、接收過程都沒問題,僅僅是文件沒内容? 其次再來考慮數據丟失問題,UDP是不可靠協議,需要自己做好確認、重傳,所以,你這麽在直連環境下測試問題不大,在實際網絡上就需要增加代碼了。
对,就是文件没内容,只有个名字的空文件。[/quote] 空文件需要专门处理。我觉得。
Cyshall 2017-12-12
  • 打赏
  • 举报
回复
引用 4 楼 zhao4zhong1 的回复:
代码功能归根结底不是别人帮自己看或讲解或注释出来的;而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。 提醒:再牛×的老师也无法代替学生自己领悟和上厕所! 单步调试和设断点调试(VS IDE中编译连接通过以后,按F10或F11键单步执行,按Shift+F11退出当前函数;在某行按F9设断点后按F5执行停在该断点处。)是程序员必须掌握的技能之一。
VS我知道怎么调试,但是使用VS调试Linux代码我折腾了两天还是没弄好,网上的教程基本都看了,都没用.
Cyshall 2017-12-12
  • 打赏
  • 举报
回复
引用 5楼老马何以识途 的回复:
首先,你設了這麽多調試信息,具體出錯在哪裏應該清楚了吧?或者是發送、接收過程都沒問題,僅僅是文件沒内容? 其次再來考慮數據丟失問題,UDP是不可靠協議,需要自己做好確認、重傳,所以,你這麽在直連環境下測試問題不大,在實際網絡上就需要增加代碼了。
对,就是文件没内容,只有个名字的空文件。
老马何以识途 2017-12-12
  • 打赏
  • 举报
回复
首先,你設了這麽多調試信息,具體出錯在哪裏應該清楚了吧?或者是發送、接收過程都沒問題,僅僅是文件沒内容? 其次再來考慮數據丟失問題,UDP是不可靠協議,需要自己做好確認、重傳,所以,你這麽在直連環境下測試問題不大,在實際網絡上就需要增加代碼了。
赵4老师 2017-12-12
  • 打赏
  • 举报
回复
代码功能归根结底不是别人帮自己看或讲解或注释出来的;而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。 提醒:再牛×的老师也无法代替学生自己领悟和上厕所! 单步调试和设断点调试(VS IDE中编译连接通过以后,按F10或F11键单步执行,按Shift+F11退出当前函数;在某行按F9设断点后按F5执行停在该断点处。)是程序员必须掌握的技能之一。
Cyshall 2017-12-12
  • 打赏
  • 举报
回复
引用 1 楼 zhao4zhong1 的回复:
UDP漏包是正常的。 搜“RUDP”?
不可能是漏包吧.接收端接收的文件大小是0,如果是漏包这等于全漏了阿,不可能的阿.
Cyshall 2017-12-12
  • 打赏
  • 举报
回复
不可能是漏包吧.接收端接收的文件大小是0,如果是漏包这等于全漏了阿,不可能的阿.
赵4老师 2017-12-12
  • 打赏
  • 举报
回复
UDP漏包是正常的。 搜“RUDP”?

69,369

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧