UDPによるデータの受信
(プログラムの概要)
ソケットを作成し、データを受信する簡単なサンプルプログラムを作成します。 |
/*
* sample program
* UDP Server program
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#define UDPMAX 0xffff
static void help(void);
static int set_udp_socket(int, unsigned long);
int main(int argc ,char *argv[])
{
char buf[UDPMAX] = {0};
int opt = 0, sockfd = 0, port = 50000, loop = 1, len = 0;
struct sockaddr_in from;
socklen_t addrlen;
const struct option longopt[] = {
{"help", 0, 0, 'h'},
{"port", 1, 0, 'p'},
{0, 0, 0, 0},
};
/* get command option */
while((opt = getopt_long(argc, argv, "hHp:P:", longopt, NULL)) != -1){
switch(opt){
case 'p':
case 'P':
port = atoi(optarg);
break;
case 'h':
case 'H':
case '?':
help();
return 0;
}
}
if((port <= 0) || (port > 65535)){
help();
return -1;
}
/* create socket */
sockfd = set_udp_socket(port, 0);
if(sockfd < 0){
return -1;
}
while(loop){
addrlen = sizeof(struct sockaddr_in);
len = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&from, &addrlen);
if(len < 0){
perror("recvfrom error");
break;
}else if(addrlen == 0){
continue; /* unidentified address */
}
fprintf(stdout, "received data(%dbyte) from %s:%d\n",
len, inet_ntoa(from.sin_addr), ntohs(from.sin_port));
fprintf(stdout, "%s", buf);
if(strncmp(buf, "quit\n", 5) == 0){
loop = 0;
}
memset(buf, 0, len);
}
close(sockfd);
return 0;
}
static void help(void)
{
fprintf(stdout,
"Usage: udpserver [-p port]\n"
);
return;
}
static int set_udp_socket(int portnum, unsigned long ipaddr)
{
int sockfd;
struct sockaddr_in server;
/* create socket */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0){
perror("socket error");
return -1;
}
/* set socket address information */
memset(&server, 0, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
if(ipaddr == 0){
server.sin_addr.s_addr = htonl(INADDR_ANY);
}else{
server.sin_addr.s_addr = htonl(ipaddr);
}
server.sin_port = htons(portnum);
/* bind socket */
if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0){
perror("bind error");
close(sockfd);
return -1;
}
return sockfd;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
(プログラムの概要)
プログラム起動時の引数でポート番号を取得し、ソケットを作成し、指定したポート番号へのデータ到着を見張れるようにします。その後受信ループに入り、受信したデータを標準出力に表示します。
33L~49L:オプション分析getopt_long()はコマンドラインオプションを解釈します。詳しくはgetoptのmanpageを参照してください。getopt_long()はgetopt()にプラスして、--で始まるオプションを解釈できるものです。
起動オプションを分析し、-pオプションがあればポート番号として値を取得します。-pオプションが無い場合はデフォルト値としてポート番号は50000になります。
52L~55L:ソケットの作成サブルーチン(詳細は下記参照)
57L~74L:受信ループ
recvfromはパケットが到着するまでスリープし、パケットが到着するとバッファに読み込みます。5番目の引数に送信元の情報が入れられるので、それを表示しています。 set_udp_socket()
ソケットを作成し、bindでは「第2引数のアドレス、ポート番号は、第1引数のソケットが接続を受け付けます。」という登録を行います。今回は引数ipaddrが0なのでINADDR_ANY(loや複数のNICがあれば全てのNICからの受信を受け付けます。)
(コンパイルと動作)
gcc -Wall -o sv udpserver.c
本プログラムを起動した状態で、前回作成した送信プログラムを動作させると、送信プログラムで入力したデータがサーバプログラムに表示されます。サンプルプログラムではrecvfrom()を使ってデータが到着するまで待ちました、1つのポートを受信専用にするのであれば効率が良いのですが、1スレッドで2つのソケットを見張りたい場合があります。次回は少し受信処理を拡張します。