• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • UNIX下的Socket編程

    發表于:2007-07-04來源:作者:點擊數: 標簽:
    一,前言 TCP(Transfer Control Protocol)傳輸控制協議是一種面向連接的協議,客戶端和服務端的連接是可靠的, 安全 的. Sockets最早是作為BSD規范提出來的,并已成為 Unix 操作系統下TCP/IP 網絡 編程標準,但是,隨著 網絡 技術的不斷進步, Sockets的應用范圍已不
    一,前言

    TCP(Transfer Control Protocol)傳輸控制協議是一種面向連接的協議,客戶端和服務端的連接是可靠的,安全的.
    Sockets最早是作為BSD規范提出來的,并已成為Unix操作系統下TCP/IP網絡編程標準,但是,隨著網絡技術的不斷進步,
    Sockets的應用范圍已不再局限于Unix操作系統和TCP/IP網絡,但是我這次主要介紹是基于Linux上的Socket編程,但所有
    示例程序都在Sco以及Linux上編譯通過,并且運行正常.
    一般的網絡程序分為server端和client端.

    二,函數詳細介紹

    1, int socket(int domain, int type,int protocol)

    domain:說明我們網絡程序所在的主機采用的通訊協族(AF_UNIX和AF_INET等).
    其實這里指定的是地址族,在unix中協議族和地址族是一一對應的.
    AF_UNIX只能夠用于單一的Unix系統進程間通信,而AF_INET是針對Internet的,
    因而可以允許在 遠程 主機之間通信(當我們 man socket時發現 domain可選項
    是 PF_*而不是AF_*,因為glibc是posix的實現 所以用PF代替了AF,實際上查看
    sys/socket.h會發現如下定義:#define AF_INET 2 ,#define PF_INET AF_INET).

    type:我們網絡程序所采用的通訊協議(SOCK_STREAM,SOCK_DGRAM,SOCK_RAW等)
    SOCK_STREAM表明我們用的是TCP協議,這樣會提供按順序的,可靠,雙向,面向連接的比特流.
    SOCK_DGRAM 表明我們用的是UDP協議,這樣只會提供定長的,不可靠,無連接的通信.
    SOCK_RAW提供對internal network interfaces的訪問,只有特權程序才能使用。對應IP協議、ICMP協議等等。

    protocol:由于我們指定了type,所以對應了單一的protocal,所以這個地方我們一般只要用0來代替就可以了 .
    如果存在多協議,則必須明確指定.

    socket為網絡通訊做基本的準備.成功時返回一個socket號,失敗時返回-1.

    server端和client端都使用本函數.


    2, int bind(int sfd, struct sockaddr_in *addr, int len)

    sfd:是由socket調用返回的描述符即socket號.
    addr:是一個指向sockaddr_in的指針.
    len:是sockaddr結構的長度. sizeof ( struct sockaddr )


    在<linux/in.h>中sockaddr_in的定義為

    struct sockaddr_in
    {
    unsigned short sin_family;
    unsigned short int sin_port;
    struct in_addr sin_addr;
    unsigned char sin_zero[8];
    }

    sin_family一般為AF_INET,
    sin_addr設置為INADDR_ANY表示可以和任何的主機通信,
    sin_port是我們要監聽的端口號.
    sin_zero[8]暫時未用

    bind后將本地的端口同socket返回的描述符捆綁在一起.成功是返回0,失敗返回-1.

    屬于server端函數.

    3, int listen(int sfd,int qlen)

    sfd:是server方的socket號.
    qlen:設置請求排隊的最大長度.當有多個客戶端程序和服務端相連時, 使用這個表示
    可以接收的排隊長度.一般我們都寫5. listen函數將bind的文件描述符變為監聽套接字.
    成功返回0,失敗返回-1.本函數必須在aclearcase/" target="_blank" >ccept之前調用.

    屬于server端函數.

    4, int accept(int sfd, struct sockaddr_in *addr,int *len)

    sfd:是server方的socket號.
    addr,addrlen是用指針接收client端填寫的信息. 調用前都做一個初始化.調用時,
    server端的程序會一直阻塞直到到有一個client調用connect函數請求了連接. accept
    成功時返回一個非負整數表示accepted socket,這時server端可以向該描述符寫信息了.
    失敗時返回-1 .
    在并發服務器方式中,server 端fork出一個從服務器,從服務器利用調用返回的accepted
    socket與client通信。

    在面向連接的通信中,client可以不調用bind,connect會自動完成。
    在無連接通信中,客戶方必須調用bind。connect主要是為面向連接通
    信的客戶設計的,accept則完全是為面向連接通信的服務器設計的。
    connect可以用于無連接的服務器以及客戶,其作用可以代替bind,
    但比bind功能強大。

    屬于server端函數.

    5, int connect(int sfd, struct sockaddr_in *serv_addr,int addrlen)

    sfd:socke fd.
    serveraddr:儲存了server端的連接信息.描述了它的連接目的地.
    addrlen:serveraddr的長度.

    connect函數是client端連接server的.成功時返回0, 失敗時返回-1.


    6, IP和域名的轉換的一組函數

    struct hostent *gethostbyname(const char *hostname)

    struct hostent *gethostbyaddr(const char *addr,int len,int type)

    struct hostent *gethostbyname_r(const char *name, struct hostent *result,
    char *buffer, int buflen, int *h_errnop);

    struct hostent *gethostbyaddr_r(const char *addr, int length, int type,
    struct hostent *result, char *buffer,
    int buflen, int *h_errnop);


    在<netdb.h>中有struct hostent的定義
    struct hostent
    {
    char *h_name; /* 主機的名稱 */
    char *h_aliases; /* 主機的別名 */
    int h_addrtype; /* 主機的地址類型 AF_INET*/
    int h_length; /* 主機的地址長度 對于IP4 是4字節32位*/
    char **h_addr_list; /* 主機的IP地址列表 */
    }

    gethostbyname可以將機器名(如 Diablo )轉換為一個結構指針.在這個結構里面儲存了域名的信息
    gethostbyaddr可以將一個32位的IP地址(C0A80001)轉換為結構指針.

    gethostbyname()、gethostbyaddr()使用了靜態數據區,這些靜態數據區會在每次函數調用
    中都使用到,在多線程應用中使用這些函數是有問題的。

    失敗時均返回NULL 且設置h_errno,調用h_strerror()可以得到詳細錯誤信息.

    gethostbyname_r()、gethostbyaddr_r()是支持重入的版本。參數result必須是一個指向
    struct hostent的指針,該結構所用內存空間必須是調用者明確分配下來的。若成功,主
    機描述信息將返回到這個結構中。參數buffer必須指向由調用者提供的緩沖空間。buffer
    用于存放主機數據,返回值struct hostent 中所有的指針均指向存放在buffer中的數據。
    buffer必須足夠大以致能存放所有可能返回的數據。參數buflen給出buffer的字節大小。
    參數h_errnop是一個整型指針,發生錯誤時這里存放了錯誤信息。

    7, 字節轉換函數

    unsigned long int htonl(unsigned long int hostlong)
    unsigned short int htons(unisgned short int hostshort)
    unsigned long int ntohl(unsigned long int netlong)
    unsigned short int ntohs(unsigned short int netshort)

    h 代表host, n 代表 network.s 代表short l 代表long
    第一個函數的意義是將本機上的long型轉化為網絡上的long型.
    第二個函數的意義是將本機上的short型轉化為網絡上的short型.
    第三個函數的意義是將網絡上的long型轉化為本機上的long型.
    第四個函數的意義是將網絡上的short型轉化為本機上的short型.

    為什么會有這樣幾個函數呢?是因為在網絡的機器在表示數據的字節順序是不同的,為了統一起來,
    有了專門的字節轉換函數.
    其實這些函數大多數是用作關聯gethostbyname和getservent返回的地址和端口號的.

    8, IP處理系列函數

    char *inet_ntoa(struct in_addr in)

    int inet_aton(const char *cp,struct in_addr *inp)

    函數里面 a 代表 ASCII ,n 代表network.

    第一個函數是將32位網絡IP轉換為a.b.c.d的ASCII格式.

    第二個函數如果地址有效,會返回1,否則函數返回0.

    9, 服務信息函數

    int getsockname(int sockfd,struct sockaddr *localaddr,int *addrlen)
    int getpeername(int sockfd,struct sockaddr *peeraddr, int *addrlen)
    struct servent *getservbyname(const char *servname,const char *protoname)
    struct servent *getservbyport(int port,const char *protoname)


    struct servent
    {
    char *s_name; /* 正式服務名 */
    char **s_aliases; /* 別名列表 */
    int s_port; /* 端口號 */
    char *s_proto; /* 使用的協議 */
    }

    一般我們很少用這幾個函數.對應客戶端,當我們要得到連接的端口號時在connect調用成
    功后使用可得到 系統分配的端口號.對于服務端,我們用INADDR_ANY填充后,為了得到連
    接的IP我們可以在accept調用成功后 使用而得到IP地址.
    網絡上有許多的默認端口和服務,比如端口21對ftp80對應WWW.為了得到指定的端口號
    的服務 我們可以調用第四個函數,相反為了得到端口號可以調用第三個函數.

    10 , 讀寫函數

    寫函數
    ssize_t write(int sfd,const void *buf,size_t nbytes)

    write函數將buf中的nbytes字節內容寫入描述符sfd.
    成功時返回寫的字節數.失敗時返回-1. 并設置errno變量.

    1)write的返回值大于0,表示寫了部分或者是全部的數據.
    2)返回的值小于0,此時出現了錯誤.我們要根據錯誤類型來處理.
    如果錯誤為EINTR表示在寫的時候出現了中斷錯誤.
    如果為EPIPE表示網絡連接出現了問題(對方已經關閉了連接).

    讀函數
    ssize_t read(int fd,void *buf,size_t nbyte)

    read函數是負責從fd中讀取內容.當讀成功時,read返回實際所讀的字節數,
    如果返回的值是0 表示已經讀到文件的結束了,小于0表示出現了錯誤.
    如果錯誤為EINTR說明讀是由中斷引起的, 如果是ECONNREST表示網絡
    連接出了問題.

    其實大多數時候我們都是傳遞一個報文,一般我們會把一個結構memcpy到一個
    buffer中,然后將這個buffer str在網絡上傳遞.
    11 , UDP函數

    int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr * from ,int *fromlen)
    int sendto(int sockfd,const void *msg,int len,unsigned int flags,struct sockaddr *to ,int tolen)


    sockfd,buf,len的意義和read,write一樣,分別表示套接字描述符,發送或接收的緩沖區及大小.
    recvfrom負責從sockfd接收數據,如果from不是NULL,那么在from里面存儲了信息來源的情況,
    如果對信息的來源不感興趣,可以將from和fromlen設置為NULL.sendto負責向to發送信息.
    此時在to里面存儲了收信息方的詳細資料.

    12 , 高級套接字函數 之recv和send

    int recv(int sockfd,void *buf,int len,int flags)
    int send(int sockfd,void *buf,int len,int flags)

    recv和send函數提供了和read和write差不多的功能.不過它們提供了第四個參數來控制
    讀寫操作.

    前面的三個參數和read,write一樣,第四個參數可以是0或者是以下的組合

    MSG_DONTROUTE,不查找路由表
    MSG_OOB,接受或者發送帶外數據
    MSG_PEEK,查看數據,并不從系統緩沖區移走數據
    MSG_WAITALL,等待所有數據

    MSG_DONTROUTE:是send函數使用的標志.這個標志告訴IP協議.目的主機在本地網絡上面
    ,沒有必要查找路由表.這個標志一般用網絡診斷和路由程序里面.

    MSG_OOB:是send函數使用的標志.表示可以接收和發送帶外的數據.

    MSG_PEEK:是recv函數的使用標志,表示只是從系統緩沖區中讀取內容,而不清楚系統緩沖
    區的內容.這樣下次讀的時候,仍然是一樣的內容.一般在有多個進程讀寫數據時可以使用
    這個標志.

    MSG_WAITALL是recv函數的使用標志,表示等到所有的信息到達時才返回.使用這個標志的
    時候recv回一直阻塞,直到指定的條件滿足,或者是發生了錯誤.
    1)當讀到了指定的字節時,函數正常返回.返回值等于len
    2)當讀到了文件的結尾時,函數正常返回.返回值小于len
    3)當操作發生錯誤時,返回-1,且設置錯誤為相應的錯誤號(errno)

    如果flags為0,則和read,write一樣的操作.上面介紹的是我們常用的幾個參數,當然還有其它
    的幾個選項,不過我們實際上用的很少.

    13 ,高級套接字函數 之recvmsg和sendmsg


    recvmsg和sendmsg可以實現前面所有的讀寫函數的功能.
    int recvmsg(int sockfd,struct msghdr *msg,int flags)
    int sendmsg(int sockfd,struct msghdr *msg,int flags)

    struct msghdr
    {
    void *msg_name;
    int msg_namelen;
    struct iovec *msg_iov;
    int msg_iovlen;
    void *msg_control;
    int msg_controllen;
    int msg_flags;
    }
    struct iovec
    {
    void *iov_base; /* 緩沖區開始的地址 */
    size_t iov_len; /* 緩沖區的長度 */
    }

    msg_name和 msg_namelen當套接字是非面向連接時(UDP),它們存儲接收和發送方的地址
    信息.msg_name實際上是一個指向struct sockaddr的指針,msg_namelen是結構的長度.
    當套 接字是面向連接時,這兩個值應設為NULL.
    msg_iov和msg_iovlen指出接受和發送的緩沖區內容.msg_iov是一個結構指針,msg_iovlen
    指出這個結構數組的大小.
    msg_control和msg_controllen這兩個變量是用來接收和發送控制數據時的 ??
    msg_flags指定接受和發送的操作選項,和recv,send的選項一樣.

    14 ,高級套接字函數 之shutdown

    int shutdown(int sockfd,int howto)

    TCP連接是雙向的(是可讀寫的),當我們使用close時,會把讀寫通道都關閉,有時侯我們希
    望只關閉一個方向,這個時候我們可以使用shutdown.針對不同的howto,系統會采取不同
    的關閉方式.

    howto=0這個時候系統會關閉讀通道.但是可以繼續往接字描述符寫.
    howto=1關閉寫通道,和上面相反,這時候就只可以讀了.
    howto=2關閉讀寫通道,和close一樣 .

    在多進程程序里面,如果有幾個子進程共享一個套接字時,如果我們使用shutdown, 那么所
    有的子進程都不能夠操作了,這個時候我們只能夠使用close來關閉子進程的套接字描述符.


    15 ,高級套接字函數 getsockopt和setsockopt

    int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen)
    int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen)

    level指定控制套接字的層次.可以取三種值:
    1)SOL_SOCKET:通用套接字選項.
    2)IPPROTO_IP:IP選項.
    3)IPPROTO_TCP:TCP選項.


    對應的optname詳細說明
    optname指定控制的方式(選項的名稱).

    選項名稱 說明 數據類型
    ========================================================================
    SOL_SOCKET
    SO_BROADCAST 允許發送廣播數據 int
    SO_DEBUG 允許調試 int
    SO_DONTROUTE 不查找路由 int
    SO_ERROR 獲得套接字錯誤 int
    SO_KEEPALIVE 保持連接 int
    SO_LINGER 延遲關閉連接 struct linger
    SO_OOBINLINE 帶外數據放入正常數據流 int
    SO_RCVBUF 接收緩沖區大小 int
    SO_SNDBUF 發送緩沖區大小 int
    SO_RCVLOWAT 接收緩沖區下限 int
    SO_SNDLOWAT 發送緩沖區下限 int
    SO_RCVTIMEO 接收超時 struct timeval
    SO_SNDTIMEO 發送超時 struct timeval
    SO_REUSERADDR 允許重用本地地址和端口 int
    SO_TYPE 獲得套接字類型 int
    SO_BSDCOMPAT 與BSD系統兼容 int


    IPPROTO_IP
    IP_HDRINCL 在數據包中包含IP首部 int
    IP_OPTINOS IP首部選項 int
    IP_TOS 服務類型
    IP_TTL 生存時間 int

    IPPRO_TCP
    TCP_MAXSEG TCP最大數據段的大小 int
    TCP_NODELAY 不使用Nagle算法 int
    =========================================================================




    optval獲得或者是設置套接字選項.ON或者OFF等 .

    16 ,高級套接字函數 ioctl

    int ioctl(int fd,int req,...)

    ioctl可以控制所有的文件描述符的情況,這里介紹一下控制套接字的選項.

    ioctl的控制選項
    SIOCATMARK 是否到達帶外標記 int
    FIOASYNC 異步輸入/輸出標志 int
    FIONREAD 緩沖區可讀的字節數 int

    17 , 高級套接字函數 select

    int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout)

    void FD_SET(int fd,fd_set *fdset)
    void FD_CLR(int fd,fd_set *fdset)
    void FD_ZERO(fd_set *fdset)
    int FD_ISSET(int fd,fd_set *fdset)

    一般的來說當我們在向文件讀寫時,進程有可能在讀寫出阻塞,直到一定的條件滿足. 比
    如我們從一個套接字讀數據時,可能緩沖區里面沒有數據可讀(通信的對方還沒有 發送數
    據過來),這個時候我們的讀調用就會等待(阻塞)直到有數據可讀.如果我們不 希望阻塞
    ,我們的一個選擇是用select系統調用. 只要我們設置好select的各個參數,那么當文件
    可以讀寫的時候select回"通知"我們說可以讀寫了.

    readfds所有要讀的文件文件描述符的集合
    writefds所有要的寫文件文件描述符的集合
    exceptfds其他的服要向我們通知的文件描述符
    timeout超時設置.
    nfds所有我們監控的文件描述符中最大的那一個加1

    在我們調用select時進程會一直阻塞直到以下的一種情況發生.
    1)有文件可以讀.
    2)有文件可以寫.
    3)超時所設置的時間到.

    為了設置文件描述符我們要使用幾個宏.
    FD_SET將fd加入到fdset
    FD_CLR將fd從fdset里面清除
    FD_ZERO從fdset中清除所有的文件描述符
    FD_ISSET判斷fd是否在fdset集合中

    .......................................等等函數.

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>