strcmp的坑
下面的程序是通過對輸入進行判斷,來決定輸出內容的。
# include<stdio.h>
#include<string.h>
int main(){
printf("請輸入: ");
char buf[1024];
//這裏想通過判斷輸入是否是quit來決定是否完成閱讀
if (fgets(buf, sizeof(buf), stdin) != NULL && (strcmp(buf, "quit"))){
printf("歡迎閱讀本文章\n");
for (int i = 0;i < 1024;i++){
//打印每個字符的內容
printf("%d ",buf[i]);
}
}
else{
printf("您已經閱讀完成本文了,點一下關注哈\n");
}
printf("\n");
}
但是當輸入quit的時候,沒有走else流程。運行結果居然是是第一個輸出。通過打印出來的buf中的字符來看,發現多了個10,因此buf比"quit"要大,也就是strcmp的返回結果是10,爲真,所以會走if的流程。
實戰抓包
重寫客戶端
通過上面的分析,可以看出,在這篇文章中,想通過輸入quit來關閉socket的實現是有問題的。可以通過另一個函數完美解決這個問題-strncmp。
原型:strcmp(str1,str2);
功能:比較兩個字符串,如果兩個字符串相等,則返回0;若str1大於str2(對於大於的理解,是指從兩個字符串的第一個字符開始比較,若兩個字符相同,則繼續比較,若發現兩個字符不相等,且str1中該字符的ASCII碼大於str2中的,則表示str1大於str2),返回一個正數(這個正數不一定是1);若str1小於str2,返回一個負數(不一定是-1);若字符串str1的長度大於str2,且str2的字符與str1前面的字符相同,則也相對於str1大於str2處理
原型2:strncmp(str1,str2,n);
功能2:比較兩個字符串的前n個字符
重新寫下客戶端的代碼
/*************************************************************************
> File Name: client.c
> Author: 無情劍客
> Mail: [email protected]
> Created Time: 2020年07月01日 星期三 21時44分37秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
#define PORT 6666
#define MAXDATASIZE 2048
int main(int argc, char *argv[])
{
if(argc != 2)
{
fprintf(stderr, "請您輸入ip地址!\n");
exit(1);
}
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
const char *server_ip = argv[1]; //從命令行獲取輸入的ip地址,此處沒有對ip地址進行檢驗
struct sockaddr_in serveraddr;
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(PORT);
inet_pton(AF_INET, server_ip, &serveraddr.sin_addr);
connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
printf("=====================服務器鏈接成功=====================\n");
char buf[MAXDATASIZE];
memset(buf, 0 , sizeof(buf));
printf("請輸入: ");
//判斷輸入的內容是否是quit,如果輸入quit,則關閉連接
while(fgets(buf, sizeof(buf), stdin) != NULL && (strncmp(buf, "quit", 4)))
{
send(sockfd, buf, sizeof(buf), 0);
memset(buf, 0, sizeof(buf));
recv(sockfd, buf, sizeof(buf), 0);
printf("服務器說: ");
fputs(buf, stdout);
memset(buf, 0, sizeof(buf));
printf("請輸入: ");
}
printf("客戶端將要被關閉,下次再見\n");
close(sockfd);
return 0;
}
服務器端代碼可參考這篇文章。
完整抓包
開啓wireshark,來進行一次完整的抓包。
因爲服務iqi地址是127.0.0.1。本地環回地址,所以使用Loopback:lo接口。如果不是本地的服務器,需要選擇相應的網卡接口。下面兩個圖展示了TCP的三次握手和四次揮手。