計算機網絡課程設計-Socket網絡通信(C++, MacOS)

實驗要求:

利用Socket實現雙機通信

實驗目的:

利用Socket編程,採用其中的TCP面向連接方式,實現計算機數據的交換

具體要求:

  1. 操作系統:可在Linux或Windows操作系統下實現
  2. 編程語言:C或C++,若使用Java、python等更高級的語言或腳本語言則須實現下面的“擴展功能”
  3. 界面要求:圖形界面或者命令行界面均可
  4. 連接方式:局域網內通信,有線或無線網絡均可
  5. 實現功能:
    • 基本功能(必做):計算機A、B(須爲不同的物理機)實現文本通信,即B收到A所發送的文本並顯示。
    • 擴展功能(加分選做):雙向通信(同一程序下,A、B既可以發送數據又可以接受數據);文本傳輸(B完整收到A發送的文件並保存);實現音視頻傳輸(類似於QQ電話);其他網絡拓展功能
  6. 驗收方式等不再贅述

實現

基於C++和MacOS的實現,unix上可以正常運行,linux好像有庫不支持。有關Socket的基礎知識見博客網絡socket編程指南,下面代碼中要實現的內容有:

  1. 基本文本雙向通信,多對多雙向通信,實現私聊和羣聊功能
  2. 文件雙向傳輸,多對多雙向傳送,實現私傳和羣傳功能

題目要求使用TCP方式,因此應該使用流式套接字。代碼使用127.0.0.1測試成功,一主機對一虛擬機也測試成功了,尚未進行多臺電腦之間的測試(疫情在家,買不起多臺電腦)。

由於沒有設計GUI,我們需要對客戶端的輸入格式進行規範化,如下所示:

首先輸入聊天對象:
1 ID表示私聊客戶端ID,2表示進入羣聊,向包括服務端的所有人發送信息。

選擇聊天對象後:
1 文本表示發送文本信息,2 PATH表示將PATH路徑上的文件發送過去,3表示開始視頻通話(此功能尚未實現)。之後的聊天不需要輸入聊天對象,默認是與此人聊天。

鍵入\exit表示退出與此人的聊天,下次需要重新輸入聊天對象。

鍵入\quit表示退出與服務器的連接。

服務端可以對羣體進行文件傳輸和文本發送,和上面相同,1 文本表示發送文本信息,2 PATH表示將PATH路徑上的文件發送過去,3表示開始視頻通話(此功能尚未實現)。

暫時沒有實現任意多個人之間進行聊天。代碼有些雜亂,並且或多或少可能有一些bug,但是可以正常運行。我儘量抽時間整理,見諒。

運行界面

服務端

在這裏插入圖片描述

客戶端1

在這裏插入圖片描述

客戶端2

在這裏插入圖片描述

完整代碼

服務端
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fstream>
const int BACKLOG=2; //完成三次握手但沒有accept的隊列的長度
const int CONCURRENT_MAX=3; //應用層同時可以處理的連接
const int BUFFER_SIZE=1024;//將被加入緩存器的最大字節數
char input_msg[BUFFER_SIZE];
char recv_msg[BUFFER_SIZE];

