计算器服务端/客户端
生活随笔
收集整理的這篇文章主要介紹了
计算器服务端/客户端
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
通常情況下我們無法預(yù)知接收數(shù)據(jù)的長度,那么我們必須定義應(yīng)用層協(xié)議
服務(wù)端程序
#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);//先調(diào)用read函數(shù)從緩存中先讀取一個字節(jié),即首先接收帶算數(shù)個數(shù)recv_len = 0;//(opnd_cnt * OPSZ+ 1)是計算連接過來的客戶端它發(fā)送的報文一共有多少個字節(jié),接受的數(shù)據(jù)量小于這個數(shù),就循環(huán)調(diào)用read函數(shù),直到讀取完一個完整報文while ((opnd_cnt * OPSZ + 1) > recv_len)//根據(jù)待算數(shù)個數(shù)接收待算數(shù),若有2個待算數(shù)就占8個字節(jié)了{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函數(shù)處理獲取到的報文信息,recv_len就相當于數(shù)組大小write(clnt_sock, (char *)&result, sizeof(result));//ssize_t write(int fd,const void*buf,size_t count);也就是我把得到的整型結(jié)果轉(zhuǎn)化為字符串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 //將待算數(shù)字的字節(jié)數(shù)和運算結(jié)果的字節(jié)數(shù)設(shè)為常數(shù) #define OPSZ 4 void error_handling(char *message);int main(int argc, char *argv[]) {int sock;//為收發(fā)數(shù)據(jù)準備的內(nèi)存空間,需要數(shù)據(jù)累積到一定程度后再收發(fā),因此通過**數(shù)組**創(chuàng)建。大小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的區(qū)別是會自動輸出換行符/*從程序用戶的輸入中得到待算數(shù)個數(shù)后,保存至數(shù)組opmsg。強制轉(zhuǎn)換成char類型, 因為協(xié)議規(guī)定待算數(shù)個數(shù)應(yīng)通過1字節(jié)整數(shù)型傳遞,因此不能超過1字 節(jié)整數(shù)型能夠表示的范圍。該示例中用的是有符號整數(shù)型,但待算數(shù)個數(shù)不能是負數(shù),因此使用無符號整數(shù)型更合理。*/fputs("Operand count: ", stdout);//2、相當于向屏幕輸出一行提示性信息,不會自動換行scanf("%d", &opnd_cnt); //3、用戶從鍵盤上輸入帶計算數(shù)字的個數(shù),保存到opnd_cnt中opmsg[0] = (char)opnd_cnt; //4、由于這個待算個數(shù)信息要傳送給服務(wù)端,所以要存放到報文數(shù)組opmsg中。但要注意我們的協(xié)議之一是:以一字節(jié)整數(shù)形式傳遞待算數(shù)字個數(shù),要進行類型轉(zhuǎn)換,才能存進去for (i = 0; i < opnd_cnt; i++) //5、循環(huán)接收帶計算的數(shù)字{ //從程序用戶的輸入中得到待算整數(shù),保存到數(shù)組opmsg。4字節(jié)int型數(shù)據(jù)要保存到char數(shù)組,//因而轉(zhuǎn)換成int指針類型。若不太理解此部分,應(yīng)單獨復(fù)習指針。//假如接收到12,向char型數(shù)組保存的化要進行類型轉(zhuǎn)換,也就是說我這個整數(shù)要占用char型數(shù)組4個字節(jié)printf("Operand %d: ", i + 1); scanf("%d", (int *)&opmsg[i * OPSZ + 1]);//6、協(xié)議之二:客戶端向服務(wù)端傳遞的每個整數(shù)占用4個字節(jié)。從當前位置開始開辟4個字節(jié)空間用來存放整數(shù),+1是因為第0位被占用存放了一個整數(shù)}fgetc(stdin); //7、下面程序中需輸入字符,在此之前調(diào)用fgetc函數(shù)刪掉緩沖中的字符'\n'。fputs("Operator: ", stdout);scanf("%c", &opmsg[opnd_cnt * OPSZ + 1]);//8、接著傳遞運算符,運算符信息占用1字節(jié),由于運算個數(shù)不確定,但可以確定的是運算符最后所在的位置。所以要用到opnd_cnt//調(diào)用write函數(shù)一次性傳輸opmsg數(shù)組中的運算相關(guān)信息。可以調(diào)用1次write函數(shù)進行傳輸,也可以分成多次調(diào)用。//這是因為TCP中不存在數(shù)據(jù)邊界write(sock, opmsg, opnd_cnt * OPSZ + 2);//保存服務(wù)器端傳輸?shù)倪\算結(jié)果。待接收的數(shù)據(jù)長度為4字節(jié),因此調(diào)用1次read函數(shù)即可接收。read(sock, &result, RLT_SIZE);//由于服務(wù)器以4字節(jié)整數(shù)型向客戶端傳遞數(shù)據(jù),所以客戶端要明確指出要接收4字節(jié)數(shù)據(jù)printf("Operation result: %d \n", result);close(sock);//接收到結(jié)果后關(guān)閉socket連接return 0; }void error_handling(char *message) {fputs(message, stderr);fputc('\n', stderr);exit(1); }
從圖中可以看出,若想在同一數(shù)組中保存并傳輸多種數(shù)據(jù)類型,應(yīng)把數(shù)組聲明為char類型。
而且需要額外做一些指針及數(shù)組運算。
總結(jié)
以上是生活随笔為你收集整理的计算器服务端/客户端的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。