1. 程式人生 > >網路IPC:套接字

網路IPC:套接字

網路程序間通訊:socket API簡介

不同計算機(通過網路相連)上執行的程序相互通訊機制稱為網路程序間通訊(network IPC)。

在本地可以通過程序PID來唯一標識一個程序,但是在網路中這是行不通的。其實TCP/IP協議族已經幫我們解決了這個問題,網路層的“ip地址”可以唯一標識網路中的主機,而傳輸層的“協議+埠”可以唯一標識主機中的應用程式(程序)。這樣利用三元組(ip地址,協議,埠)構成套接字,就可以標識網路的程序了,網路中的程序通訊就可以利用這個標誌與其它程序進行互動。

套接字是通訊埠的抽象!通過套接字網路IPC介面,程序能夠使用該介面和其他程序通訊。

幾個定義:

  1. IP地址
    :即依照TCP/IP協議分配給本地主機的網路地址,兩個程序要通訊,任一程序首先要知道通訊對方的位置,即對方的IP。
  2. 埠號:用來辨別本地通訊程序,一個本地的程序在通訊時均會佔用一個埠號,不同的程序埠號不同,因此在通訊前必須要分配一個沒有被訪問的埠號。
  3. 連線:指兩個程序間的通訊鏈路。
  4. 半相關:網路中用一個三元組可以在全域性唯一標誌一個程序:(協議,本地地址,本地埠號)這樣一個三元組,叫做一個半相關,它指定連線的每半部分。
  5. 全相關:一個完整的網間程序通訊需要由兩個程序組成,並且只能使用同一種高層協議。也就是說,不可能通訊的一端用TCP協議,而另一端用UDP協議。因此一個完整的網間通訊需要一個五元組來標識:(協議,本地地址,本地埠號,遠地地址,遠地埠號),這樣一個五元組,叫做一個相關(association),即兩個協議相同的半相關才能組合成一個合適的相關,或完全指定組成一連線。

套接字描述符

套接字是端點的抽象。與應用程序要使用檔案描述符訪問檔案一樣,訪問套接字也需要用套接字描述符。套接字描述符在UNIX系統中是用檔案描述符實現的。

要建立一個套接字,可以呼叫socket函式。

#include<sys/socket.h>
int socket(int domain, int type, int protocol);

  引數:

作用:socket()用於建立一個socket描述符(socket descriptor),它唯一標識一個socket。

網路位元組序

網路協議指定了位元組序,因此異構計算機系統能夠交換協議資訊而不會混淆位元組序。TCP/IP協議棧採用大端位元組序。應用程序交換格式化資料時,位元組序問題就會出現。對於TCP/IP,地址用網路位元組序來表示,所以應用程序有時需要在處理器的位元組序與網路位元組序之間轉換。

#include<arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

這些函式名很好記,h表示host,n表示network, l表示32位長整數,s表示16位短整數

在將一個地址繫結到socket的時候,請先將主機位元組序轉換成為網路位元組序,對主機位元組序不要做任何假定,務必將其轉化為網路位元組序再賦給socket!

將套接字與地址繫結

與客戶端的套接字關聯的地址意義不大,可以讓系統選擇一個預設的地址。然而,對於伺服器,需要給一個接收客戶端請求的套接字繫結一個眾所周知的地址。客戶端應有一種方法用以連線伺服器的地址,最簡單的方法就是為伺服器保留一個地址並且在/etc/services或某個名字服務(name service)中註冊。

  可以用bind函式來搞定這個問題:

#include <sys/types.h>        
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

引數:

第一個引數:bind()函式把一個地址族中的特定地址賦給該sockfd(套接字描述字)。例如對應AF_INET、AF_INET6就是把一個ipv4或ipv6地址和埠號組合賦給socket。 

第二個引數:struct sockaddr *指標,指向要繫結給sockfd的協議地址。這個地址結構根據地址建立socket時的地址協議族的不同而不同:

地址格式

地址標識了特定通訊域中的套接字端點,地址格式與特定的通訊域相關。為使不同格式地址能夠被傳入到套接字函式,地址需被強轉為通用的地址結構sockaddr表示。

//標頭檔案
#include<netinet/in.h>

