该例子完成了一个基本用TCP实现的CS程序,支持大文件传输,Client可以向Server发送要下载文件的名字,Server支持Client完成下载,并显示进度。该例子并没有文件的容错处理,也没有socket方面的各种异常的处理。 两个地方注意:1.TCP客户端和服务器端程序的基本结构 2.文件的处理
Client:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cerrno>
#include <unistd.h>
#include <cstring>
#include <fstream>
#define MAX_LEN 1024
#define MAX_SIZE 1024
using namespace std;
static int download_size = 0;
int main(int argc, char *argv[])
{
int sockfd;
int connfd;
int ret_val = 0;
struct sockaddr_in servaddr;
string down_filename;//download filename
string save_filename;//save filename
//user input the argument
if(argc != 5) {
printf("Usage:./client <address> <port> <Download filename> <Save filename>/n");
exit(1);
}
//inital the download and save file name
down_filename = argv[3];
save_filename = argv[4];
//initial servaddr
bzero(&servaddr,sizeof(servaddr));
ret_val = inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
if(ret_val <= 0)
{
perror("inet_pton");
return -1;
}
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
//create socket
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
perror("socket");
return -1;
}
//connect
socklen_t len_serv = sizeof(servaddr);
connfd = connect(sockfd,(struct sockaddr *)&servaddr,len_serv);
if(connfd < 0) {
perror("Connect");
return -1;
}
//send the command
string command = down_filename;
command += "/n/r";
int num_bytes = 0;
int ret_val_sent = 0;
int len_send = command.length();
while(num_bytes < len_send)
{
string send_buf = command.substr(num_bytes,len_send - num_bytes);
ret_val_sent = send(sockfd,send_buf.c_str(),send_buf.length(),0);
if(ret_val_sent<0)
{
perror("send");
return -1;
}
num_bytes += ret_val_sent;
}
//recv and save the file
char buf[MAX_SIZE];
int ret_val_save = 0;
ofstream out(save_filename.c_str(),ios::out | ios::trunc | ios::binary);
cout<<endl;
if(!out.is_open())
{
cout<<"Create file error:"<<save_filename<<endl;
return -1;
}
for(;;)
{
ret_val_save = recv(sockfd,buf,MAX_SIZE,0);
if(ret_val_save < 0)
{
out.close();
return -1;
}
else if(ret_val_save > 0)//write
{
out.write(buf, static_cast<streamsize>(ret_val_save));
download_size += ret_val_save;
cout<<"Complete "<<download_size<<" bytes!"<<endl;
}
else if(ret_val_save == 0)
{
cout<<"Finished!"<<endl;
break;
}
}
cout<<endl;
out.close();
//close
if(sockfd > 0)
close(sockfd);
return 0;
}
Server:
#include <cstdlib>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <unistd.h>
#include <fstream>
#include <cstring>
#define BACKLOG 10
#define MAX_LEN 1024
#define MAX_SIZE 1024
using namespace std;
int main(int argc, char* argv[])
{
int sockfd = 0;//for socket,bind,listen
int connfd = 0;//for accept,send,recv
struct sockaddr_in servaddr;
struct sockaddr_in tempaddr;
socklen_t temp_len;//using in bind,listen
char ip_str[INET_ADDRSTRLEN];
ssize_t number_bytes;
string down_filename;
string save_filename;
int ret_val;
//initial
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = 0;
//socket,bind and listen
sockfd = socket(AF_INET,SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("socket");
exit(1);
}
ret_val = bind(sockfd,(const sockaddr*)&servaddr,sizeof(struct sockaddr));
if(ret_val < 0)
{
perror("bind");
exit(1);
}
//get localhost ip by getsockname, and then print it
temp_len = sizeof(struct sockaddr);
ret_val = getsockname(sockfd, (struct sockaddr *)&tempaddr,&temp_len);
if(ret_val < 0)
{
perror("getsockname");
return -1;
}
ret_val = listen(sockfd, BACKLOG);
if(ret_val < 0)
{
perror("listen");
return -1;
}
inet_ntop(AF_INET, &tempaddr.sin_addr, ip_str,INET_ADDRSTRLEN);
cout<<"The host IP is: "<<ip_str<<". Please use this IP in Client!"<<endl;
cout<<"The server is listening on port: "<<ntohs(tempaddr.sin_port)<<". Please use this Port in Client!"<<endl;
//the server run always
while(1)
{
struct sockaddr_in cliaddr;
socklen_t cli_len;
cli_len = sizeof(cliaddr);
bzero(&cliaddr,cli_len);
connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&cli_len);
//sockfd change to connfd
if(connfd == -1)
{
perror("Accept");
exit(1);
}
//get the command
char line[MAX_SIZE];
char cmd_buf[MAX_SIZE];
int number_bytes = 0;
char *ptr = 0;
int read = 1;
int recv_bytes = 0;
memset(line, '/0', MAX_SIZE);
memset(cmd_buf,'/0', MAX_SIZE);
//read command
while(read)
{
recv_bytes = recv(connfd,line,MAX_SIZE,0);
if(ret_val_recv <0 )
{
perror("Recv");
}
line[recv_bytes] = '/0';
number_bytes += recv_bytes;
if ((ptr = strstr(line,"/n/r")) == 0)
{
if (number_bytes <= MAX_SIZE)
strcat(cmd_buf, line);
}
else
{
strncat(cmd_buf, line , ptr - line);
read = 0;
}
}//while
//after while(read),get the down_filename
down_filename = cmd_buf;
cout<<"The client requsts filename is: "<<down_filename<<endl;
//read and send
char buf_read[MAX_SIZE];
int read_size = 0;
ifstream in;
in.open(down_filename.c_str(),ios::binary | ios::in);
if(!in){
cout<<"Cannot open the file"<<down_filename<<endl;
return -1;
}
while(1)
{
int num_bytes = 0;
in.read(buf_read, static_cast<streamsize>(MAX_SIZE));
read_size = in.gcount();
if(read_size < 0)
{
return -1;
}
else if(read_size == 0)
{
cout<<"Have finished transfer the file!"<<endl;
break;
}
else
{
int len = read_size;
int once_send_size = 0;
while(num_bytes < read_size)
{
once_send_size = send(connfd,buf_read + num_bytes,len,0);
if (once_send_size < 0)
{
perror("send");
}
num_bytes += once_send_size;
len -= once_send_size;
}
}
}//for
in.close();
close(connfd);
}
return 0;
}