Linux TCP Socket程式分析

來源:互聯網
上載者:User

/************************

c語言編寫的tcp socket通訊的server端。

可以持續監聽myprot指定的連接埠

列印連接埠接收到的字元流

標頭檔因為角括弧被轉義,所以用了引號

************************/

#include "stdio.h"

#include "stdlib.h"
#include "errno.h"
#include "string.h"

#include "sys/types.h"

#include "netinet/in.h"
#include "sys/socket.h"
#include "sys/wait.h"

int main(int argc,char **argv)
{
    int sockfd, new_fd;
    struct sockaddr_in my_addr;
    struct sockaddr_in their_addr;
    unsigned int sin_size,myport,listnum;
    myport = 9785;  //綁定的連接埠號碼
    listnum = 10;
    /*************************************************
     Socket介面:是TCP/IP網路的API,Socket介面定義了許多的函數,可以
                 在此基礎上開發Internet上的TCP/IP網路編程
    
     Create Socket: int socket(int domain, int type, int protoco);
    
     Argument Description:domain 指明所有協議族,通常是PF_INET(TCP/IPV4)
                          當然他也可以支援IPV6,和更多的網路通訊協定,根據
                          具體的應用來選擇
                          type 分SOCK_STREAM(TCP),SOCK_DGRAM(UDP),SOCK_RAW
                          (允許程式使用底層協議)
                          protolol 通常賦值“0”
   
    Return Value: Socket描述符是一個指向內部資料結構的指標,它指向描述符表
                    入口。調用Socket函數時,socket執行體將建立一個Socket,
                    實際上"建立一個Socket"意味著為一個Socket資料結構分配存
                    儲空間。Socket執行體為你管理描述符表。
    **************************************************/
    if((sockfd = socket(PF_INET,SOCK_STREAM,0)) == -1 )
    {
        perror("socket is error/n;");
        exit(1);
    }
    my_addr.sin_family = PF_INET;           //指定協議族

    /***************************************************
      電腦資料存放區有兩種位元組優先順序:高位位元組優先和低
      位位元組優先。Internet上資料以高位位元組優先順序在網路
      上傳輸,所以對於在內部是以低位位元組優先方式儲存資料
      的機器,在Internet上傳輸資料時就需要進行轉換,否則
      就會出現資料不一致。
      htonl():把32位值從主機位元組序轉換成網路位元組序
      htons():把16位值從主機位元組序轉換成網路位元組序
      ntohl():把32位值從網路位元組序轉換成主機位元組序
      ntohs():把16位值從網路位元組序轉換成主機位元組序
    ****************************************************/
    my_addr.sin_port = htons(myport);      //如果填入0,系統將隨機播放一個連接埠
   
    my_addr.sin_addr.s_addr = INADDR_ANY;  //填入本機IP地址
    bzero(&(my_addr.sin_zero),0);

    /**************************************************
      int   bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
      Description:建立socket,並綁定該連接埠,申明已經使用,別人不會再佔用該連接埠

      Argument Description: sockfd:是Socket系統調用返回的socket 描述符
                            my_addr:需要綁定在通訊端上的地址,
                                     是類似於以下結構體的變數
                            struct sockaddr {
                                                sa_family_t sa_family;
                                                char        sa_data[14];
                                            }
     Return Value: 成功執行時,返回0。失敗返回-1,errno被設定出錯資訊
    ***************************************************/
     if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr)) == -1)
    {
        perror("bind is error/n");
        exit(1);
    }

    /**************************************************
     int listen(int sockfd, int ListenSum)
     Description: Listen()函數使socket處於被動的監聽模式,
                  並為該socket建立一個輸入資料隊列,將
                  到達的服務要求儲存在此隊列中,直到程
                  序處理它們。
     Argument Description:sockfd: 是Socket系統調用返回的socket 描述符
                          ListenSum: 指定在請求隊列中允許的最大請求數
     Return Value: 成功執行時,返回0。失敗返回-1,errno被設定出錯資訊
    ***************************************************/
    if(listen(sockfd,listnum) == -1)
    {
        perror("listen is error/n");
        exit(1);
    }
        printf("start to listen/n");
    while(1)
    {
        sin_size = sizeof(struct sockaddr_in);

        /***********************************************************
         int accept(int sockfd, void *addr, int *addrlen)
         Description:accept()函數讓伺服器接收客戶的串連請求。
                     在建立好輸入隊列後,伺服器就調用accept
                     函數,然後睡眠並等待客戶的串連請求。
       Argument Description:sockfd: 是Socket系統調用返回的socket 描述符
                            addr  : 通常是一個指向sockaddr_in變數的指標,
                                    該變數用來存放提出串連請求服務的主
                                    機的資訊(某台主機從某個連接埠發出該請求)
                            addrten: 通常為一個指向值為
                                     sizeof(struct sockaddr_in)的整型指標變數
        Return Value: 失敗返回-1,errno被設定出錯資訊
                      成功返回  當accept函數監視的socket收到串連請求時,
                                 socket執行體將建立一個新的 socket,執
                                 行體將這個新socket和請求串連進程的地址
                                 聯絡起來,收到服務要求的初始socket仍可
                                 以繼續在以前的 socket上監聽,同時可以在
                                 新的socket描述符上進行資料轉送操作
        ***************************************************************/
        if((new_fd = accept(sockfd,(struct sockaddr *)&their_addr,&sin_size)) == -1)
        {
            perror("accept is error/n");
            continue;
       
        }
        printf("server:got connection from %s/n",inet_ntoa(their_addr.sin_addr));

                char *p;
                char sock_buf[1024];
                bzero(sock_buf, 1024);
                p = sock_buf;
                int rval=0;

    /**************************************************************************
int recv(SOCKET s, char FAR *buf, int len, int flags );
不論是客戶還是伺服器應用程式都用recv函數從TCP串連的另一端接收資料。

該函數的第一個參數指定接收端通訊端描述符;

第二個參數指明一個緩衝區,該緩衝區用來存放recv函數接收到的資料;

第三個參數指明buf的長度;

第四個參數一般置0。

這裡只描述同步Socket的recv函數的執行流程。當應用程式調用recv函數時,recv先等待s的發送緩衝中的資料被協議傳送完畢,如果協
議在傳送s的發送緩衝中的資料時出現網路錯誤,那麼recv函數返回SOCKET_ERROR,如果s的發送緩衝中沒有資料或者資料被協議成功發送完畢
後,recv先檢查通訊端s的接收緩衝區,如果s接收緩衝區中沒有資料或者協議正在接收資料,那麼recv就一直等待,只到協議把資料接收完畢。當協議把
資料接收完畢,recv函數就把s的接收緩衝中的資料copy到buf中(注意協議接收到的資料可能大於buf的長度,所以在這種情況下要調用幾次
recv函數才能把s的接收緩衝中的資料copy完。recv函數僅僅是copy資料,真正的接收資料是協議來完成的),recv函數返回其實際copy
的位元組數。如果recv在copy時出錯,那麼它返回SOCKET_ERROR;如果recv函數在等待協議接收資料時網路中斷了,那麼它返回0。
    ***************************************************************************/

                if ((rval = recv(new_fd, p, 1024, 0)) < 0)
                {
                        printf("recv errror/n");
                }
                else
                {
                        printf("recv %s/n",p);
                }
    /**************************************************************************
      fork一個子進程對此次串連同父進程並行運行    ***************************************************************************/
    if(!fork())
    {
       
        /***********************************************************************
         int send(int sockfd, const void *msg, int len, int flags)
         Description:通過子進程發送資訊給用戶端
         Argument Description: sockfd: 是你想用來傳輸資料的socket描述符
                               msg   : 是一個指向要發送資料的指標
                               Len   : 是以位元組為單位的資料的長度
                               flags : 一般情況下置為0(這個參數涉及到阻塞和非阻塞問題)
        ************************************************************************/
        if(send(new_fd,"hello,HuHan/n",14,0) == -1)
        {
            perror("send is error/n");
            close(new_fd);
            exit(0);
        }
    }

    /******************************************************************************
     當所有的資料操作結束以後,你可以調用close()函數來釋放該socket,從而停止在
     該socket上的任何資料操作

     如你可以關閉某socket的寫操作而允許繼續在該socket上接受資料,直至讀入所有資料。

   int shutdown(int sockfd,int how);

  Sockfd是需要關閉的socket的描述符。參數 how允許為shutdown操作選擇以下幾種方式:
    ·0-------不允許繼續接收資料
    ·1-------不允許繼續發送資料
    ·2-------不允許繼續發送和接收資料
    shutdown在操作成功時返回0,在出現錯誤時返回-1共置相應errno。
    *******************************************************************************/
    close(new_fd);
   
    /****************************************************************************
     pid_t waitpid(pid_t pid, int *stat_loc, int option)
     pid參數指定需要等待的子進程的PID,如果是-1,waitpid將返回任一子進程的資訊,與
     wati()一樣,如果stat_loc不是null 指標,waitpid將把狀態資訊寫到所指向的位置。option
     參數允許我們改變waitpid的行為,其中最有用的一個選項是WNOHANG,他的作用是防止
     waitpid()調用將調用者執行掛起,可以用這個選項來尋找是否有子進程已經結束,如果沒有
     將繼續執行。
    *****************************************************************************/
    waitpid(-1,NULL,WNOHANG);
    }
}

 

