作為Computer networks課程的一個project,我們需要實現用TCP在mininet中client和server的相互通信,需要能夠傳輸文本文件,binary file 和image file。整個project的目的還是比較明確的。我主要列出了一下幾個問題,需要在代碼中實現。
1. 實現main函數的接口
因為client和server 需要的參數需要通過命令行輸入,所以需要用getoptlong函數來實現。
int main(int argc, char* argv[]) { int code; int flag; int server_port; int client_port; string hostname; string filename; while(1) { static struct option long_options[]={ {"c", no_argument, &flag, 1}, {"s", no_argument, &flag, 0}, {"port", required_argument, 0, 'p'}, {"host", required_argument, 0, 'h'}, {"file", required_argument, 0, 'f'}, {"sp", required_argument, 0, 'e'}, {"cp", required_argument, 0, 'l'} }; int option_index=0; code=getopt_long_only(argc,argv,"p:h:f:e:l:", long_options, &option_index); if(code==-1) break; switch(code) { case 'p': server_port=atoi(optarg); break; case 'h': hostname=string(optarg); break; case 'f': filename=string(optarg); break; case 'e': server_port=atoi(optarg);break; case 'l': client_port=atoi(optarg);break; default: break; } } if(flag) send_message(hostname.c_str(), server_port, filename.c_str(), client_port); else run_server(server_port);
2. 實現socket programming中的功能
在這個項目中提供了UDP的socket programming的接口,按照需要設置即可。
// (1) Create a socket int sockfd = socket(AF_INET, SOCK_STREAM, 0); //hzd: socket interface structure // (2) Create a sockaddr_in to specify remote host and port struct sockaddr_in server_addr; // specify server's address server_addr.sin_family = AF_INET; // Step (2): specify socket address (hostname). // The socket will be a client, so call this unix helper function to convert a hostname string to a useable `hostent` struct. // hostname here may be 10.0.0.1 struct hostent *host = gethostbyname(hostname); //hzd: instantiate a instance of host with name specified if (host == nullptr) { fprintf(stderr, "%s: unknown host\n", hostname); return -1; } memcpy(&(server_addr.sin_addr), host->h_addr, host->h_length);//hzd: copy the hostname to the server address // if we also we also want to bind client's socket to a port number... struct sockaddr_in my_addr;//hzd: the address of the client memset(&my_addr, 0, sizeof(my_addr));//hzd: alloctae memory for the my_addr. my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = INADDR_ANY; //INADDRY_ANY == 0.0.0.0 my_addr.sin_port = htons(client_port); //hzd: specify the port number of the client bind(sockfd, (struct sockaddr *) & my_addr, sizeof(my_addr));// hzd: bind the address of client to the socket // Step (3): Set the port value. // Use htons to convert from local byte order to network byte order. server_addr.sin_port = htons(port);//hzd: spcify the port of sever // (3) Connect to remote server if (connect(sockfd, (sockaddr *) &server_addr, sizeof(server_addr)) == -1) { perror("Error connecting stream socket"); return -1; }
3. 連續send造成的粘包問題
由於TCP擁有優化算法,可以將多個send的數據被一個recv收到,所以造成粘包問題。本項目中會要求先發送文件長度,再發送文件,連續發送造成文件長度和文件粘合在一起,造成無法讀出文件。解決辦法是加入sleep()函數在兩次send之間。這樣就能將兩次send分開了。
4. 大文件的接收問題
大文件的接受容易由於buffer長度的限制,造成丟失。因此需要設置一個MAX_MESSAGE_SIZE。分成多次完成接收。本來想要在send端也多次發送,效果不好。
5. 文件的讀寫
由於文件的類型不確定,打開文件都需要用binary格式,讀寫的時候一定要注意,用read()和write()函數,千萬不要<<或者>>,因為后一種方式沒法確定文件類型。
ifstream infile; infile.open((const char*) msg,ios::in|ios::binary); char* data; infile.seekg(0, ios::end); int length=infile.tellg(); infile.seekg(0, ios::beg); data= new char[length]; infile.read(data,length);