Linux 應用開發----socket編程筆記

Linux socket編程

套接字定義描述


套接字的域


  • AF_INET ====>IPv4
  • AF_INET6 ====>IPv6
  • AF_UNIX ====>unix 域
  • AF_UPSPEC ====>未使用

套接字的類型

  • SOCK_DGRAM ====>固定長度,無連接的,不可靠的報文傳遞
  • SOCK_RAM ====>IP協議數據報接口
  • SOCK_SEQPACKET====>固定長度,有序,可靠的,面向鏈接的報文傳遞
  • SOCK_STREAM ====>有序的,可靠的,雙向的,面向鏈接的字節流

協議類型

  • IPPROTO_IP ====>IPV4協議族
  • IPPROTO_IPV6 ====>IPV6協議族
  • IPPROTO_ICMP ====>控制報文協議
  • IPPROTO_RAM ====>原始IP數據包
  • IPPROTO_TCP ====>字節流傳輸控制協議
  • IPPROTO_UDP ====>用戶數據包協議

API

頭文件

#include <sys/socket.h>

字節序

以太網中的字節序是大端的,所以相關的以太網的數據在小端機器上須要使用相關接口轉換爲以太網字節序。經常使用轉換工具函數:linux

//4字節整形變量主機字節序的轉換爲網絡字節序
uint32_t htonl(uint32_t hostint32);
//2字節整形變量主機字節序的轉換爲網絡字節序
uint16_t htons(uint15_t hostint16);
//和上面相反
uint32_t ntohl(uint32_t netint32);
//和上面相反
uint32_t ntohl(uint32_t netint32);

經常使用接口(具體使用man 2 xxx 命令查看具體說明)

