Linuxネットワークプログラミング(1)
8943 ワード
Preview
授业の要求、だからUNIXのネットのプログラミングを学んで、先生はとても简単だと言って、実际に手を出してからやっとこの中の関系がそんなに简単ではないことに気づきました.CS:APP 11章からのネットワークプログラミングに加え、
man
をひっきりなしに行い、現在の学習をまとめ、ついでに報告した.参考ブログ安利この重要な参考、外国のプログラマーの習慣かもしれませんが、この大神ネタは次々と出てきて、本当にawesomeを話しています.
Some Important Data Structures
socketプログラミングを入手して、最初に整理しなければならないのはいくつかのデータ構造で、出典はCS:APPです
sockaddr
ソケットアドレスとはIP:PORTのように組み合わせられた構造を指し,UNIXではsockaddrという構造で記録されている.注意しなければならないのは、sockaddrはより汎用的な概念であり、クラスの概念クラス比を借りると、実際には、彼のサブクラスを便利に使用し、関数の汎用性を保証するために設計されただけであり、従来のCではvoidポインタが定義されていないため、このデータ構造が設計されている.この概念を用いて我々のネットワークプログラミングに用いたのは,次のいくつかのセクションで述べたデータ構造である.
struct sockaddr{
unsigned short sa_family; // address family, AF_XXX
char sa_data[14]; // 14 bytes of protocol addresss
};
sa_familyは住所(A)家庭(F)を指す.例えばAF_INET(IPv4), AF_INET(IPv6)
そしてsa_dataとは、ソケットアドレス、すなわちIP:PORTを指し、フォーマットされる.
sockaddr_in
上のsockaddrには明らかなトラブルがありますsa_dataのフォーマットは操作するプログラマーにとって非常に友好的ではないので,次の細分化が行われる.
IPv 4で使用されるデータ構造
struct sockaddr_in{
short int sin_family; // address family, here is AF_INET
unsigned short int sin_port; // Port
struct in_addr sin_addr; // Internet address
unsigned char sin_zero[8]; // Seted to keep the same size as struct sockaddr
};
ここではsockaddrを細分化し,プログラマもソケットアドレスのデータ構造を容易に操作できる.
そのうちsin_familyはAF_INET(これはIPV 4を記録するデータ構造です).sin_portはネットワークバイトシーケンスに変換する必要があるため、
htons()
が役に立ちます.歴史的要因、sin_addrは
union
の構造で、ブログを参考にして彼を歴史上の災難と呼んだことがある.sin_zeroは数を数えるために使用され、このデータ構造のメモリサイズはsockaddrと一致します.in_addr
ここではsockaddr_を単独で述べる必要があるinで述べたsin_addr,in_addrの定義は以下の通りです.
struct in_addr{
uint32_t s_addr;
};
sockaddr_in6
IPV 6に対応するデータ構造
struct sockaddr_in6{
u_int16_t sin6_family;
u_int16_t sin6_port;
u_int32_t sin6_flowinfo;
struct in6_addr sin6_addr;
u_int32_t sin6_scope_id;
};
新しく記入したflow特性とscopeはIPV 6の新しい要素と関係があり、ここでは先に木を植え、その後知識がそろって、ここを補完します.
in6_addr
ここではsockaddr_に対応inのin_addr
struct in6_addr{
unsigned char s6_addr;
};
sockaddr_storage
これは互換性があり、IPV 4とIPV 6のアドレスですが、困ったことに、彼はsockaddrのsizeよりも大きいです.
struct sockaddr_storage {
sa_family_t ss_family; // address family
// all this is padding, implementation specific, ignore it:
char __ss_pad1[_SS_PAD1SIZE];
int64_t __ss_align;
char __ss_pad2[_SS_PAD2SIZE];
};
addrinfo
以上の前置データ構造の詳細を紹介し、addrinfo構造を紹介し、その後の学習と結びつけて、実はaddrinfoは「ツールマン」の役割を果たしているが、このツールマンはほとんどhuman readableのソケットアドレスなどの概念とコンピュータのフォーマット、ネットワークのプログラムで利用されるデータ構造の重要なメディアである.
struct addrinfo{
int ai_flags; // AI_PASSIVE, AI_CANONNAME, etc.
int ai_family; // AF_INET, AF_INET6, AF_UNSPEC(both 4&6)
int ai_socktype; // SOCK_STREAM, SOCK_DGRAM
int ai_protocol; // use 0 for "any"
size_t addrlen; // sizeof ai_addr in bytes
struct sockaddr * ai_addr; //important information of addrinfo
char *ai_canonname;
struct addrinfo *ai_next;
}
そのうちai_family, ai_socktype, ai_protocolはsocket関数の第1、2、3つのパラメータです.
他の部分もそれぞれ重要な応用があります
How to manipulate these data structures?
ここでは、人間が読むことができるソケットアドレス式を、機械的に理解され、フォーマットされたデータに変換し、上記の独自のデータ構造に埋め込む方法について、ネットワークプログラミング関数の部分に入ります.
Port Number
ポートの変換はバイト順の変換にあり、この部分はhtons()とhtonl()の2つの関数を借りて入力先ポート番号を変換した後、sockaddr_に値を割り当てる必要があります.in(またはsockaddr_in 6)のsin_port(またはsin 6_port)
IP Address
主に機械可読と人間可読の転化である.
まず、2つのサンプル変数を定義して説明します.
struct sockaddr_in sa;
struct sockaddr_in6 sa6;
人間が機械で読むことができる関数は
inet_pton(AF_INET, "10.12.110.57", &(sa.sin_addr));
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr));
ここで、関数の1番目のパラメータは、IPV 4であるかIPV 6であるかを判定する、2番目がIPアドレスである、ここではドメイン名のフォーマットではなく、数字に小数点を加えた形式で書かなければならず、結果はsaに格納される.sin_addr(or sa 6.sin 6_addr)にあります.ptonは簡単に「printable to network」と表記できます
機械可読から人間可読に変換するには,変換後の文字列の最大長を加える必要がある.
前の例
// IPv4:
char ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string
struct sockaddr_in sa; // pretend this is loaded with something
inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("The IPv4 address is: %s
", ip4);
// IPv6:
char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 sa6; // pretend this is loaded with something
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("The address is: %s
", ip6);
INET_ADDRSTRLEN, INET6_ADDRSTTRLENは、使いやすく設定された2つのマクロです.
getaddrinfo()
実際のネットワークプログラミングでは、各データ構造の各具体的な項目を手動で入力するのは本当に人間に反することであり、エラー確率も増加しています.
so getaddrinfo()のいくつかのカラムメソッドを使用して、安全で便利です.
#include
#include
#include
int getaddrinfo(const char *host, // e.g. "www.example.com" or IP
const char *service, // e.g. "http" or port number
const struct addrinfo *hints,
struct addrinfo **res);
ここで、hostはドメイン名またはIPアドレスです.サービスはサービスまたはポート番号であり、サービスは自動的に対応する周知ポートに変換され、/etc/servicesファイルにはすべての周知ポート情報が表示されます.
以下では、ソケットアドレスをどのように変換するかを複数の状況を分析することができます.
Server Address Convert
サーバに関してはテンプレート式のものが以下の通りであり、これはTCPに対して
int status;
struct addrinfo hints;
struct addrinfo *servinfo; // will point to the results, is a linklist
memset(&hints, 0, sizeof(hints)); // make sure hints is clean
hints.ai_family= AF_UNSPEC
hints.ai_socktype= SOCK_STREAM; // TCP stream sockets
hints.ai_flags= AI_PASSIVE; // fill in my IP for me
if (0!= (status= getaddrinfo(NULL, "3498", &hints, &servinfo))){
fprintf(stderr, "getaddrinfo error: %s
", gai_strerror(status));
return EXIT_FAILURE;
}
注意ここでgetaddrinfo()パラメータの設定:NULL.そしてhintsの設定、まずクリア、ai_ファミリーとaisocktypeが必要声明を行った後、AI_PASIVEでは、自機のIPアドレスを手動で入力する必要はありません.もちろん、IPアドレスを手動で入力することもできます.
もう一つの方法はマクロINADDR_を用いた.ANY(IN 6 ADDR_ANY)、直接sin_を設定addr.s_addr(sin6_addr.s6_addr).
Client Address Convert
対応するクライアントTCPの例
int status;
struct addrinfo hints;
struct addrinfo *servinfo;
memset(&hints, 0, sizeof(hints));
hints.ai_family= AF_UNSPEC;
hints.ai_socktype= SOCK_STREAM;
if (0!= (status= getaddrinfo("www.example.net", "3490", &hints, &servinfo))){
fprintf(stderr, "getaddrinfo error: %s
", gai_strerror(status));
return EXIT_FAILURE;
}
次の例示的なプログラムは、
nslookup
に類似したドメイン名変換と見なすことができる./*
** showip.c -- show IP addresses for a host given on the command line
*/
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,"usage: showip hostname
");
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s
", gai_strerror(status));
return 2;
}
printf("IP addresses for %s:
", argv[1]);
for(p = res;p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s
", ipver, ipstr);
}
freeaddrinfo(res); // free the linked list
return 0;
}
この記事では、次のblogでTCPの接続向け操作について説明します