airkiss server
使用udp broadcast
实现,包括两部分
udp client
广播airkiss
编码数据udp server
接收device
广播的random
数据
socket type
常用的三种类型
SOCK_STREAM
流式的套接字可以提供可靠的、面向连接的通讯流SOCK_DGRAM
数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错SOCK_RAW
原始套接字主要用于一些协议的开发,可以进行比较底层的操作
关系如下
其中SOCK_STREAM (TCP)
、SOCK_DGRAM (UDP)
工作在传输层,SOCK_RAW
工作在网络层。SOCK_RAW
可以处理ICMP
、IGMP
等网络报文、特殊的IPv4
报文、可以通过IP_HDRINCL
套接字选项由用户构造IP
头。
setsocket 指定 interface
socket
发送数据默认根据路由表来发送,如果要指定interface
可以使用setsocket
选项SO_BINDTODEVICE
来实现
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "wlan0");
if ((rc = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) < 0) {
perror("Server-setsockopt() error for SO_BINDTODEVICE");
printf("%s\n", strerror(errno));
close(sock);
exit(-1);
}
需要注意测试时,
server
与client
要选定相同的interface
,否则不再一个子网中,导致测试失败
参考文章:setsockopt 的 SO_BINDTODEVICE 套接口选项
- 对于
TCP 套接口
、UDP 套接口
、RAW 套接口
,可以通过SO_BINDTODEVICE 套接口选项
将套接口绑定到指定的网络接口上。绑定之后,套接口的所有数据包收发都只经过指定的网络接口 - 对于
PACKET 类型的套接口
,不能通过SO_BINDTODEVICE
绑定到指定的网络接口上,而要通过bind(2)
来与特定的网络接口绑定,所用的套接口地址结构为struct sockaddr_ll
,此套接口地址结构是链路层的地址结构,独立于具体的网络设备。比如,该地址结构既可以用于表示PPP 设备
,也能用于表示ethernet 设备
broadcast
广播地址255.255.255.255
,在头文件netinet/in.h
中定义为INADDR_BROADCAST
/* Address to accept any incoming messages. */
#define INADDR_ANY ((unsigned long int) 0x00000000)
/* Address to send to all hosts. */
#define INADDR_BROADCAST ((unsigned long int) 0xffffffff)
INADDR_ANYA
表示地址为0.0.0.0
,表示不确定地址、任意地址或所有地址。也就是表示本机的所有IP
,因为有些机子不止一块网卡,多网卡的情况下,这个就表示所有网卡IP
地址的意思INADDR_BROADCAST
表示广播地址255.255.255.255
,仅用于本地连接,如果不指定interface
,根据路由表确定
设置广播属性
/* Set socket to allow broadcast */
broadcastPermission = 1;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (void *) &broadcastPermission,
sizeof(broadcastPermission)) < 0) {
printf("setsockopt() SO_BROADCAST failed\n");
}
udp broadcast
/* Create socket for sending/receiving datagrams */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
printf("socket() failed\n");
}
/* Set socket to allow broadcast */
broadcastPermission = 1;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (void *) &broadcastPermission,
sizeof(broadcastPermission)) < 0) {
printf("setsockopt() SO_BROADCAST failed\n");
}
struct sockaddr_in broadcastAddr;
char *buffer = NULL;
buffer = malloc(length);
memset(buffer, 'a', length);
/* Construct local address structure */
memset(&broadcastAddr, 0, sizeof(broadcastAddr)); /* Zero out structure */
broadcastAddr.sin_family = AF_INET; /* Internet address family */
broadcastAddr.sin_addr.s_addr = INADDR_BROADCAST; /* Broadcast IP address */
broadcastAddr.sin_port = htons(BROADCAST_PORT); /* Broadcast port */
sendto(s_akHandler.sock_fd, buffer, length, 0, (struct sockaddr *)&broadcastAddr, sizeof(broadcastAddr));
bind
TCP
通信时,server
必须bind
自己本机地址和端口。client
不需要blind
UDP
通信时与TCP
一样
bind
不是server
专属,一般情况下client
是不用调用bind
的,一切都交给内核搞定!
server
需要bind
的原因
因为服务器是时时在监听有没有客户端的连接,如果服务器不绑定 IP 和端口的话,客户端上线的时候怎么连到服务器呢,所以服务器要绑定 IP 和端口,而客户端就不需要了,客户端上线是主动向服务器发出请求的,因为服务器已经绑定了 IP 和端口,所以客户端上线的就向这个 IP 和端口发出请求,这时因为客户开始发数据了(发上线请求), 系统就给客户端分配一个随机端口,这个端口和客户端的 IP 会随着上线请求一起发给服务器,服务收到上线请求后就可以从中获起发此请求的客户的 IP 和端口,接下来服务器就可以利用获起的 IP 和端口给客户端回应消息了
server
需要bind
:port
和ip addr
port
包括公共端口和私有端口ip addr
目的是限制了服务端进程创建的 socket 只接受那些目的地为此 IP 地址的客户链接,一般server
使用servaddr.sin_addr.s_addr = htonl(INADDR_ANY)
,表示不指定client ip
,来者不拒
数据收发时限
struct timeva timeout;
timeout.tv_sec=5;
timeout.tv_usec=0;
// 接受时限
setsockopt(serversocket, SQL_SOCKET,SO_RCVTIMEO, (char*)&timeout,sizeof(timeout));
// 发送时限
setsockopt(serversocket, SQL_SOCKET,SO_SNDTIMEO, (char*)&timeout,sizeof(timeout));
Example
udp server
#include <stdio.h> // Default System Calls
#include <stdlib.h> // Needed for OS X
#include <string.h> // Needed for Strlen
#include <sys/socket.h> // Needed for socket creating and binding
#include <netinet/in.h> // Needed to use struct sockaddr_in
#include <time.h> // To control the timeout mechanism
#define EXPR_SIZE 1024
#define BUFLEN 512
#define TRUE 1
#define SERVERLEN 1024
int main(int argc, char **argv){
struct sockaddr_in myaddr; // address of the server
struct sockaddr_in claddr; // address of the client
char buf[BUFLEN];
int fd;
long recvlen;
socklen_t clientlen;
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
perror("cannot create socket");
return 0;
}
memset((char *)&myaddr, 0, sizeof(myaddr));
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
myaddr.sin_port = htons(0);
if(bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0){
perror("cannot bind");
return 0;
}
clientlen = sizeof(claddr);
while (TRUE) {
recvlen = recvfrom(fd, buf, BUFLEN, 0, (struct sockaddr *)&claddr, &clientlen);
if (recvlen < 0) {
perror("cannot recvfrom()");
return 0;
}
printf("Received %ld bytes\n",recvlen);
buf[recvlen] = 0;
printf("Received message: \"%s\"\n",buf);
}
return 0;
}
udp client
使用bind
版本
#include <stdio.h> // Default System Calls
#include <stdlib.h> // Needed for OS X
#include <string.h> // Needed for Strlen
#include <sys/socket.h> // Needed for socket creating and binding
#include <netinet/in.h> // Needed to use struct sockaddr_in
#include <time.h> // To control the timeout mechanism
#define EXPR_SIZE 1024
#define BUFLEN 512
#define TRUE 1
#define FALSE 0
#define SERVERLEN 1024
int main(int argc, char **argv){
long portNum; // Since it's possible to input a value bigger
// than 65535 we'll be using long to
// avoid overflows
char expr[EXPR_SIZE];
char server[SERVERLEN];
int fd; // file descriptor for the connected socket
int buf[512];
struct hostent *h; // information of the host
unsigned int addrLen; // address length after getting the port number
struct sockaddr_in myaddr; // address of the client
struct sockaddr_in servaddr; // server's address
unsigned int exprLen;
socklen_t slen = sizeof(servaddr);
printf("Enter server name or IP address:");
scanf("%s",server);
printf("Enter port:");
scanf("%ld",&portNum);
if ((portNum < 0) || (portNum > 65535)) {
printf("Invalid port number. Terminating.");
return 0;
}
printf("Enter expression:");
scanf("%s",expr);
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
perror("cannot create socket");
return 0;
}
memset((char *)&myaddr, 0, sizeof(myaddr));
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
myaddr.sin_port = htons(0);
if(bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0){
perror("cannot bind");
return 0;
}
/*
// Discovering the port number the OS allocated
addrLen = sizeof(myaddr);
if(getsockname(fd, (struct sockaddr *)&myaddr, &addrLen) < 0){
perror("cannot getsockname");
return 0;
}
printf("local port number = %d\n", ntohs(myaddr.sin_port));
*/
memset((char*)&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htonl(portNum);
exprLen = sizeof(expr);
while(TRUE){
printf("Sending message to %s port %ld\n",server, portNum);
if (sendto(fd, expr, strlen(expr), 0, (struct sockaddr *)&servaddr, slen) < 0) {
perror("cannot sendto()");
}
printf("Success\n");
}
return 0;
}
不使用bind
版本
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<netdb.h>
#include<stdarg.h>
#include<string.h>
#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
int main()
{
/* 服务端地址 */
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
//server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(SERVER_PORT);
/* 创建 socket */
int client_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (client_socket_fd < 0) {
perror("Create Socket Failed:");
exit(1);
}
/* 输入文件名到缓冲区 */
char file_name[FILE_NAME_MAX_SIZE + 1];
bzero(file_name, FILE_NAME_MAX_SIZE + 1);
printf("Please Input File Name On Server:\t");
scanf("%s", file_name);
char buffer[BUFFER_SIZE];
bzero(buffer, BUFFER_SIZE);
strncpy(buffer, file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));
/* 发送文件名 */
if (sendto(client_socket_fd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Send File Name Failed:");
exit(1);
}
close(client_socket_fd);
return 0;
}