struct sockaddr 是一個通用地址結構,該結構定義如下: 

struct sockaddr
{
   sa_family_t sa_family;
   char        sa_data[14];
}

IPV4因特網域:

//ipv4對應的是: 
/* 網路地址 */
struct in_addr 
{
    uint32_t       s_addr;     /* address in network byte order */
};

struct sockaddr_in {
    sa_family_t    sin_family;    /* address family: AF_INET */
    in_port_t      sin_port;      /* port in network byte order */
    struct in_addr sin_addr;      /* internet address */
};

IPv6因特網域:

//ipv6對應的是: 
struct in6_addr 
{ 
    unsigned char   s6_addr[16];   /* IPv6 address */ 
};

struct sockaddr_in6 
{ 
    sa_family_t     sin6_family;   /* AF_INET6 */ 
    in_port_t       sin6_port;     /* port number */ 
    uint32_t        sin6_flowinfo; /* IPv6 flow information */ 
    struct in6_addr sin6_addr;     /* IPv6 address */ 
    uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */ 
};

  Unix域對應的是: 

#define UNIX_PATH_MAX    108

struct sockaddr_un 
{ 
    sa_family_t sun_family;               /* AF_UNIX */ 
    char        sun_path[UNIX_PATH_MAX];  /* pathname */ 
};

第三個引數:addrlen 對應的是地址的長度

返回值:成功返回0,出錯返回-1

作用:將套接字與埠號繫結,即把一個ip地址和埠號組合賦給socket

點分十進位制IP與網路位元組序IP之間的轉換

有時需要打印出能被人而不是計算機所理解的地址格式。我們可以利用函式來進行二進位制地址格式與點分十進位制格式的相互轉換。但是這些函式僅支援IPv4地址。

 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 //點分十進位制IP轉換網路位元組序IP
 int inet_aton(const char *cp, struct in_addr *inp);
 //點分十進位制IP轉換網路位元組序IP
 in_addr_t inet_addr(const char *cp);
 //網路位元組序IP 轉化點分十進位制IP
 char *inet_ntoa(struct in_addr in);

其中inet_pton和inet_ntop不僅可以轉換IPv4的in_addr,還可以轉換IPv6的in6_addr,因此函式介面是void* 型別!

#include <arpa/inet.h>
//網路位元組序IP 轉化點分十進位制IP
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
//點分十進位制IP轉換網路位元組序IP
int inet_pton(int af, const char *src, void *dst);

監聽

如果作為一個伺服器,在呼叫socket()、bind()之後就會呼叫listen()來監聽這個socket,如果客戶端這時呼叫connect()發出連線請求,伺服器端就會接收到這個請求。

伺服器呼叫 listen 來宣告可以接收連線請求!

#include <sys/types.h>    
#include <sys/socket.h>
 int listen(int sockfd, int backlog);

引數:sockfd為要監聽的socket描述字,backlog為相應socket可以排隊的最大連線個數  

返回值:成功返回0,出錯返回-1

作用:socket函式建立一個套接字時,預設是一個主動套接字,listen函式把一個未呼叫connect的未連線的套接字轉換成一個被動套接字,指示核心應接收指向該套接字的連線請求。(主動/客戶 -> 被動/伺服器)

連線

如果是面向連線的網路服務,在開始交換資料前,都要在請求服務的程序套接字(客戶端)和提供服務的程序套接字(伺服器)之間建立一個連線,使用connect函式:

#include <sys/types.h>        
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

引數:第一個引數sockfd為客戶端的socket描述字,第二引數為伺服器的socket地址,第三個引數為socket地址的長度。 

返回值:成功返回0,出錯返回-1

作用:客戶端通過呼叫connect函式來建立與TCP伺服器的連線

注意:在connect中所指定的地址是想與之通訊的伺服器地址。如果sockfd沒有繫結到一個地址,connect會給呼叫者繫結一個預設地址!

使用accept函式獲得連線請求並建立連線

#include <sys/types.h>          
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

參 數 :第一個引數為伺服器的socket描述字,第二個引數為指向struct sockaddr *的指標,用於返回客戶端的協議地址,第三個引數為協議地址的長度 