class Socket_server{
public:
    Socket_server(){
        server_sock_fd=0;
        tv.tv_sec = 20;//秒
        tv.tv_usec = 0;//微秒
        max_fd=-1;
        memset(client_fds,0,sizeof(client_fds));
        memset(client_to,-1,sizeof(client_to));
        filename.clear(); path.clear();
    };
    ~Socket_server(){

    };
    bool init(const int port);
    bool start();
    bool sendmessage();//服務器發出信息
    bool sendIP(int index);//發送號碼錶
    void recv_and_forward();//接收信息,同時起到中轉站的作用
    void recv_word(int index,int byte_num);
    void recv_file(int index,int byte_num);
    void recv_video(int index);
    void accept_new();
private:
    int server_sock_fd;//服務端套接字
    //fd_set
    fd_set server_fd_set;//用來存放所有的待檢查的文件描述符
    int max_fd;
    timeval tv;//struct timeval結構用於描述一段時間長度,如果在這個時間內,需要監視的描述符沒有事件發生則函數返回,返回值爲0。
    int client_fds[CONCURRENT_MAX+1];//客戶端套接字
    struct sockaddr_in client_addr[CONCURRENT_MAX+1];//客戶端對應的地址信息
    int client_to[CONCURRENT_MAX+1];//客戶端想要發送的對象
    std::string filename;
    std::string path;
};
bool Socket_server::init(const int port){
    struct sockaddr_in server_addr;//sockaddr_in: Socket address, internet style.表示網絡地址的結構體(internet和socket通用)
    server_addr.sin_len = sizeof(struct sockaddr_in);
    server_addr.sin_family = AF_INET;//socket只能用這個
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//    server_addr.sin_port = htons(0);//16位整數 主機字節序轉網絡字節序,此處爲設置端口號(本機是小端存儲,網絡字節是大端)
//    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//設置本機IP地址
    memset(&(server_addr.sin_zero),0,8);
    //創建socket
    server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);

    if (server_sock_fd == -1){//未能成功打開網絡通訊端口
        perror("socket error");//輸出字符串+errno對應的錯誤
        return false;
    }

    int on = 1;
    if(setsockopt(server_sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))==-1){//防止出現bind error的地址已被佔用
        perror("setsockopt");
        return false;
    }

    //綁定socket
    int bind_result = bind(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));

    if (bind_result == -1){
        perror("bind error");
        return false;
    }

    //listen
    if (listen(server_sock_fd, BACKLOG) == -1){
        perror("listen error");
        return false;
    }
    printf("歡迎使用SOCKET通訊系統,輸入quit並確認可退出\n");

    return true;
}
bool Socket_server::start()
{
    FD_ZERO(&server_fd_set);
    //標準輸入
    FD_SET(STDIN_FILENO, &server_fd_set);//用於在文件描述符集合中增加一個新的文件描述符。STDIN_FILENO是標準輸入文件描述符

    if (max_fd < STDIN_FILENO)//STDIN_FILENO = 0
        max_fd = STDIN_FILENO;
    //服務器端socket
    FD_SET(server_sock_fd, &server_fd_set);//把要檢測的套接字server_sock_fd加入到集合中
    if (max_fd < server_sock_fd){
        max_fd = server_sock_fd;
    }
    //客戶端連接
    for (int i = 1; i <= CONCURRENT_MAX; i++){
        if (client_fds[i]!=0){
            FD_SET(client_fds[i], &server_fd_set);
            if (max_fd < client_fds[i])//找到文件描述符中的最大值並存儲
                max_fd = client_fds[i];
        }
    }
    int ret = select(max_fd+1, &server_fd_set, NULL, NULL, &tv);//檢測server_fd_set中的套接字中是否有可讀信息
    if (ret < 0){
        perror("select 出錯");
        return false;
    }
    else if(ret == 0){
        printf("timeout...\n");
        return false;
    }
    return true;
}
bool Socket_server::sendmessage()
{
    //ret爲未狀態發生變化的文件描述符的個數
    if (FD_ISSET(STDIN_FILENO, &server_fd_set)){//檢測是否可寫,可發送語句
        //標準輸入
        memset(input_msg,0,sizeof(input_msg));
        fgets(input_msg, BUFFER_SIZE, stdin);
        char *find = strchr(input_msg, '\n');//查找換行符
        if(find) *find = '\0';//如果find不爲空指針,就把一個空字符放在這裏
        if (strcmp(input_msg, "\\quit") == 0){
            shutdown(server_sock_fd,SHUT_RDWR);
            return false;
        }
        if(input_msg[0]=='1' && input_msg[1]==' '){
            for (int i=1; i<=CONCURRENT_MAX; i++){//向每個連接的客戶端發送信息
                if (client_fds[i]!=0){
                    std::string s; s.clear(); s="0";//表明這是誰發過來的
                    if(send(client_fds[i],s.c_str(),BUFFER_SIZE,0)==-1)
                        perror("發送消息出錯!");
                    if(send(client_fds[i], input_msg, BUFFER_SIZE, 0)==-1)
                        perror("發送消息出錯!");
                }
            }
        }
        else if(input_msg[0]=='2' && input_msg[1]==' '){
            for (int i=1; i<=CONCURRENT_MAX; i++){
                if(client_fds[i]!=0){
                    std::string s; s.clear();
                    s="0";//表明這是誰發過來的
                    if(send(client_fds[i],s.c_str(),BUFFER_SIZE,0)==-1)
                        perror("轉發消息出錯!");
                }
            }
            for (int i=1; i<=CONCURRENT_MAX; i++){
                if(client_fds[i]!=0){
                    if(send(client_fds[i],input_msg,BUFFER_SIZE,0)<0)
                        perror("發送消息出錯!");
                }
            }
            path.clear();
            for (int i=2; input_msg[i]!='\n' && input_msg[i]!='\0'; i++){
                path+=input_msg[i];
            }
            FILE *fp=fopen(path.c_str(),"rb+");
            char chtemp[BUFFER_SIZE+1];
            memset(chtemp,'\0',sizeof(chtemp));
            int cnt=0;
            if(fp==nullptr){
                printf("文件無法打開\n");
            }
            else {
                while((cnt=fread(chtemp,sizeof(char),BUFFER_SIZE,fp))>0){
                    for (int i=1; i<=CONCURRENT_MAX; i++){
                        if(client_fds[i]!=0){
                            if(send(client_fds[i],chtemp,cnt,0)<0){
                                perror("發送消息出錯!");
                            }
                        }
                    }
                    memset(chtemp,'\0',sizeof(chtemp));
                }
                fclose(fp);
                printf("Transfer Successful!\n");
            }
            path.clear(); filename.clear();
        }
        else if(input_msg[0]=='3' && input_msg[1]==' '){
            printf("敬請期待!\n");
        }

    }
    return true;
}
bool Socket_server::sendIP(int index)
{
    std::string s1="0";
    send(client_fds[index],s1.c_str(),BUFFER_SIZE,0);
    s1.clear();

    s1="1 您現在是"+std::to_string(index)+"號客戶機, 當前聊天室內成員如下:\n";
    send(client_fds[index],s1.c_str(),BUFFER_SIZE,0);
    for (int i=1; i<=CONCURRENT_MAX; i++){
        if(client_fds[i]!=0){
            s1.clear();
            s1="0";
            send(client_fds[index],s1.c_str(),BUFFER_SIZE,0);
            s1.clear();
            s1="1 "+std::to_string(i)+"號客戶機,IP地址爲:"+inet_ntoa(client_addr[i].sin_addr)+"\n";
            if(send(client_fds[index],s1.c_str(),BUFFER_SIZE,0)==-1) return false;
        }
        if(client_fds[i]!=0 && i!=index){
            s1.clear();
            s1="0";
            send(client_fds[i],s1.c_str(),BUFFER_SIZE,0);
            s1.clear();
            s1="1 新加入了"+std::to_string(index)+"號客戶機,IP地址爲:"+inet_ntoa(client_addr[index].sin_addr)+"\n";
            if(send(client_fds[i],s1.c_str(),BUFFER_SIZE,0)==-1) return false;
        }
    }
    return true;
}
void Socket_server::recv_word(int index,int byte_num)
{
    if(client_to[index]==0 || client_to[index]==-2){//發送給服務器的或者羣發
        printf("客戶端(%d):",index);
        for (int j=2; j<byte_num; j++){
            if(recv_msg[j]=='\0') break;
            printf("%c",recv_msg[j]);
        }
        printf("\n");
    }
    if(client_to[index]==-2){//羣發其餘客戶端
        for (int i=1; i<=CONCURRENT_MAX; i++){
            if(client_fds[i]!=0 && i!=index){
                //先給id發送這是誰發過來的
                std::string s; s.clear();
                s=std::to_string(index);//表明這是誰發過來的
                if(send(client_fds[i],s.c_str(),BUFFER_SIZE,0)==-1)
                    perror("轉發消息出錯!");
                if (send(client_fds[i], recv_msg, BUFFER_SIZE, 0) == -1){
                    perror("轉發消息出錯!");
                }
            }
        }
    }
    else if(client_to[index]!=0){//發送給個人的
        //先給id發送這是誰發過來的
        std::string s; s.clear();
        s=std::to_string(index);//表明這是誰發過來的
        if(send(client_fds[client_to[index]],s.c_str(),BUFFER_SIZE,0)==-1)
            perror("轉發消息出錯!");
        if (send(client_fds[client_to[index]], recv_msg, BUFFER_SIZE, 0) == -1){
            perror("轉發消息出錯!");
        }
    }
}
void Socket_server::recv_file(int index,int byte_num)
{
    std::string tempfilename; tempfilename.clear();
    if(client_to[index]==0 || client_to[index]==-2){//發送給服務器的或者羣發
        int pos=0;
        for (int j=byte_num-1; j>0; j--){
            if(recv_msg[j]=='/' || recv_msg[j]=='\\'){
                pos=j; break;
            }
        }
        for (int j=pos+1; j<byte_num; j++){
            if(recv_msg[j]=='\0') break;
            filename+=recv_msg[j];
        }
        //取得文件名稱,準備建立新的文件
        printf("客戶端(%d)向你傳送了一個文件,請輸入您想要存放的絕對路徑,例如:/Users/longzhengtian/Desktop/\n注意,最後要有一個'/'符號,如果沒有,系統將自動填充,請輸入:",index);
        std::cin>>path; getchar();
        if(path[path.size()-1]!='/'){//如果沒有'/'的補救措施
            path+="/";
        }
        //filename="new_"+filename;
        path+=filename;
        FILE *fp=fopen(path.c_str(),"wb+");
        if(fp==nullptr)
            printf("接收文件出錯!\n");
        else {
            memset(recv_msg,0,sizeof(recv_msg));
            int cnt=0;
            while((cnt=recv(client_fds[index],recv_msg,BUFFER_SIZE,0)) && cnt>0){
                if(fwrite(recv_msg,sizeof(char),cnt,fp)<cnt){
                    printf("接收文件出錯!\n");
                    break;
                }
                memset(recv_msg,0,sizeof(recv_msg));
                if(cnt!=BUFFER_SIZE) break;
            }
            printf("Receive successful!\n");
            fclose(fp);
        }
        if(client_to[index]==0) path.clear(); filename.clear();
    }
    if(client_to[index]==-2){//羣發
        for (int i=1; i<=CONCURRENT_MAX; i++){
            if(client_fds[i]!=0 && i!=index){
                std::string s; s.clear();
                s=std::to_string(index);//表明這是誰發過來的
                if(send(client_fds[i],s.c_str(),BUFFER_SIZE,0)==-1)
                    perror("轉發消息出錯!");
            }
        }
        filename="2 "+path;
        for (int i=1; i<=CONCURRENT_MAX; i++){
            if(i!=index && client_fds[i]!=0){
                if(send(client_fds[i],filename.c_str(),BUFFER_SIZE,0)<0)
                    perror("發送消息出錯!");
            }
        }
        FILE *fp=fopen(path.c_str(),"rb+");
        char chtemp[BUFFER_SIZE+1];
        memset(chtemp,'\0',sizeof(chtemp));
        int cnt=0;
        if(fp==nullptr){
            printf("文件無法打開\n");
        }
        else {
            while((cnt=fread(chtemp,sizeof(char),BUFFER_SIZE,fp))>0){
                for (int i=1; i<=CONCURRENT_MAX; i++){
                    if(i!=index && client_fds[i]!=0){
                        if(send(client_fds[i],chtemp,cnt,0)<0){
                            perror("發送消息出錯!");
                        }
                    }
                }
                memset(chtemp,'\0',sizeof(chtemp));
            }
            fclose(fp);
            printf("Transfer Successful!\n");
        }
        path.clear(); filename.clear();
    }
    else if(client_to[index]!=0){//發送給個人的
        std::string s; s.clear();
        s=std::to_string(index);//表明這是誰發過來的
        if(send(client_fds[client_to[index]],s.c_str(),BUFFER_SIZE,0)==-1)
            perror("轉發文件出錯!");
        if(send(client_fds[client_to[index]],recv_msg,BUFFER_SIZE,0)==-1)
            perror("轉發文件出錯!");

        memset(recv_msg,0,sizeof(recv_msg));
        int cnt=0;
        while((cnt=recv(client_fds[index],recv_msg,BUFFER_SIZE,0)) && cnt>0){
            if(send(client_fds[client_to[index]],recv_msg,cnt,0)==-1)
                perror("轉發文件出錯!");
            memset(recv_msg,0,sizeof(recv_msg));
            if(cnt!=BUFFER_SIZE) break;
        }
        printf("A file from Client%d to Client%d was successfully forwarded through this server\n",index,client_to[index]);
    }
}
void Socket_server::recv_video(int index)
{
    printf("敬請期待!\n");
}
void Socket_server::recv_and_forward()
{
    for (int i = 1; i <= CONCURRENT_MAX; i++){
        if (client_fds[i]!=0){
            if (FD_ISSET(client_fds[i], &server_fd_set)){
                //處理某個客戶端過來的消息
                memset(recv_msg,0,sizeof(recv_msg));
                int byte_num = recv(client_fds[i],recv_msg,BUFFER_SIZE,0);
                if (byte_num > 0){
                    if (byte_num > BUFFER_SIZE) byte_num = BUFFER_SIZE;
                    recv_msg[byte_num] = '\0';
                    if(strcmp(recv_msg,"\\exit")==0){//客戶端要求解除連接
                        client_to[i]=-1;
                    }
                    else if(client_to[i]==-1){//這條信息是對面發來指定發送對象的
                        if(recv_msg[0]=='-') client_to[i]=-2;
                        else client_to[i]=recv_msg[0]-'0';
                    }
                    else if(recv_msg[0]=='1'){//文字
                        recv_word(i,byte_num);
                    }
                    else if(recv_msg[0]=='2'){//文件
                        recv_file(i,byte_num);
                    }
                    else if(recv_msg[0]=='3'){//視頻
                        recv_video(i);
                    }
                }
                else if(byte_num < 0)
                    printf("從客戶端(%d)接受消息出錯.\n",i);
                else{
                    FD_CLR(client_fds[i], &server_fd_set);//用於在文件描述符集合中刪除一個文件描述符。
                    client_fds[i] = 0;
                    printf("客戶端(%d)退出了\n",i);
                    std::string s1;
                    for (int j=1; j<=CONCURRENT_MAX; j++){
                        if(client_fds[j]!=0 && i!=j){
                            s1.clear();
                            s1=std::to_string(i)+"號客戶機退出了!\n";
                            send(client_fds[j],s1.c_str(),BUFFER_SIZE,0);
                        }
                    }
                }
            }
        }
    }
}
void Socket_server::accept_new()
{
    if (FD_ISSET(server_sock_fd, &server_fd_set)){//檢測server_sock_fd是否真的變成了可讀,可接受語句
        //有新的連接請求
        struct sockaddr_in client_address;
        socklen_t address_len=sizeof(struct sockaddr_in);
        int client_socket_fd = accept(server_sock_fd, (struct sockaddr *)&client_address, &address_len);//對應客戶端對connect

        if (client_socket_fd > 0){
            int index = -1;
            for (int i = 1; i <= CONCURRENT_MAX; i++){
                if (client_fds[i] == 0){
                    index = i;
                    client_fds[i] = client_socket_fd;
                    break;
                }
            }
            if (index >= 0){
                printf("新客戶端(%d)加入成功 %s:%d \n",index,inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
                //inet_ntoa:接受一個in_addr結構體類型的參數並返回一個以點分十進制格式表示的IP地址字符串
                client_addr[index]=client_address;
                sendIP(index);
            }
            else{
                memset(input_msg,0,sizeof(input_msg));
                strcpy(input_msg, "服務器加入的客戶端數達到最大值!\n");
                send(client_socket_fd, input_msg, BUFFER_SIZE, 0);
                printf("客戶端連接數達到最大值,新客戶端加入失敗 %s:%d \n",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
            }
        }
    }
}
int main (){
    //本地地址
    Socket_server tcpserver;
    tcpserver.init(11111);//初始化,創建並綁定套接字,開始監聽

    while (true){
        if(!tcpserver.start()) {//建立連接失敗
            continue;
        }
        else{
            tcpserver.accept_new();//是否有新的加入
            if(!tcpserver.sendmessage()) exit(0);//發送信息,包含退出操作
            tcpserver.recv_and_forward();//接受信息。
        }
    }
    return 0;
}
客戶端
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fstream>
const int BUFFER_SIZE=1024;
char recv_msg[BUFFER_SIZE];
char input_msg[BUFFER_SIZE];

class Socket_client
{
public:
    Socket_client(){
        server_sock_fd=0;
        tv.tv_sec = 20;
        tv.tv_usec = 0;
        toID=-1;
        fromID=0;
    }
    ~Socket_client(){

    }
    bool start();
    bool init();
    bool connect_s();
    bool sendmessage();
    void receive();
    void recvmessage();
private:
    int server_sock_fd;
    struct sockaddr_in server_addr;
    fd_set client_fd_set;
    struct timeval tv;
    int toID;//-1是初始化,-2是羣發,0以上是私聊
    int fromID;
    std::string filename;
    std::string path;
};
bool Socket_client::init()
{
    server_addr.sin_len = sizeof(struct sockaddr_in);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(11111);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    memset(&(server_addr.sin_zero),0,8);
    server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_sock_fd == -1){
        perror("socket error");
        return false;
    }
    return true;
}
bool Socket_client::connect_s()
{
    if(connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in))==0) {
        printf("服務器加入成功!\n");
        return true;
    }
    else return false;
}
bool Socket_client::start()
{
    FD_ZERO(&client_fd_set);
    FD_SET(STDIN_FILENO, &client_fd_set);
    FD_SET(server_sock_fd, &client_fd_set);

    int ret = select(server_sock_fd + 1, &client_fd_set, NULL, NULL, &tv);
    if (ret < 0 ){
        printf("select 出錯!\n");
        return false;
    }
    else if(ret ==0){
        printf("timeout...\n");
        return false;
    }
    return true;
}
bool Socket_client::sendmessage()
{
    if (FD_ISSET(STDIN_FILENO, &client_fd_set)){

        if(toID==-1){//沒有制定發送對象,0號表示服務器
            int a=-1,b=-1;
            scanf("%d",&a);
            if(a==1) {
                scanf("%d",&b); toID=b;
            }
            else toID=-2;
            std::string s=std::to_string(toID);
            if(send(server_sock_fd,s.c_str(),BUFFER_SIZE,0)==-1){
                perror("制定發送對象出錯!");
            }
            getchar();
        }
        else {
            memset(input_msg,0,sizeof(input_msg));
            fgets(input_msg, BUFFER_SIZE, stdin);

            char *find = strchr(input_msg, '\n');//查找換行符
            if(find)//如果find不爲空指針
                *find = '\0';//就把一個空字符放在這裏

            if (strcmp(input_msg, "\\quit") == 0){
                shutdown(server_sock_fd,SHUT_RDWR);
                return false;
            }
            if (strcmp(input_msg, "\\exit") == 0){
                toID=-1;
                send(server_sock_fd,input_msg,5,0);
                return true;
            }
            if(input_msg[0]=='1'){//文字傳送
                if (send(server_sock_fd, input_msg, BUFFER_SIZE, 0) == -1){
                    perror("發送消息出錯!");
                }
            }
            else if(input_msg[0]=='2'){//文件傳輸
                if (send(server_sock_fd, input_msg, BUFFER_SIZE, 0) == -1){
                    perror("文件傳輸出錯!");
                }
                std::string _path;
                for (int i=2; input_msg[i]!='\n' && input_msg[i]!='\0'; i++){
                    _path+=input_msg[i];
                }
                FILE *fp=fopen(_path.c_str(),"rb+");
                char chtemp[BUFFER_SIZE+1];
                memset(chtemp,'\0',sizeof(chtemp));
                int cnt=0;
                if(fp==nullptr){
                    printf("文件無法打開\n");
                }
                else {
                    while((cnt=fread(chtemp,sizeof(char),BUFFER_SIZE,fp))>0){
                        if(send(server_sock_fd,chtemp,cnt,0)<0){
                            perror("發送消息出錯!");
                        }
                        memset(chtemp,'\0',sizeof(chtemp));
                    }
                    fclose(fp);
                    printf("Transfer Successful!\n");
                }
            }
            else if(input_msg[0]=='3'){//視頻聊天
                printf("您選擇了視頻聊天,功能尚未完成\n");
            }
            else {
                printf("傳輸形式不合法,請重新輸入。\n");
            }
        }
    }
    return true;
}
void Socket_client::recvmessage()
{
    fromID=0;
    //先接收到的是發送方ID;
    for (int i=0; recv_msg[i]!='\0'; i++){
        fromID*=10;
        fromID+=recv_msg[i]-'0';
    }
    memset(recv_msg,0,sizeof(recv_msg));
    memset(recv_msg,0,sizeof(recv_msg));
    int byte_num=recv(server_sock_fd,recv_msg,BUFFER_SIZE,0);
    if(byte_num>0){
        if (byte_num > BUFFER_SIZE) byte_num = BUFFER_SIZE;
        recv_msg[byte_num] = '\0';
        if(recv_msg[0]=='1' && recv_msg[1]==' '){//文字傳輸
            if(fromID==0) printf("服務器:");
            else printf("客戶端(%d):",fromID);
            for (int j=2; j<byte_num; j++){
                if(recv_msg[j]=='\0' || recv_msg[j]=='\n') break;
                printf("%c",recv_msg[j]);
            }
            printf("\n");
        }
        else if(recv_msg[0]=='2' && recv_msg[1]==' '){
            int pos=0;
            for (int j=byte_num-1; j>0; j--){
                if(recv_msg[j]=='/' || recv_msg[j]=='\\'){
                    pos=j; break;
                }
            }
            for (int j=pos+1; j<byte_num; j++){
                if(recv_msg[j]=='\0') break;
                filename+=recv_msg[j];
            }
            //取得文件名稱,準備建立新的文件
            if(fromID==0) printf("服務器");
            else printf("客戶端(%d)",fromID);
            printf("向你傳送了一個文件,請輸入您想要存放的絕對路徑,例如:/Users/longzhengtian/Desktop/\n注意,最後要有一個'/'符號,如果沒有,系統將自動填充,請輸入:");
            std::cin>>path; getchar();
            if(path[path.size()-1]!='/'){//如果沒有'/'的補救措施
                path+="/";
            }
            //filename="new_"+filename;
            path+=filename;
            FILE *fp=fopen(path.c_str(),"wb+");
            if(fp==nullptr)
                printf("接收文件出錯!(R216)\n");
            else {
                memset(recv_msg,0,sizeof(recv_msg));
                int cnt=0;
                while((cnt=recv(server_sock_fd,recv_msg,BUFFER_SIZE,0)) && cnt>0){
                    if(fwrite(recv_msg,sizeof(char),cnt,fp)<cnt){
                        printf("接收文件出錯!(R223)\n");
                        break;
                    }
                    memset(recv_msg,0,sizeof(recv_msg));
                    if(cnt!=BUFFER_SIZE) break;
                }
                printf("Receive successful!\n");
                fclose(fp);
            }
            path.clear(); filename.clear();
        }
        else if(recv_msg[0]=='3' && recv_msg[1]==' '){
            //視頻傳輸尚未完成
        }
    }
    else if(byte_num < 0){
        printf("接受消息出錯!\n");
    }
    else{
        printf("服務器端退出!\n");
        shutdown(server_sock_fd,SHUT_RDWR);
        exit(0);
    }
}
void Socket_client::receive()
{
    if (FD_ISSET(server_sock_fd, &client_fd_set)){
        memset(recv_msg,0,sizeof(recv_msg));
        long byte_num = recv(server_sock_fd,recv_msg,BUFFER_SIZE,0);
        if (byte_num > 0){
            if (byte_num > BUFFER_SIZE){
                byte_num = BUFFER_SIZE;
            }
            recv_msg[byte_num] = '\0';

            recvmessage();

            if(strcmp(recv_msg,"服務器加入的客戶端數達到最大值!\n")==0){
                exit(0);
            }
        }
        else if(byte_num < 0){
            printf("接受消息出錯!\n");
        }
        else{
            printf("服務器端退出!\n");
            shutdown(server_sock_fd,SHUT_RDWR);
            exit(0);
        }
    }
}
int main (){
    Socket_client tcpclient;
    tcpclient.init();

    if (tcpclient.connect_s()){
        while (true){
            if(!tcpclient.start()){
                continue;
            }
            else{
                if(!tcpclient.sendmessage()) exit(0);
                tcpclient.receive();
            }
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章