用戶端程式:

#i nclude <stdlib.h>

#i nclude <stdio.h>

#i nclude <errno.h>

#i nclude <string.h>

#i nclude <netdb.h>

#i nclude <sys/types.h>

#i nclude <netinet/in.h>

#i nclude <sys/socket.h>

int main(int argc, char *argv[])

{

int sockfd;

char buffer[1024];

struct sockaddr_in server_addr;

struct hostent *host;

int portnumber,nbytes;

if(argc!=3)

{

printf("Usage:%s hostname portnumber/a/n",argv[0]);

exit(1);

}

/*gethostbyname
可以通過主機名稱得到主機的
IP
地址
*/

if((host=gethostbyname(argv[1]))==NULL)

{

printf("Gethostname error/n");

exit(1);

}

/*portnumber
為連接埠號碼
*/

if((portnumber=atoi(argv[2]))<0)

{

printf("Usage:%s hostname portnumber/a/n",argv[0]);

exit(1);

}

/*
客戶程式開始建立
sockfd
描述符
*/

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)

{

printf("Socket Error:%s/a/n",strerror(errno));

exit(1);

}

/*
客戶程式填充服務端的資料
*/

bzero(&server_addr,sizeof(server_addr));

server_addr.sin_family=AF_INET;

/*
主機位元組序轉換為網路位元組序
*/

server_addr.sin_port=htons(portnumber);

server_addr.sin_addr=*((struct in_addr *)host->h_addr);

/*
客戶程式發起串連請求
*/

if(connect(sockfd,(struct sockaddr *)(&server_addr),

sizeof(struct sockaddr))==-1)

{

printf("Connect Error:%s/a/n",strerror(errno));

exit(1);

}

/*
串連成功了
*/

if((nbytes=read(sockfd,buffer,1024))==-1)

{

printf("Read Error:%s/n",strerror(errno));

exit(1);

}

buffer[nbytes]='/0';

printf("I have received:%s/n",buffer);

/*
結束通訊
*/

close(sockfd);

exit(0);

}



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.