返回值:如果accpet成功,那麼其返回值是由核心自動生成的一個全新的描述字,該描述符連線到呼叫connect的客戶端。這個新的套接字描述符和原始的套接字描述符具有相同的套接字型別和地址族。

注 意:傳給accept的原始套接字沒有關聯到這個連線,而是繼續保持可用狀態並接受其它連線請求!

通俗點來說,accept的第一個引數為伺服器的socket描述字,是伺服器開始呼叫socket()函式生成的,稱為監聽socket描述字;而accept函式返回的是已連線的socket描述字。一個伺服器通常通常僅僅只建立一個監聽socket描述字,它在該伺服器的生命週期內一直存在。核心為每個由伺服器程序接受的客戶連線建立了一個已連線socket描述字,當伺服器完成了對某個客戶的服務,相應的已連線socket描述字就被關閉。

資料傳輸

既然套接字端點表示檔案描述符,那麼只要建立連線,就可以使用write和read來通過套接字通訊了。

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);

write()會把指標buf所指的記憶體寫入count個位元組到引數fd所指的檔案內(檔案讀寫位置也會隨之移動),如果順利write()會返回實際寫入的位元組數。當有錯誤發生時則返回-1,錯誤程式碼存入errno中!

read()會把引數fd所指的檔案傳送nbyte個位元組到buf指標所指的記憶體中,成功返回讀取的位元組數,出錯返回-1並設定errno,如果在調read之前已到達檔案末尾,則這次read返回0 。

如果想指定多個選項、從多個客戶端接收資料包或傳送帶外資料,需要採用6個傳遞資料的套接字函式中的一個。

三個函式用來發送資料:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

 sendto()適用於已連線的資料報或流式套介面傳送資料。

引數:

  •   sockfd:一個標識套介面的描述字。
  •   buf:包含待發送資料的緩衝區。
  •   len:buf緩衝區中資料的長度。
  •   flags:呼叫方式標誌位。
  •   dest_addr:(可選)指標,指向目的套介面的地址。
  •   addrlen:所指地址的長度。

三個函式用來接收資料:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
recvfrom()函式用於從已連線的套介面上接收資料,並捕獲資料傳送源的地址。 引數:
  • sockfd:用來標識一個已連線套介面的描述字;
  • buf:接收資料緩衝區;
  • len:緩衝區長度;
  • flags:呼叫操作方式,一般情況下為0;
  • src_addr:指向裝有源地址緩衝區的指標;

關閉套接字描述符

 close函式用來關閉檔案描述符:

#include <unistd.h>
int close(int fd);

  注意:close操作只是使相應socket描述字的引用計數-1,只有當引用計數為0的時候,才會觸發TCP客戶端向伺服器傳送終止連線請求。

 地址“重用”

預設條件下,一個套接字不能與一個已在使用中的本地地址捆綁。但有時會需要“重用”地址。因為每一個連線都由本地地址和遠端地址的組合唯一確定,所以只要遠端地址不同,兩個套介面與一個地址捆綁並無大礙。為了通知套介面實現不要因為一個地址已被一個套介面使用就不讓它與另一個套介面捆綁,應用程式可在bind()呼叫前先設定SO_REUSEADDR選項。請注意僅在bind()呼叫時該選項才被解釋;故此無需(但也無害)將一個不會共用地址的套接字設定該選項,或者在bind()對這個或其他套介面無影響情況下設定或清除這一選項。

解決這個問題的方法是使用setsockopt()設定socket描述符的 選項SO_REUSEADDR為1,表示允許建立埠號相同但IP地址不同的多個socket描述符。 在server程式碼的socket()和bind()呼叫之間插入如下程式碼:

int opt=1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

基於TCP的socket通訊基本流程:

  1. TCP伺服器端依次呼叫socket()、bind()、listen()之後,就會監聽指定的socket地址了。
  2. TCP客戶端依次呼叫socket()、connect()之後就向TCP伺服器傳送了一個連線請求。
  3. TCP伺服器監聽到這個請求之後,就會呼叫accept()函式取接收請求,這樣連線就建立好了。
  4. 之後就可以開始網路I/O操作了,即類同於普通檔案的讀寫I/O操作。

建立一個基於TCP的socket API  :

伺服器:

