前言:
实习刚好一个月,今天做个总结:刚来前两周没什么事,熟悉一下公司,配置一下编程环境,适应厦门的生活。后两周跟着小组的老哥们(哈哈)一边学习一边写写测试脚本和测试文档,画画业务流程图。总之,工作还没有那么繁忙,然后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;
}