//建立套接字
int socket(int domain,int type,int protocol);
//將一個套接字和指定地址和端點關聯起來
int bind(int sockfd,const struct sockaddr * addr,socklen_t addrlen);
//開始監聽這個套接字
int listen(int socket, int backlog);
//向指定socket發起鏈接請求
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//接收監聽到的一個socket鏈接請求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//經過socket 發送數據,地址是socket默認
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//經過socket 接收數據,地址爲socket 默認
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//經過socket 發送數據,地址是由參數指定
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
//經過socket 接收數據,地址由參數指定
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
//經常使用在unix 域下的進程通信,發送數據
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
//經常使用在unix 域下的進程通信,接收數據
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
//獲取當前socket信息
int getsockname((int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//獲取原端socket信息(創建鏈接以後)
int getpeername((int sockfd, const struct sockaddr *addr, socklen_t addrlen);

關閉套接字

套接字關閉能夠像關閉一個文件的描述符同樣調用close()接口來關閉socket接口,除此以外還可使用shutdown接口進行更加細緻的關閉操做:編程

sockfd :sock描述符
how    :SHUT_RDWR 讀寫兩端都關閉
       : SHUT_WR 關閉寫端
       : SHUT_RD 關閉讀端
返回值  :成功返回0 失敗返回-1
int shutdown(int sockfd,int how);

相關結構體

//描述網絡信息
struct sockaddr_in {
s_add.sin_family = AF_INET;
s_add.sin_port   = htons(1040);
s_add.sin_addr.s_addr = inet_addr("127.0.0.1");
};

TCP服務端配置過程

  • 創建套接字,返回套接字
    //創建 IPv4域下的字節流服務的TCP協議的socket
    int sockfd_s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  • 將套接字和指定地址關聯,使用上述的結構體描述端口和地址信息。
    //將這個socket個特定地址何端口關聯起來
    bind(sockfd_s,(struct sockaddr*)&s_add,sizeof(struct sockaddr_in))
  • 告訴系統套接字刻意監聽連接。
    //開始監聽
    listen(sockfd_s,1)
  • 接受一個鏈接,這一步將返回新的套接字接口
    //接受任意鏈接請求,返回能夠用來數據傳輸的socket描述符
    accept(sockfd_s,NULL,NULL);

TCP客戶端配置過程

  • 創建套接字,返回套接字
    //創建 IPv4域下的字節流服務的TCP協議的socket
    int sockfd_s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  • 鏈接到指定地址何端口
    //向制定地址和端口發起連接請求
    connect(sockfd_c,(struct sockaddr*)&c_add,sizeof(c_add))

示例代碼

見文末api

鏈接信息獲取

獲取本地套接字信息
獲取遠端套接字信息(鏈接後)網絡

套接字選項

  • 選項 類型(非特殊爲int型) 描述
  • SO_ACCEPTCONN 查詢套接字是否能夠被監聽
  • SO_BROADCAST 非0 廣播數據報
  • SO_DEBUG 非0 啓用調試
  • SO_ERROR 返回掛起套接字的錯誤並清除
  • SO_KEEPALIVE 非0 啓用keep-alive報文
  • SO_OOBINLINE TCP 帶外數據
  • SO_RCVBUF 獲取接收緩衝區的數據
  • SO_RCVLOWAT 接受調用返回的最小字節數
  • SO_RCVTIMEO struct timeval 接收調用的超時時間
  • SO_REUSEADDR 重用地址,有用
  • SO_SNDBUF 發送buf中的字節數
  • SO_SNDLOWAT 發送傳送的最小字節數
  • SO_SNDTIMEO struct timeval 發送超時時間
  • SO_TYPE 套接字類型標識,僅get可用
設置選項
int setsockopr(int sockfd,int level,int option,void *val,socklen_t len);
獲取選項
int setsockopr(int sockfd,int level,int option,void *val);

帶外數據

是一些通信協議支持的可選功能,與普通數據相比其具備更高的優先級被傳輸。目前TCP的支持帶外數據傳輸的,僅支持一個字節的緊急數據。有些實現TCP的帶外數據還能夠產生SIGURG信號。還能夠經過下面的接口判斷是否有緊急數據,當有緊急數據時,返回1。dom

iny sockatmark(int sockfd);

異步socket和非阻塞socket

基本上socket的異步IO和阻塞和非阻塞的處理和普通的文件基本相同,可是有一些區別的是,可是目前尚未完整的標準,視具體平臺的實現狀況。異步

示例demo

int main(int argc,int *argv)
    {
        char *name,i;
        struct hostent*  host;
        size_t len = sysconf(_SC_HOST_NAME_MAX);
        if(len>0){
            name = malloc(len+1);
        }else{
            name = malloc(1024);
            len = 1024;
        }
        printf("Buff size%ld\n",stdout->_IO_buf_end-stdout->_IO_buf_base);
        if(gethostname(name,len)<0){
            printf("gethostname error!\n");
        }
        host = gethostbyname(name);
        if(*host->h_aliases){
            printf("%s\n%s\n%s\n%d\n",
            host->h_name,//host name		
            *host->h_aliases, //host_name aliases
            (host->h_addrtype==AF_INET?"IP_V4":"IP_V6"),	
            host->h_length);
        }else{
            printf("%s\n%s\n%d\n",
            host->h_name,		
            (host->h_addrtype==AF_INET?"IP_V4":"IP_V6"),	
            host->h_length);	
        }
        for(i=0;host->h_addr_list[i] != NULL;i++){
            printf("%s\n",inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
        }
        printf("befor the fork call\n\n");
        int pid = fork();
        if(pid){
            int status,temp=1;
            int sockfd_s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
            if(sockfd_s<0){
                goto fail;
            }
            if(setsockopt(sockfd_s,SOL_SOCKET,SO_REUSEADDR,&temp,sizeof(int)) < 0)
            {
                perror("setsockopt");
            }
            struct sockaddr_in s_add;
            s_add.sin_family = AF_INET;
            s_add.sin_port   = htons(1040);
            s_add.sin_addr.s_addr = inet_addr("127.0.0.1");
            // inet_pton(AF_INET,"127.0.0.1",&s_add.sin_addr);
            if(bind(sockfd_s,(struct sockaddr*)&s_add,sizeof(s_add))<0){
                printf("1\n");
                goto fail;
            }
            if(listen(sockfd_s,1)<0){
                goto fail;
            }
            int c_fd = accept(sockfd_s,NULL,NULL);
            if(c_fd < 0){
                goto fail;
            }
            int len=sizeof(s_add);
            if(getpeername(c_fd,(struct sockaddr*)&s_add,&len)<0){
                printf("2\n");
                goto fail;
            }
            printf("server:\n%s\n%d\n",inet_ntoa(s_add.sin_addr),ntohs(s_add.sin_port));
            if(c_fd){
                while(1){
                    char buf[64];
                    recv(c_fd,buf,64,0);
                    printf("%s\n",buf);
                    sleep(1);
                }
            }
        fail:
            close(sockfd_s);
            perror(strerror(errno));
            kill(pid,9);
            waitpid(pid,&status,0);
            printf("server end\n");
            return 0;
        }else if(!pid){
            sleep(3);
            int sockfd_c = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
            if(sockfd_c<0){
            goto fail_c;
            }
            struct sockaddr_in c_add;
            c_add.sin_family = AF_INET;
            c_add.sin_port   = htons(1040);
            c_add.sin_addr.s_addr = inet_addr("127.0.0.1");
            // inet_pton(AF_INET,"127.0.0.1",&c_add.sin_addr);
            if(connect(sockfd_c,(struct sockaddr*)&c_add,sizeof(c_add))){
                goto fail_c;
            }
            struct sockaddr_in d_add;
            int len=sizeof(d_add);

            if(getpeername(sockfd_c,(struct sockaddr*)&d_add,&len)<0){
                goto fail_c;
            }
            printf("cliens\n%s\n%d\n",inet_ntoa(d_add.sin_addr),ntohs(d_add.sin_port));
            while(1)
            {
                send(sockfd_c,"hello word!\n",strlen("hello word!\n"),0);
                sleep(1);
            }
            fail_c:
            close(sockfd_c);
            perror(strerror(errno));
            _exit(0);
        }
        return 0;
    }
相關文章
相關標籤/搜索