/*************************************************************************
 > File Name: server.c
 > Author:Lynn-Zhang 
 > Mail: [email protected]
 > Created Time: Fri 29 Jul 2016 12:15:28 PM CST
 ************************************************************************/

#include<stdio.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<pthread.h>

static void usage(const char* proc)
{
    printf("Usage: %s [ip] [port]\n",proc);
}

void *thread_run(void *arg)
{
    printf("create a new thread\n");
    int fd=(int)arg;
    char buf[1024];
    while(1)
    {
        //伺服器端將套接字描述符中到資料讀到buf並列印,再將自己的回覆寫入套接字描述符
        memset(buf,'\0',sizeof(buf));
        ssize_t _s=read(fd,buf,sizeof(buf)-1);
        if(_s>0)
        {
            buf[_s]='\0';
            printf("client:# %s",buf);
            printf("server:$ ");
            fflush(stdout);
            
            //伺服器將回複寫入fd
            memset(buf,'\0',sizeof(buf));
            ssize_t _in=read(0,buf,sizeof(buf)-1);
            if(_in>=0)
            {
              buf[_in-1]='\0';
              write(fd,buf,strlen(buf));
            }
            printf("please wait ...\n");
        }
        else if(_s==0)
        {
            printf("client close...\n");
            break;
        }
        else
        {
            printf("read error ...\n");
            break;
        }
    }
    return (void*)0;
}

int main(int argc,char *argv[])
{
    //引數必須能構成完整的socket
    if(argc!=3)
    {
        usage(argv[0]);
        exit(1);
    }
    //建立伺服器端socket
    int listen_sock=socket(AF_INET,SOCK_STREAM,0);
    if(listen_sock<0)
    {
        perror("socket");
        return 1;
    }

    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(atoi(argv[2]));
    local.sin_addr.s_addr=inet_addr(argv[1]);
    
    int opt=1;
    if(setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))<0)
    {
        perror("setsockopet error\n");
        return -1;
    }

    //將套接字繫結到伺服器端的ip地址和埠號繫結
    if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0)
    {
        perror("bind");
        return 2;
    }
    //建立監聽佇列,等待套接字的連線請求
    listen(listen_sock,5);

    struct sockaddr_in peer;
    socklen_t len=sizeof(peer);
    while(1)
    {
        //獲得連線請求並建立連線
        int client_sock=accept(listen_sock,(struct sockaddr*)&peer,&len);
        if(client_sock<0)
        {
            perror("accept faild ...\n");
            return 3;
        }
        printf("get a new link,socket -> %s:%d\n",inet_ntoa(peer.sin_addr));

        pthread_t id;
        pthread_create(&id,NULL,thread_run,(void*)client_sock);

        pthread_detach(id);

//        pid_t id=fork();
//        if(id==0)
//        {//child
//            char buf[1024];
//            while(1)
//            {
//                 //將監聽到的套接子描述符指定檔案描述中的資料讀到buf中
//                memset(buf,'\0',sizeof(buf));
//                ssize_t _s=read(client_sock,buf,sizeof(buf)-1);
//                if(_s>0)
//                {
//                    buf[_s-1]='\0'
//                    printf("client:# %s\n",buf); 
//                    printf("server:$ ");
//                    fflush(stdout);
//                    memset(buf,'\0',sizeof(buf));
//                    ssize_t _s=read(0,buf,sizeof(buf)-1);
//                    if(_s>0)
//                    {                    
//                         buf[_s-1]='\0';
//                         write(client_sock,buf,strlen(buf));
//                    }
//                    else
//                    {
//                        printf("Fail !\n");
//                    }
//                }
//                else
//                {
//                      printf("read done...\n");
//                    break;
//                }
//            }
//
//        }
//        else
//        {//father
//            waitpid(-1,NULL,WNOHANG);
//        }
//
    }
    close(listen_sock);
    return 0;
}

  客戶端:

/*************************************************************************
 > File Name: client.c
 > Author:Lynn-Zhang 
 > Mail: [email protected]
 > Created Time: Fri 29 Jul 2016 09:00:01 AM CST
 ************************************************************************/

#include<stdio.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<errno.h>
#include<pthread.h>

static usage(const char* proc)
{
    printf("Usage: %s [ip] [port]\n",proc);
}

