前段时间自己在写一个基于TCP/UDP的文件传输小程序,就是客户端输入文件名之后服务器能够接收。
开始由于没有构思很仔细,在传递文件信息和文件内容的时候总是不能够很成功,因为第一次传送的是文件信息,但是之后传送的是文件的内容,就是这两步区分不是很好,之后还是突然经过一帅哥提醒了一下,发现在TCP中客户端第一次可以发送文件信息,于是自己创建了一个结构体:
/** @struct fileInfo
* @brief file information, file name and file size
*/
struct fileInfo // file information
{
char fileName[FILENAMESIZE];
unsigned long fileSize;
};
之后通过强制转换为(char *)类型传递给服务器端,
if( send(sockfd, (char *)fileInfo, sizeof(struct fileInfo), 0) < 0 )
{
printf("send file information error!\n");
exit(1);
}
之后服务器端强制转换为(struct fileInfo *)类型获取对应的消息内容
recvNum = recv(connfd, buff, MAXBUF, 0); //receive data from client
tmp = (struct fileInfo *)buff;
之后服务器再发送一个确认信息:
strcpy(buff, "SUCCESS");
if( send(connfd, buff, sizeof(buff), 0) < 0 )
{
printf("tcp: send error!\n");
exit(1);
}
再之后客户端收到确认信息
recv(sockfd, buf, MAXBUF, 0); // receive confirm message
if ( strcmp(buf, "SUCCESS") != 0 )
{
printf("receive confirm message error!\n");
exit(1);
}
经过这一段的交互,之后就可以通过send()和recv()发送读取文件内容。
这样就实现了文件的传递。
之后自己在UDP中也打算使用这个机制,发现由于UDP不是可靠的连接,你不能保证你第一次发送的文件头信息服务器就能够收到,于是纠结了好久,最后发现了另外一种可以实现的方法:
就是同样定义一个结构体,
/** @struct udpMsg
* @brief file information and a synchronize flg used to synchronize server and client
*/
struct udpMsg // udp message to synchronous the data
{
struct fileInfo fileInfo;
int SynFlg;
};
但是其中包含了一个flg,初始化为0,之后服务器端收到文件之后读取flg的值看是否为0,如果是表示传送的文件头信息,如果不是表示接收到的是文件内容信息,之后就可以通过文件读写操作来实现文件的接收了,就完成了文件的传送。并且还使用了select()阻塞的监听客户端的请求,部分代码如下:
printf("======UDP:waiting for client's request======\n");
while (1)
{
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&rdset);
FD_ZERO(&writeset);
FD_SET(sockfd, &rdset);
FD_SET(sockfd, &writeset);
ret = select(sockfd + 1, &rdset, &writeset, NULL, &tv);
if (-1 == ret)
{
printf("udp: select error!\n");
exit(1);
}
else if (0 == ret)
{
//printf("udp: select timeout, continue...\n");
continue;
}
if (FD_ISSET(sockfd, &rdset)) // socket is readable, then we read
{
printf("udp: server first read udpMsg->SynFlg = %d\n", udpMsg->SynFlg);
if ( 0 == udpMsg->SynFlg ) // means it is the first time we get the msg, the msg is file infomation
{
readSize = read(sockfd, buf, MAXBUF);
printf("udp: receive num: %d\n", readSize);
tmp = (struct udpMsg *)buf;
strcpy(udpMsg->fileInfo.fileName, tmp->fileInfo.fileName);
udpMsg->fileInfo.fileSize = tmp->fileInfo.fileSize;
udpMsg->SynFlg = tmp->SynFlg + 1;
printf("udp: fileSize = %lu, SynFlg = %d\n", udpMsg->fileInfo.fileSize, udpMsg->SynFlg);
if ( (fd = open(udpMsg->fileInfo.fileName, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1 ) // create the file
{
printf("udp: create error\n");
exit(1);
}
printf("udp: create file succeed!\n");
}
else // it is file content ,we just read and write to file
{
while ( (readSize = read(sockfd, buf, MAXBUF)) > 0 )
{
int tmp = 0;
//printf("udp: read client data: %s\n", buf);
fileSize += readSize;
//printf("udp: read file size = %lu\n", fileSize);
if ( (tmp = write(fd, buf, readSize)) != readSize )
{
//(write(fd, buff, recvNum)
printf("udp: write data:%d\n", tmp);
printf("udp: received file error!\n");
exit(1);
}
}
if ( fileSize == udpMsg->fileInfo.fileSize )
{
printf("udp: received file success!");
}
}
}
}
ps:最初自己udp使用的是sendto和recvfrom,发现这两个函数用的巨蛋疼,而且还是五个参数,期间还发现了莫名其妙的错误,可能也是自己不熟悉的原因,之后自己就先通过connect()连上服务器,后面的操作就通过read(),write()函数来发送数据,轻松了很多...
代码如下:
void connectUDP(struct udpMsg *udpMsg, int sockfd, struct sockaddr *serveraddr, socklen_t servlen)
{
.......
if ( -1 == (connect(sockfd, serveraddr, servlen)) )
{
printf("UDP connect error!\n");
exit(1);
}
......
}
程序截图如下:
以上东西也是自己的心得,发现遇到问题然后解决感觉还是挺舒服的,也希望对大家能够有所帮助...