linux下多線程模擬TCP客戶端向服務器端發送消息數據報並解析收到的回覆報文

前言:

實習剛好一個月,今天做個總結:剛來前兩週沒什麼事,熟悉一下公司,配置一下編程環境,適應廈門的生活。後兩週跟着小組的老哥們(哈哈)一邊學習一邊寫寫測試腳本和測試文檔,畫畫業務流程圖。總之,工作還沒有那麼繁忙,然後5.1後就得返校寫論文了,工作還是挺開心的,小組組長和隊友們都挺好的,期待6月中旬返回,繼續幹,菜雞的學習之路又要開始啦!

大致流程:

1、先自己擬定幾組發送消息便於測試

2、開啓多個線程在每個線程中通過socket建立連接,併發送connect消息和pull消息

3、解析服務器端回覆的消息,根據消息頭中的消息長度字段解析json字段(需要用到jsoncpp庫)

代碼如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<istream>
#include<list>
#include<iostream>
#include<json/json.h>
#include<pthread.h>

using namespace std;

#pragma  comment(lib, "jsoncpp.lib")

#define MAXLINE 4096
#define THREADNUM 3

unsigned char respMsg[MAXLINE]; //注意接收的最大長度字節爲4096B(4KB)
enum TYPE{
    CONNTYPE,   //其值從0開始,之後依次遞增
    PULLTYPE
};
struct sockaddr_in servaddr;
enum TYPE msgType;

pthread_t thread[THREADNUM];
pthread_mutex_t mut;
int sockfd[THREADNUM];  //有多少個線程就必須要有多少個套接字
struct TheradPamter{    //線程函數參數結構體
    char *argv1;
    int sockfd;
}theradPamter[THREADNUM];

unsigned char connMsg1[] = {0x7F,0x01,0x01,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x01,0x61,0x00,0x0A,0x00,0x0A}; //最大承載數爲十個攝像頭
unsigned char connMsg2[] = {0X7F,0X01,0X01,0X00,0X00,0X00,0X00,0X02,0X00,0X00,0X00,0X08,0X00,0X02,0X00,0X01,0X00,0X0F,0X00,0X64}; //最大承載數爲一百個攝像頭
unsigned char pullMsg1[] = {0x7F,0x01,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x0A}; //對應connMsg1
unsigned char pullMsg2[] = {0x7F,0x01,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x64};
unsigned char respConn[] = {0x7f,0x01,0x01,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00};  //若回覆的連接消息與此相同,則進行後續測試,否則表示連接失敗(解析出第十三個字節)
unsigned char respCameraNum[] = {0x7f,0x01,0x04,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x0A};  //返回給業務服務器已收到10個攝像頭信息
//unsigned char threadMsgMgn[2][2] = {conn};

int SEND_MSG(int sockfd , unsigned char msg[] , enum TYPE msgType , unsigned int size){ //typedef (unsigned int) size_t 
        unsigned char temp[4];  //臨時保存消息長度,4個字節
        unsigned char* info;    //存儲消息體
        int i,j,n,len,flag;          //len爲十進制消息體長度
        int sumBytes = 0;       //接收到的總字節數
        int cameraNum = 0;      //接收到的攝像頭數
        bool ok;

	    Json::Value root;
        Json::CharReaderBuilder builder;
        Json::CharReader* reader(builder.newCharReader());
        JSONCPP_STRING errs;

        printf("send msg to server: \n");
        if ( send(sockfd,msg,size,0) < 0 ) {
			printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
			return -1;
		}
        for(i=0;i<size;i++){
            printf("%02x ",msg[i]);
        }
        printf("\n");
        n = recv(sockfd,respMsg,sizeof(respMsg),0);
        sumBytes += (n-12);
        
        //printf("the Bytes you received are : %d\n",n);
        if(n == -1){
            printf("recv msg error: %s(errno: %d)\n", strerror(errno), errno);
            return -1;
        }else{
            printf("Response head: ");
            for(i=0;i<4;i++){
                temp[i] = respMsg[i+8];
                printf("%02x ",temp[i]);
            }

            len = (temp[0]<<24)+(temp[1]<<16)+(temp[2]<<8)+temp[3];
            printf("The msg len is : %d\n",len);

            info = (unsigned char*)malloc(len*sizeof(unsigned char));   
            for(i=12;i<n;i++){
                info[i-12] = respMsg[i];
            }
            flag = i-12;
            
            printf("receive msg from server:\n");
            for(j=0;j<n;j++){
                printf("%02x ",respMsg[j]);
            }
            //printf("\n");
            //memset(respMsg, 0, sizeof(respMsg));
            if(len > (n-12)){   //對於接收信息長度大於緩衝區大小的情況,需要根據每種回覆的消息類型解析出其消息長度
                switch(msgType){    
                    case CONNTYPE:{
                        //正常連接消息長度(包括出錯的情況)都不會超過緩衝區大小,故此處可以不進行任何處理
                    }
                        break;
                    case PULLTYPE:{
                        n = n-12;
                        while(len>0){
                            //printf("\ncome on,bro...\n");
                            memset(respMsg, 0, sizeof(respMsg));    //刷新緩衝區
                            len -= n;
                            if(len>0){
                                n = recv(sockfd,respMsg,sizeof(respMsg),0);     //繼續獲取剩下的
                                for(i=0;i<n;i++){
                                    info[flag+i] = respMsg[i];
                                }
                                flag += n;
                                sumBytes += n;
                                for(j=0;j<n;j++){
                                    printf("%02x ",respMsg[j]);
                                } 
                            }
                        }
                        //解析攝像頭個數
                         ok = reader->parse((const char*)info, (const char*)info + std::strlen((const char*)info), &root,&errs);
                         cameraNum += root.size();
                         printf("\n cameraNums are : %d\n",cameraNum);
                    }
                        break;
                    default:
                        break;
                }
            }else if(len == (n-12)){    //一次就接收完畢
                switch(msgType){    
                    case CONNTYPE:{
                        //TODO
                    }
                        break;
                    case PULLTYPE:{
                        n = n-12;
                        //解析攝像頭個數
                         ok = reader->parse((const char*)info, (const char*)info + std::strlen((const char*)info), &root,&errs);
                         cameraNum += root.size();
                         printf("\n cameraNums are : %d\n",cameraNum);
                    }
                        break;
                    default:
                        break;
                }
            }else{  //len < (n-12)表示接收到的字節數超過了協議中定義的消息長度,可能接收到錯誤消息 To-do

            }
        }
        printf("\nALL RECEIVED: %d\n",sumBytes);
        memset(respMsg, 0, sizeof(respMsg));
        return 0;
} 

