前言:
實習剛好一個月,今天做個總結:剛來前兩週沒什麼事,熟悉一下公司,配置一下編程環境,適應廈門的生活。後兩週跟着小組的老哥們(哈哈)一邊學習一邊寫寫測試腳本和測試文檔,畫畫業務流程圖。總之,工作還沒有那麼繁忙,然後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;
}