int main(int argc,char* argv[])
{
    //傳入的引數是一個完整的socket(ip地址+埠號)
    if(argc!=3)
    {
        usage(argv[0]);
        exit(1);
    }
    //建立一個套接字描述符
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        return 2;
    }
    //IPv4因特網域(AF_INET)中,套接字地址用sockaddr_in表示
    struct sockaddr_in remote;
    remote.sin_family=AF_INET;   //socket通訊域
    remote.sin_port=htons(atoi(argv[2]));   //埠號
    remote.sin_addr.s_addr=inet_addr(argv[1]);  //ip地址
    //請求連線
    int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote));
    if(ret<0)
    {
        printf("connect failed ... ,errno is :%d,errstring is: %s\n",errno,strerror(errno));
        return 3;
    }
    printf("connect success ...\n");
    char buf[1024];
    while(1)
    {
        //從標準輸入將資料讀入buf中,再寫入sock中
        memset(buf,'\0',sizeof(buf));
        printf("client:# ");
        fflush(stdout);
        ssize_t _s=read(0,buf,sizeof(buf)-1);
        fflush(stdin);
        if(_s<0)
        {
            perror("read\n");
            break;
        }
        buf[_s]='\0';
        write(sock,buf,strlen(buf));
        if(strcmp(buf,"quit")==0)
        {
            printf("quit!\n");
            break;
        }

        _s=read(sock,buf,sizeof(buf));
        if(_s>0)
        {
            buf[_s]='\0';
            printf("server:$ %s\n",buf);
        }
    }
    close(sock);
    printf("sock close");
    return 0;
}

  伺服器: 

客戶端:

 基於UDP協議的Socket程式設計

注意:

UDP沒有建立連線的過程!

建立一個基於udp協議的套接字,使用socket函式時第二個引數不能傳遞SOCK_STREAM,而是傳遞SOCK_DGRAM 

如建立一個基於IPv4地址族的UDP套接字: socket(AF_INET,SOCK_DGRAM, 0);

通常用於基於UDP協議的I/O一般使用 recvfrom 和 sendto 兩個函式進行資料收發!

 伺服器:

