看板 C_and_CPP 關於我們 聯絡資訊
開發平台(Platform): (Ex: Win10, Linux, ...) Mac OS Mojave 編譯器(Ex: GCC, clang, VC++...)+目標環境(跟開發平台不同的話需列出) G++ 額外使用到的函數庫(Library Used): (Ex: OpenGL, ...) 問題(Question): 我做了一個server想接收client的指令去執行,但是我將指令傳到 server時,server無法判斷指令,所以我先做了一個小程式,把收到的指令印出來, 並判斷他是不是ls,結果還是不行 餵入的資料(Input): ls 預期的正確結果(Expected Output): 印出recevied:'ls' yes 錯誤結果(Wrong Output): server: client connected with ip address: 0.0.0.0 received: 'ls ' received: 'ls ' client: telnet 0.0.0.0 8877 Trying 0.0.0.0... Connected to 0.0.0.0. Escape character is '^]'. ls ls 程式碼(Code):(請善用置底文網頁, 記得排版,禁止使用圖檔) int main(int argc, char *argv[]) { int SERVER_PORT = 8877; struct sockaddr_in server_address; memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_port = htons(SERVER_PORT); server_address.sin_addr.s_addr = htonl(INADDR_ANY); int listen_sock; if ((listen_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { printf("could not create listen socket\n"); return 1; } if ((bind(listen_sock, (struct sockaddr *)&server_address,sizeof(server_address))) < 0) { printf("could not bind socket\n"); return 1; } int wait_size = 16; // maximum number of waiting clients, after which // dropping begins if (listen(listen_sock, wait_size) < 0) { printf("could not open socket for listening\n"); return 1; } struct sockaddr_in client_address; socklen_t client_address_len = 0; while (true) { int sock; if ((sock =accept(listen_sock, (struct sockaddr *)&client_address, &client_address_len)) < 0) { printf("could not open a socket to accept data\n"); return 1; } int n = 0; int len = 0, maxlen = 100; char buffer[maxlen]; char *pbuffer = buffer; printf("client connected with ip address: %s\n",inet_ntoa(client_address.sin_addr)); while ((n = recv(sock, pbuffer, maxlen, 0)) > 0) { pbuffer += n; maxlen -= n; len += n; printf("received: '%s'\n", buffer); string line=buffer; line=line.substr(0,n); printf("received: '%s'\n", line.c_str()); if (!strcmp(buffer,"ls")){ printf("yes"); } if (line=="ls"){ printf("yes"); } send(sock, buffer, len, 0); } close(sock); } close(listen_sock); return 0; } 補充說明(Supplement): 看起來傳回去的東西是沒問題的,但是為什麼我server收到的東西不太對。 1.右括號被換行了 2.沒有判斷出我傳的是ls(應該要印出yes) 請各位大大救救我QQ 感謝! -- ◢█◤ ╭══╮ ╭ ◢█ ◣ theanswer3 █▌╰═════════════════╯ ╰══╯ █▌ █ / ◢█◣ ◢█◣ ◢█◣ ◢█◣ ◢◢◣ ◢ ◢█◣ ◢ ◤ █▌ ◢ █▌█ ███ █▌█ █▌█ █◤ █ █◢█ ◥ ◣ ◢███◤◥█◤ ◥▌◤ ◥██ ◥█◤ █ ◤ ◥◤◥ █▌ █ / ║ ╰═════════════▅◤═════════════ ◥█ ◤ ═════╯ -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 59.127.199.39 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1541815726.A.2E0.html
bigbite: 斷行也被傳進去了, 改成strncmp(buffer,"ls", 2) 11/10 11:01
我也覺得是這樣,所以我有用一個string line去接,然後line=line.substr(0,n), 如果是這樣那感覺第二個yes應該會印出來? 然後如果我改成line=line.substr(0,n-1)那會變成印出 client connected with ip address: 0.0.0.0 received: 'ls ' 'eceived: 'ls 我無法理解為什麼右括號會蓋掉第一個字母.....
s4300026: 不是啊,你應該送的時候就把/n處理掉啊,因為你不能保 11/10 11:35
s4300026: 證n=recv會傳幾個,如果一次只傳一個怎辦? 11/10 11:35
可是我是用telnet 0.0.0.0 port這樣去連我的server,我不知道他為什麼會把/n 傳過來
s4300026: 而不是在收的地方做line的substr的處理 11/10 11:37
雖然不應該這樣處理,但是處理了還不行就更崩潰XD 還是我telnet的使用方法錯誤,我是打一行指令然後按enter傳送到server。
s4300026: 那你應該去看client送出前的字串 11/10 11:40
s4300026: 你按enter總是會有接受的buffer吧,在那buffer把/n處理 11/10 11:43
s4300026: 掉 11/10 11:43
可是我沒有寫client,我是在終端機打telnet 0.0.0.0 port直接連我的server, 而且我server echo回去的訊息看起來也沒有/n ※ 編輯: TampaBayRays (111.251.137.40), 11/10/2018 11:45:47
s4300026: 那你printf(%d %s, n,buffer) 11/10 11:51
s4300026: recv後直接印最原始的 11/10 11:52
這樣會出現 client connected with ip address: 0.0.0.0 4 ls 'eceived: 'ls 收到4個字元...? client的echo: telnet 0.0.0.0 8878 Trying 0.0.0.0... Connected to 0.0.0.0. Escape character is '^]'. ls ls
s4300026: buffer宣告時 ={} 11/10 12:05
s4300026: Is/n 另一個猜不出來 /0? 11/10 12:07
我把buffer宣告改成 char buffer[]={}; 然後就換變成 client connected with ip address: 0.0.0.0 4 lo 'eceived: 'lo client: telnet 0.0.0.0 8878 Trying 0.0.0.0... Connected to 0.0.0.0. Escape character is '^]'. ls lo 好像更慘了 求解QQ
s4300026: char buffer[maxlen] ={}; 11/10 12:19
好像不行這樣 variable-sized object may not be initialized int maxlen ※ 編輯: TampaBayRays (111.251.137.40), 11/10/2018 12:22:13
s4300026: 不是啊,你要去看你收到什麼東西,才能做處理,如果你要 11/10 12:22
s4300026: 收到什麼,就回傳什麼,那這樣寫沒問題,但是如果你想要 11/10 12:22
s4300026: 判斷收到的內容,不是應該收完再處理嗎? 也就是要跳出w 11/10 12:22
s4300026: hile再處理問題 11/10 12:22
s4300026: -2-select 11/10 12:22
我現在做的只有單一client,想要做到的事是client連上來輸入一行指令, server就回傳這行指令執行結果,然後在收下一行指令,傳回下一行指令執行結果, 直到client斷線
s4300026: 整串收完再做判斷,去看總共收到什麼,才去掉不想要的東 11/10 12:25
s4300026: 西 11/10 12:25
s4300026: 然後buffer初始化清零是我想表達的 11/10 12:26
s4300026: 重點是 recv不會一次收完全部的訊息,假設你傳 12345678 11/10 12:28
s4300026: 90,但收到的可能會是 1234、567890 11/10 12:28
1.我希望做的是有互動的感覺,所以希望是一是收一行指令 2.這個我之前有加上 memset (&buffer, 0, maxlen) ;我覺得應該是一樣的意思? 但是還是沒用 3.這個我知道,但是我才傳兩個char,而且看起來應該是有收到完整指令,如果真的 是這個問題,請問要怎麼處理呢? ※ 編輯: TampaBayRays (111.251.137.40), 11/10/2018 12:32:56
Bencrie: 你確定對方 send 會幫你送 \0 嗎 11/10 13:12
JJJJoe: 可以先 line.length() 看看長度多少,如果是 3 的話代表有 11/10 13:27
JJJJoe: 收到 \0 11/10 13:27
JJJJoe: 還有 Mac OS 預設的換行符是 \r,所以才會有蓋掉第一個字 11/10 13:28
JJJJoe: 元的情形 11/10 13:28
length是3,另外我加上了 cout<<(line.find("\0")!=string::npos)<< " "<<(line.find("\n")!=string::npos) <<(line.find("\r")!=string::npos)<<endl; 結果印出來是101 另外可以請問Mac OS預設換行符是\r就會有蓋掉第一個字元的情況發生呢? ※ 編輯: TampaBayRays (111.251.137.40), 11/10/2018 13:46:26
JJJJoe: \r 代表的是返回到該行開頭的意思,因此單引號就會回到該 11/10 13:49
JJJJoe: 行的開頭印出,就蓋掉原本的字元了 11/10 13:49
JJJJoe: 我建議使用 line=line.substr(0,line.length()-1) 來處理 11/10 13:51
JJJJoe: 話說回來 每個系統換行符不同,在其他系統 telnet 結果可 11/10 13:54
JJJJoe: 能會不一樣? 11/10 13:55
我改用line=line.substr(0,line.length()-1)結果還是一樣會印出 'eceived: 'ls line size:3 1 01 我之前是用ubuntu的telnet結果也是一樣....
JJJJoe: 你是在substr之後才print的嗎?一般來說不會有這個結果@@ 11/10 14:27
我現在的while裡面長這樣 while ((n = recv(sock, pbuffer, maxlen, 0)) > 0) { pbuffer += n; maxlen -= n; len += n; printf("%d %s", n,buffer); printf("received: '%s'\n", buffer); string line=buffer; line=line.substr(0,line.length()-1); printf("received: '%s'\n", line.c_str()); cout<<"line size:"<<line.length()<<endl; cout<<(line.find("\0")!=string::npos) <<(line.find("\n")!=string::npos) <<(line.find("\r")!=string::npos)<<endl; if (!strcmp(buffer,"ls")){ printf("yes"); } if (line=="ls"){ printf("yes"); } send(sock, buffer, len, 0); memset (&buffer, 0, maxlen) ; } 然後傳ls會印出 client connected with ip address: 0.0.0.0 4 ls received: 'ls ' 'eceived: 'ls line size:3 1 01 我發現直接印出來不會有換行,可是前後加東西一起印就會有, 難道是char[]跟string之間轉換有什麼要注意的地方嗎? 不是string=char[]直接傳就可以嗎? 還是我在一開始設定socket的時候有什麼參數設定錯了?
JJJJoe: 所以你一開始說length=3是在line=line.substr(0,n-1)還是 11/10 15:11
JJJJoe: line=line.substr(0,n)之後? 11/10 15:11
我在這裡把length印出來 cout<<"line size:"<<line.length()<<endl; 在line=line.substr(0,line.length()-1);之後
JJJJoe: 我是指我一開始問的時候,還沒有使用line.length()-1 11/10 15:15
JJJJoe: 我覺得收到的四個字元應該是 ls\r\n 才會有這種情況 11/10 15:24
line=line.substr(0,n-1)後length是3
JJJJoe: 那我猜的沒錯,line.length()-2 應該就行了 11/10 15:29
JJJJoe: 順便問一下你目前 clinet 是在哪個系統進行的? 11/10 15:30
真的可以了!!!!! 感謝大大!!!!!! 我沒有client,我是在終端機打telnet 0.0.0.0 port直接去連我的server 可是我server ehco回去的沒有\r\n,所以telnet在收到東西後會自己切掉, 可是傳的時候不會? 我的系統是Mac OS Mojave跟Ubuntu 16.04直接用終端機的telnet連都會有上面的問題
JJJJoe: 我想知道的是你是在哪個作業系統下telnet指令的,我不知道 11/10 15:44
JJJJoe: 每個作業系統有沒有差別 11/10 15:44
JJJJoe: echo 的訊息如果有換行,就表示有印出\r\n 11/10 15:44
JJJJoe: 喔喔 看來每個系統的telnet在訊息結尾都會加上\r\n 11/10 15:47
JJJJoe: 學到一課了XD 11/10 15:47
我在Mac OS跟Ubuntu的終端機用telnet連我的server,傳過去都會有\r\n, 但是echo回去印出來的都沒有換行,所以感覺是telnet再送的時候會送\r\n過來, 然後收到的時候會自己把它切掉。 終於解決這個問題了,感謝大大的幫忙!!!!! ※ 編輯: TampaBayRays (111.251.137.40), 11/10/2018 16:04:15