计算器服务端/客户端
生活随笔
收集整理的這篇文章主要介紹了
计算器服务端/客户端
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
通常情況下我們無法預知接收數據的長度,那么我們必須定義應用層協議
服務端程序
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 1024 #define OPSZ 4// void error_handling(char *message); int calculate(int opnum, int opnds[], char oprator); int main(int argc, char *argv[]) {int serv_sock, clnt_sock;char opinfo[BUF_SIZE];int result, opnd_cnt, i;//int recv_cnt, recv_len; //struct sockaddr_in serv_adr, clnt_adr;socklen_t clnt_adr_sz;if (argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}serv_sock = socket(PF_INET, SOCK_STREAM, 0);if (serv_sock == -1)error_handling("socket() error");memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)error_handling("bind() error");if (listen(serv_sock, 5) == -1)error_handling("listen() error");clnt_adr_sz = sizeof(clnt_adr);for (int i = 0; i < 5; i++){opnd_cnt = 0;//有5個客戶端連接請求需要按序處理,所以每次處理完一個請求后,在準備接受下一個請求時需要清空這個變量clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);read(clnt_sock, &opnd_cnt, 1);//先調用read函數從緩存中先讀取一個字節,即首先接收帶算數個數recv_len = 0;//(opnd_cnt * OPSZ+ 1)是計算連接過來的客戶端它發送的報文一共有多少個字節,接受的數據量小于這個數,就循環調用read函數,直到讀取完一個完整報文while ((opnd_cnt * OPSZ + 1) > recv_len)//根據待算數個數接收待算數,若有2個待算數就占8個字節了{recv_cnt = read(clnt_sock, &opinfo[recv_len], BUF_SIZE - 1);//buf_SIZE-1確保一次性讀取完整的一行recv_len += recv_cnt;}//opinfo:"\f\000\000\000\f\000\000\000+"12result = calculate(opnd_cnt, (int *)opinfo, opinfo[recv_len - 1]);//calculate函數處理獲取到的報文信息,recv_len就相當于數組大小write(clnt_sock, (char *)&result, sizeof(result));//ssize_t write(int fd,const void*buf,size_t count);也就是我把得到的整型結果轉化為字符串close(clnt_sock);}close(serv_sock);return 0; }int calculate(int opnum, int opnds[], char op) {//opnds:"\12\12\43"int result = opnds[0], i;switch (op){case '+':for (i = 1; i < opnum; i++)result += opnds[i];break;case '-':for (i = 1; i < opnum; i++)result -= opnds[i];break;case '*':for (i = 1; i < opnum; i++)result *= opnds[i];break;}return result; } void error_handling(char *message) {fputs(message, stderr);fputc('\n', stderr);exit(1); }客戶端程序
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 1024 //TCP報文段最大長度 #define RLT_SIZE 4 //將待算數字的字節數和運算結果的字節數設為常數 #define OPSZ 4 void error_handling(char *message);int main(int argc, char *argv[]) {int sock;//為收發數據準備的內存空間,需要數據累積到一定程度后再收發,因此通過**數組**創建。大小1024char opmsg[BUF_SIZE];int result, opnd_cnt, i;struct sockaddr_in serv_adr;if (argc != 3){printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock = socket(PF_INET, SOCK_STREAM, 0);if (sock == -1)error_handling("socket() error");memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = inet_addr(argv[1]);serv_adr.sin_port = htons(atoi(argv[2]));if (connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)error_handling("connect() error!");elseputs("Connected...........");//1、和printf的區別是會自動輸出換行符/*從程序用戶的輸入中得到待算數個數后,保存至數組opmsg。強制轉換成char類型, 因為協議規定待算數個數應通過1字節整數型傳遞,因此不能超過1字 節整數型能夠表示的范圍。該示例中用的是有符號整數型,但待算數個數不能是負數,因此使用無符號整數型更合理。*/fputs("Operand count: ", stdout);//2、相當于向屏幕輸出一行提示性信息,不會自動換行scanf("%d", &opnd_cnt); //3、用戶從鍵盤上輸入帶計算數字的個數,保存到opnd_cnt中opmsg[0] = (char)opnd_cnt; //4、由于這個待算個數信息要傳送給服務端,所以要存放到報文數組opmsg中。但要注意我們的協議之一是:以一字節整數形式傳遞待算數字個數,要進行類型轉換,才能存進去for (i = 0; i < opnd_cnt; i++) //5、循環接收帶計算的數字{ //從程序用戶的輸入中得到待算整數,保存到數組opmsg。4字節int型數據要保存到char數組,//因而轉換成int指針類型。若不太理解此部分,應單獨復習指針。//假如接收到12,向char型數組保存的化要進行類型轉換,也就是說我這個整數要占用char型數組4個字節printf("Operand %d: ", i + 1); scanf("%d", (int *)&opmsg[i * OPSZ + 1]);//6、協議之二:客戶端向服務端傳遞的每個整數占用4個字節。從當前位置開始開辟4個字節空間用來存放整數,+1是因為第0位被占用存放了一個整數}fgetc(stdin); //7、下面程序中需輸入字符,在此之前調用fgetc函數刪掉緩沖中的字符'\n'。fputs("Operator: ", stdout);scanf("%c", &opmsg[opnd_cnt * OPSZ + 1]);//8、接著傳遞運算符,運算符信息占用1字節,由于運算個數不確定,但可以確定的是運算符最后所在的位置。所以要用到opnd_cnt//調用write函數一次性傳輸opmsg數組中的運算相關信息。可以調用1次write函數進行傳輸,也可以分成多次調用。//這是因為TCP中不存在數據邊界write(sock, opmsg, opnd_cnt * OPSZ + 2);//保存服務器端傳輸的運算結果。待接收的數據長度為4字節,因此調用1次read函數即可接收。read(sock, &result, RLT_SIZE);//由于服務器以4字節整數型向客戶端傳遞數據,所以客戶端要明確指出要接收4字節數據printf("Operation result: %d \n", result);close(sock);//接收到結果后關閉socket連接return 0; }void error_handling(char *message) {fputs(message, stderr);fputc('\n', stderr);exit(1); }
從圖中可以看出,若想在同一數組中保存并傳輸多種數據類型,應把數組聲明為char類型。
而且需要額外做一些指針及數組運算。
總結
以上是生活随笔為你收集整理的计算器服务端/客户端的全部內容,希望文章能夠幫你解決所遇到的問題。