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の接続向け操作について説明します