下载
GitHub:
client 端:https://github.com/AmoAmoAmo/Smart_Device_Client
server端:https://github.com/AmoAmoAmo/Smart_Device_Server
另还写了一份macOS版的server,但是目前还有一些问题,有兴趣的去看看吧:https://github.com/AmoAmoAmo/Server_Mac
前言
首先我们需要明确一点的就是,为什么需要自定义socket协议?
① 客户端与服务器间的相互通信是异步的
② 理论上,二者都可以任意地发送或者接受数据
③ 但是实际上,它们应该配合:当client发送时,server接受; 当server发送时,让client去接收
④ 那么,任何让它们二者配合默契,相互协调呢?
⑤ 这就引出了我们需要的——应用底层协议来解决
⑥ 这个所谓的协议,实质上就是代码
我们都知道,TCP或者UDP的握手协议(三次握手 三次挥手之类),
不过这些协议都已经被封装在了TCP或UDP协议内部,
我们使用socket提供接口来操作底层的工作,平时不用管。
话不多说,开始敲代码吧。
UDP —— 搜索设备
1)首先,让server一直处于监听的状态,等待client来连接。这个等待是阻塞的 所以要记得把它放在子线程里
2)client主动向局域网端口发送广播包数据,INADDR_BROADCAST,广播地址是255.255.255.255
3)server收到数据包后,将设备(server)的信息(如设备ID、设备类型)打包成一个数据包,回复给client,client便可获得设备的IP地址和设备信息
server端:
-(int)startUDPSearchServiceWithBlock:(ReturnRecvDataBlock)block
{
self.returnDataBlock = block;
m_recvSignal = true;
// 1. socket
int ret = -1;
struct sockaddr_in serveraddr = {0};
m_sockfd = socket(AF_INET, SOCK_DGRAM, 0); // *** SOCK_DGRAM -> UDP ****
if (m_sockfd < 0) {
perror("sockfd error :");
return -1;
}
// 2. bind
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(MY_PORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 也可直接 = INADDR_BROADCAST
ret = bind(m_sockfd, (const struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (ret < 0) {
perror("bind error :");
return -1;
}
printf("bind success, 准备就绪\n");
// 开一个线程 去阻塞client来连接
[NSThread detachNewThreadSelector:@selector(startServiceThread) toTarget:self withObject:nil];
printf("startUDPService, socketfd = %d.......\n",m_sockfd);
return 0;
}
-(void)stopUDPService
{
m_recvSignal = false;
m_sockfd = -1;
}
-(void)startServiceThread
{
// 首先一直在阻塞等待client主动来连接
[self recvDataAndProcess];
// 3. 回复客户端
[self sendMsgtoClient];
}
// 收到数据包,开始处理它
-(void)recvDataAndProcess
{
HJ_MsgHeader msgHead;
memset (&msgHead,0,sizeof(msgHead));
if ([self recvData:(char *)&msgHead length:sizeof(msgHead)]) {
if (msgHead.controlMask==CONTROLLCODE_SEARCH_BROADCAST_REQUEST) {
NSLog(@"RECV:::::IPADDR: %s Port: %d",inet_ntoa(m_clientaddr.sin_addr),htons(m_clientaddr.sin_port));
// 回调
self.returnDataBlock(true);
}
}
}
-(BOOL)sendMsgtoClient
{
HJ_SearchReply reply;
memset (&reply,0,sizeof(reply));
int replyLen = sizeof(reply);
reply.header.controlMask = CONTROLLCODE_SEARCH_BROADCAST_REPLY;
reply.type = CAMERA_TYPE;
reply.devID = CAMERA_ID;
if ([self sendData:(char *)&reply length:replyLen]) {
return true;
}
return false;
}
-(BOOL)sendData:(char