1 Socket編程-udp服務端和客戶端通信
(1).建立一個套接字(Socket)
(2).綁定服務器端IP地址及端口號--服務器端
(3).通過SendTo()方法向指定主機發送消息
(需提供主機IP地址及端口)
(4).通過ReciveFrom()方法接收指定主機發送的消息
2 UDP信息傳遞的方式分三類
① 單播Unicast:是客戶端與服務器之間的點到點連接。
② 廣播BroadCast:主機之間“一對所有”的通訊模式,廣播者可以向網絡中所有主機發送信息。廣播禁止在Internet寬帶網上傳輸(廣播風暴)。
③ 多播MultiCast:主機之間“一對一組”的通訊模式,也就是加入了同一個組的主機可以接受到此組內的所有數據。
這裏需要注意的是:只有UDP纔有廣播、組播的傳遞方式;而TCP是一對一連接通信。多播的重點是高效的把同一個包儘可能多的發送到不同的,甚至可能是未知的設備。但是TCP連接是一對一明確的,只能單播。
3 配置LWIP
3.1 打開lwipopts.h中IGMP組播功能
#define LWIP_IGMP 1
3.2 將以太網驅動stm32_eth.c中的void ETH_StructInit(ETH_InitTypeDef *ETH_InitStruct)函數中進行如下設置。
ETH_InitStruct->ETH_ReceiveAll = ETH_ReceiveAll_Enable;//全部接收
ETH_InitStruct->ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_None; //不多播幀過濾
3.3 修改在ethernetif.c
中low_level_init
函數添加NETIF_FLAG_IGMP
#if LWIP_IGMP
/* igmp support */
netif->flags |= NETIF_FLAG_IGMP;
#endif
4 編寫組播實現代碼
UDP組播的基本步驟
- 建立socket
- socket和端口綁定
- 加入一個組播組
- 通過sendto / recvfrom進行數據的收發
- 關閉socket
服務器和客戶端必須都要加入相同的組播地址纔可以。
#define MCAST_ADDR "233.0.0.6"
#define LOCAL_ADDR "192.168.2.198"
#define MCAST_PORT 9090
#define MCAST_CLIENT_PORT 8080
void MulticastClient()
{
int sock = -1;
struct sockaddr_in local_addr, remote_addr;
int recv_data_len;
char* recv_data;
socklen_t addrlen;
int err = -1;
char sendline[1024];
while(1)
{
recv_data = (char *)rt_malloc(1024);
if(recv_data == NULL)
{
printf("No memory\r\n");
//return;
goto __exit;
}
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock <0)
{
printf("UDP Socket error\r\n");
goto __exit;
}
memset(&local_addr, 0 , sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(MCAST_PORT);
#if 1
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
#else
local_addr.sin_addr.s_addr = inet_addr(LOCAL_ADDR);
#endif
#if 1
err = bind(sock, (struct sockaddr*)&local_addr, sizeof(local_addr));
if(err<0)
{
printf("bind error\r\n");
goto __exit;
}
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(MCAST_ADDR);
#if 0
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
#else
mreq.imr_interface.s_addr = inet_addr(LOCAL_ADDR);
#endif
err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if(err < 0)
{
printf("2 setsockopt():IP_ADD_MEMBERSHIP error\r\n");
goto __exit;
}
#endif
while(1)
{
memset(recv_data, 0, RECV_DATA);
recv_data_len = recvfrom(sock, recv_data, RECV_DATA, 0, (struct sockaddr*)&remote_addr, &addrlen);
printf("receive from UDP server:%s\r\n", inet_ntoa(remote_addr.sin_addr));
printf("recevie:%s\r\n", recv_data);
sprintf(sendline, "hello world\r\n");
int send_length = 0;
send_length = sendto(sock, sendline, sizeof("hello server!\r\n"), 0,
(struct sockaddr*)&remote_addr, sizeof(remote_addr));
rt_thread_delay(100);
if(send_length < 0)
{
printf("sendto() error!\r\n");
}
else
{
printf("sendto() ok %d!\r\n", send_length);
}
}
__exit:
if(sock >= 0) closesocket(sock);
if(recv_data) rt_free(recv_data);
}
}
5 測試工具和測試結果
參考鏈接:UDP之多播/組播