void *threadFun(void *args){
        TheradPamter *tp = (TheradPamter*)args;
        int thId = tp->sockfd;

        if ( (tp->sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0 ) {
		    printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
		    return 0;
	    }

	    memset(&servaddr, 0, sizeof(servaddr));
	    servaddr.sin_family = AF_INET;
	    servaddr.sin_port = htons(8801);

	    if ( inet_pton(AF_INET,tp->argv1,&servaddr.sin_addr) <= 0 ) {
		    printf("inet_pton error for %s\n", tp->argv1);
		    return 0;
	    }

	    if (connect(tp->sockfd,(struct sockaddr*) & servaddr,sizeof(servaddr)) < 0 ) {
		    //客戶端需要調用connect()連接服務器,connect和bind的參數形式一致,區別在於bind的參數是自己的地址,而connect的參數是對方的地址。connect()成功返回0,出錯返回-1
		    printf("connect error: %s(errno: %d)\n", strerror(errno), errno);
		    return 0;
	    }
        printf("connnnn\n");

        pthread_mutex_lock(&mut);   //加鎖
        connMsg1[14] += 1;      //客戶端ID+1,因爲要模擬多個客戶端連接服務器的情況
        printf("\n Client ID (msg[14]) now is :%02x\n",connMsg1[14]);
        pthread_mutex_unlock(&mut); 

        msgType = CONNTYPE;
        if(SEND_MSG(tp->sockfd,connMsg1,msgType,sizeof(connMsg1)) == 0){
            printf("connect message transport success!\n");
        }
        printf("\n");
        msgType = PULLTYPE;
        if(SEND_MSG(tp->sockfd,pullMsg1,msgType,sizeof(pullMsg1)) == 0){
            printf("pull message transport success!\n");
        }
       
        close(tp->sockfd);
        printf("close\n");
    
}

int main(int argc, char** argv) {

    int flag;   //線程創建是否成功的標誌
    int i;

    pthread_mutex_init(&mut,NULL);

	if (argc != 2) {   //命令行參數爲要連接的服務端IP地址
		printf("Usage: ./Client <ip_address>\n");
		return 0;
	}
    for(i=0;i<THREADNUM;i++){   //開啓多線程
        theradPamter[i].argv1 = argv[1];
        theradPamter[i].sockfd = i;
        if((flag = pthread_create(&thread[i],NULL,threadFun,(void *)&theradPamter[i]))!=0) {
            printf("thread %d fail to be created\n", i);
        }else{
            printf("thread %d is created success\n", i);
        }
        
    }
    for(i=0;i<THREADNUM;i++){
        if(thread[i] !=0){     
            pthread_join(thread[i],NULL);
            printf("thread %d is end !\n",i);
        }
    }
    
	pthread_mutex_destroy(&mut);
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章