/*************************************************************************
 > File Name: server.c
 > Author:Lynn-Zhang 
 > Mail: [email protected]
 > Created Time: Wed 03 Aug 2016 01:14:30 PM CST
 ************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>

void usage(const char* proc)
{
    printf("Usage: %s [ip] [port]\n",proc);
}
int main(int argc,char* argv[])
{
    //要求輸出配套到套接字
    if(argc!=3)
    {
        usage(argv[0]);
        return 1;
    }
    //建立套接字描述符
    int sock=socket(AF_INET,SOCK_DGRAM,0);
    if(sock<0)
    {
        perror("socket");
        return 2;
    }
    
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(atoi(argv[2]));
    local.sin_addr.s_addr=inet_addr(argv[1]);
    //將套接字與地址繫結 
    int ret=bind(sock,(struct sockaddr*)&local,sizeof(local));
    if(ret<0)
    {
        perror("bind");
        return 3;
    }
    int done=0;
    struct sockaddr_in peer;
    socklen_t len=sizeof(peer);
    char buf[1024];

    while(!done)
    {
        memset(buf,'\0',sizeof(buf));
        recvfrom(sock,buf,sizeof(buf),0,(struct sockaddr*)&peer,&len);
        printf("##########################\n");
        printf("get a client , socket:%s:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));
        printf("client:%s,echo client!\n",buf);
        printf("##########################\n");
printf("server:");
        fflush(stdout);
        memset(buf,'\0',sizeof(buf));
        ssize_t _s= read(0,buf,sizeof(buf)-1);
        buf[_s]='\0';
        sendto(sock,buf,sizeof(buf),0,(struct sockaddr*)&peer,len);
    }
    return 0;
}

  客戶端:

/*************************************************************************
 > File Name: client.c
 > Author:Lynn-Zhang 
 > Mail: [email protected]
 > Created Time: Wed 03 Aug 2016 03:48:21 PM CST
 ************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>

static void usage(const char* proc)
{
    printf("Usage:%s [remote_ip] [remote_port]\n",proc);
}

int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        usage(argv[0]);
        return 1;
    }

    int sock=socket(AF_INET,SOCK_DGRAM,0);
    if(sock<0)
    {
        perror("socket");
        return 2;
    }

    struct sockaddr_in remote;
    remote.sin_family=AF_INET;
    remote.sin_port=htons(atoi(argv[2]));
    remote.sin_addr.s_addr=inet_addr(argv[1]);

    int done=0;
    char buf[1024];
    struct sockaddr_in peer;
    socklen_t len=sizeof(peer);
    while(!done)
    {
        printf("Please Enter: ");
        fflush(stdout);
        ssize_t _s=read(0,buf,sizeof(buf)-1);
        if(_s>0)
        {
            buf[_s-1]='\0';
            sendto(sock,buf,sizeof(buf),0,(struct sockaddr*)&remote,sizeof(remote));
            
            memset(buf,'\0',sizeof(buf));
            recvfrom(sock,buf,sizeof(buf),0,(struct sockaddr*)&peer,&len);
            printf("server echo %s\nsocket: %s:%d\n",buf,inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));
        }
    }
    return 0;
}

 執行結果:

伺服器:

客戶端:

部分參考:

吳秦   http://www.cnblogs.com/skynet/

《Unix 環境高階程式設計》

相關推薦

網路IPC

網路程序間通訊:socket API簡介 不同計算機(通過網路相連)上執行的程序相互通訊機制稱為網路程序間通訊(network IPC)。 在本地可以通過程序PID來唯一標識一個程序,但是在網路中這是行不通的。其實TCP/IP協議族已經幫我們解決了這個問題,網路層的“ip地址”可以唯一標識網路中的主機,而

UNIX再學習 -- 網路IPC

一、基本概念1、程式設計介面什麼是伯克利套接字(Berkeley Socket)?美國加利福尼亞大學比克利分校於 1983年釋出 4.2 BSD Unix 系統。其中包含一套用 C 語言編寫的應用程式開

UNIX網路程式設計卷1聯網API 詳解筆記

UNIX網路程式設計卷1:套接字聯網API 詳解筆記 第一部分:簡介和TCP/IP 1.Socket 套接字 sockfd = socket(AF_INET(網際流), SOCK_STREAM, 0); 2.*bind(int fd, const struct sockaddr sa,

Linux網路程式設計——原始例項MAC 頭部報文分析

通過《Linux網路程式設計——原始套接字程式設計》得知,我們可以通過原始套接字以及 recvfrom( ) 可以獲取鏈路層的資料包,那我們接收的鏈路層資料包到底長什麼樣的呢? MAC 頭部(有線區域網) 注意:CRC、PAD 在組包時可以忽略 鏈路層資料包的其中一

Linux 網路程式設計——原始例項MAC 地址掃描器

如果 A (192.168.1.1 )向 B (192.168.1.2 )傳送一個數據包,那麼需要的條件有 ip、port、使用的協議(TCP/UDP)之外還需要 MAC 地址,因為在乙太網資料包中 MAC 地址是必須要有的。那麼怎樣才能知道對方的 MAC 地址?答案是:它通

Linux 網路程式設計——原始例項傳送 UDP 資料包

乙太網(Ethernet)報文格式(MAC頭部報文格式): IP 報文格式: UDP 報文格式: 校驗和函式: /******************************************************* 功能:     

《Unix網路程式設計》卷1聯網API(第3版)簡介、傳輸層、程式設計

全書共31章+附錄。 計劃安排:吃透這本書,一天三章+原始碼,並實測程式碼做當天筆記,CSDN見。時間安排:計劃時間1.5個月 == 6個週末 == 12天。 2017.08.05    第01-03章:TCP/IP簡介、傳輸層、套接字程式設計簡介2017.08.06  

《TCP/IP網路程式設計》第一章理解網路程式設計和 筆記

第一章:理解網路程式設計和套接字 本章程式碼,在TCP-IP-NetworkNote中可以找到。 1.1 理解網路程式設計和套接字 1.1.1構建打電話套接字 以電話機打電話的方式來理解套接字。 呼叫 socket 函式(安裝電話機)時進行的對話: 問:接

socket網路程式設計複習筆記(三)描述符背後的祕密

1.套接字概念回顧 (1)套接字是一個識別符號; (2)套接字是一個結構; (3)套接字是一個包含標識、資料和操作的複合體,是服務訪問點。 我們已經知道,一個套接字描述符s實際上是一個整形資料,在winsock.h標頭檔案中,SOCKET是這樣被定義的:

網路程式設計——原始實現原理

目錄 1. 基礎知識  1.1、概述 1.2、鏈路層原始套接字  1.3、網路層原始套接字 2、原始套接字的實現 2.1  原始套接字報文收發流程 2.2鏈路層原始套接字的實現     2.2.1  套接字建

Linux網路程式設計——原始程式設計

原始套接字的建立 int socket ( int family, int type, int protocol ); 引數: family:協議族 這裡寫 PF_PACKET type:  套接字類,這裡寫 SOCK_RAW protocol:協議類別,指定可以接收或傳送的資料包型別,不能寫

嵌入式Linux網路程式設計,網路基礎,socket(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW),IP地址,埠號,位元組序,位元組序轉換函式,IP地址的轉換

文章目錄 1,socket 1.1,socket的型別(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW) 1.2,socket的位置 2,IP地址 2.1,特殊IP地址: 3,埠號

網路程式設計——1. 理解網路程式設計和

1.1 理解網路程式設計和套接字 網路程式設計和套接字概要 1)首先需要物理連線,如今大部分計算機都已經連線到網際網路,因此不用擔心這一點 2)資料傳輸軟體。作業系統會提供名為“套接字”的部件,套接字是網路資料傳輸用的軟體裝置。 構建接電話套接字 1)安裝電

易學筆記-第1章socket/1.1 服務端socket/1.1.2 sockaddr_in地址族結構體

易學筆記 十年IT經驗個人學習筆記分享: 開發語言:C/C++/JAVA/PYTHON/GO/JSP WEB架構:Servlets/springMVC/springBoot/springClound 容器架構:Docker容器/Docker叢集/Docker與微服務整合/

易學筆記-第1章socket/1.1 服務端socket/1.1.3 位元組序

易學筆記 十年IT經驗個人學習筆記分享: 開發語言:C/C++/JAVA/PYTHON/GO/JSP WEB架構:Servlets/springMVC/springBoot/springClound 容器架構:Docker容器/Docker叢集/Docker與微服務整合/

解決VMware虛擬機器報錯“無法連線MKS連線嘗試次數太多,正在放棄”

1.錯誤描述 在VMware中開啟虛擬機器時報錯: “無法連線MKS:套接字連線嘗試次數太多,正在放棄” 物理機作業系統: Windows 7 虛擬機器作業系統: Kali Linux

虛擬機器出現錯誤“無法連線MKS連線嘗試次數太多,正在放棄“,無法進入系統的解決方法

開發環境:   我在windows上使用的是VMware Workstation 14 Pro+Ubuntu16.04的組合進行開發。 原因:  自從將vm從12pro升級成了14pro之後就出現了這種問題,應該是14pro版本本身還有些問題吧。 解決方法:  

Windows Socket 網路程式設計(二) —— 程式設計原理

 一、客戶機/伺服器模式在TCP/IP網路中兩個程序間的相互作用的主機模式是客戶機/伺服器模式(Client/Server model)。該模式的建立基於以下兩點:1、非對等作用;2、通訊完全是非同步的。客戶機/伺服器模式在操作過程中採取的是主動請示方式:首先伺服器方要先啟動

Linux網路程式設計 tcp程式碼

本次介紹一下TCP協議下的套接字程式碼,總體來看,tcp協議比udp協議更加安全可靠,無論是從使用者使用的角度還是從編寫程式碼的角度,你會發現與udp不同的是tcp在每次通訊前,伺服器端和客戶端都會進行一次連線,連線成功後,才可以進行相互間的通訊。 套接字

Linux網路程式設計之選項設定

轉自 http://blog.csdn.net/chenjin_zhong/article/details/7268939 1.介紹 在Linux網路程式設計中,有時需要設定地址複用,允許傳送廣播包,將主機加入某個多播組,設定傳送與接收緩衝區的大小,設定傳送與接收的超