本实验是掌握 Windows 环境下使用 Socket 开发的方法 ,利用tcp实现文件的传输。
算法思想: 首先服务端加载套接字库然后调用socket函数创建一个socket,调用bind函数将地址与端口号绑定,然后listen监听来自客户端请求。客户端建立socket,利用connect函数将套接字与服务器相连接,连接成功则返回连接成功反之返回连接失败。然后客户端先获取待发送文件的文件名(包含文件路径)以及文件长度,发送给服务端,服务端接收到文件名和文件长度之后为其创建新的文件保存路径和数据缓冲区。之后客户端建立和文件大小一样的缓冲区,利用fread函数将文件内容读入缓冲区再将缓冲区内容利用send函数发送给服务端,服务端利用recv函数将接收的文件的数据写入缓冲区,最后利用fwrite函数依次将数据缓冲区的数据写入新创建的文件中。
程序流程图:
代码:
Server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#define SERVER_IP "127.0.0.1"
#define B_SIZE 1024
#define FILE_SIZE 512
#pragma comment(lib, "WS2_32")
int main()
{
WSADATA wsdt;
WORD skVer = MAKEWORD(2, 2);
if(WSAStartup(skVer, &wsdt) != 0)
{
printf("端口初始失败!");
return 0;
}
// 创建socket
SOCKET s_skt = socket(AF_INET, SOCK_STREAM, 0);
if (s_skt == SOCKET_ERROR)
{
printf("创建socket失败!");
return 0;
}
// 声明并初始化一个服务端(本地)的地址结构
sockaddr_in addr_ser;
addr_ser.sin_family = AF_INET;
addr_ser.sin_addr.S_un.S_addr = INADDR_ANY;
addr_ser.sin_port = htons(8087);
//绑定socket和服务端(本地)地址
if (bind(s_skt, (LPSOCKADDR)&addr_ser, sizeof(addr_ser)))
{
printf("绑定失败!");
return 0;
}
//监听
if (SOCKET_ERROR==listen(s_skt, 10))
{
printf("监听失败!");
return 0;
}
for(;;) {
printf("监听中...\n");
sockaddr_in addr_cli;
int addr_cli_len = sizeof(addr_cli);
SOCKET s_Socket = accept(s_skt, (sockaddr *)&addr_cli, &addr_cli_len);
if (s_Socket==SOCKET_ERROR)
{
printf("接收失败!");
break;
}
char server_buffer[B_SIZE];
memset(server_buffer, 0, B_SIZE); //初始化
if (recv(s_Socket, server_buffer, B_SIZE, 0) < 0)
{
printf("服务端接收数据失败!");
break;
}
char recvfile[FILE_SIZE+1];
char str[20];
memset(recvfile, 0, FILE_SIZE+1);
if(strlen(server_buffer)>FILE_SIZE){
strncpy(recvfile,server_buffer,FILE_SIZE);
}else{
strncpy(recvfile,server_buffer,strlen(server_buffer));
}
printf("%s\n", recvfile);
printf("请输入文件保存路径:");
gets(str);
FILE * fp = fopen(str, "wb");
if(fp==NULL){
printf("文件打开失败!");
return 0;
}
memset(server_buffer, 0, B_SIZE);
int i=0;
for(;;){
i+=B_SIZE;
recv(s_Socket, server_buffer, B_SIZE+1,0);
fwrite(server_buffer, 1, B_SIZE, fp);
if(i>=ftell(fp)){
printf("文件接收完毕!\n");
fclose(fp);
break;
}
}
fclose(fp);
printf("文件: %传输成功!\n", recvfile);
closesocket(s_Socket);
}
closesocket(s_skt);
//释放winsock库
WSACleanup();
return 0;
}
Client:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#define SERVER_IP "127.0.0.1"
#define B_SIZE 1024
#define FILE_MAX_SIZE 512
#pragma comment(lib, "WS2_32")
int main()
{
// 初始化socket dll
WSADATA wsdt;
WORD skVer = MAKEWORD(2, 2);
if(WSAStartup(skVer, &wsdt) != 0)
{
printf("端口初始失败!");
return 0;
}
// 创建socket
SOCKET c_Socket = socket(AF_INET, SOCK_STREAM, 0);
if (c_Socket==SOCKET_ERROR )
{
printf("创建socket失败!");
return 0;
}
//指定服务端的地址
sockaddr_in addr_ser;
addr_ser.sin_family = AF_INET;
addr_ser.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
addr_ser.sin_port = htons(8087);
if (SOCKET_ERROR ==connect(c_Socket, (LPSOCKADDR)&addr_ser, sizeof(addr_ser)))
{
printf("连接失败!\n");
return 0;
}
//输入文件名
char sendfile[FILE_MAX_SIZE+1];
memset(sendfile, 0, FILE_MAX_SIZE+1);
printf("请输入要发送的文件名: ");
scanf("%s", &sendfile);
char client_buffer[B_SIZE];
memset(client_buffer, 0, B_SIZE);
if(strlen(client_buffer)>FILE_MAX_SIZE){
strncpy(client_buffer,sendfile,B_SIZE);
}else{
strncpy(client_buffer,sendfile,strlen(sendfile));
}
if(send(c_Socket, client_buffer, B_SIZE, 0) < 0)
{
printf("文件名发送失败!\n");
return 0;
}
//打开文件
FILE * fp = fopen(sendfile, "rb");
if(NULL == fp)
{
printf("文件: %s 不能打开\n", sendfile);
return 0;
}
else
{
memset(client_buffer, 0, B_SIZE);
int i=0;
while (!feof(fp))
{
fread(client_buffer, 1, B_SIZE, fp);
i+=B_SIZE;
if(i<ftell(fp)){
send(c_Socket, client_buffer, B_SIZE+1, 0);
}else{
send(c_Socket, client_buffer,ftell(fp)+B_SIZE-i+1, 0);
closesocket(c_Socket);
//释放winsock库
WSACleanup();
}
}
fclose(fp);
}
printf("文件发送完毕!");
system("pause");
return 0;
}
结果截图:
其中1.txt中内容是要发送的文件内容,2.txt用于接收文件,文件名还有文件保存路径可改。