socket編程——一個簡單的例子

從一個簡單的使用TCP例子開始socket編程,其基本步驟以下:編程

server                                                  client服務器

 

+++++++                                          ++++++++網絡

建立socket                                          建立socketapp

+++++++                                          ++++++++socket

      |                                                         |tcp

      |                                                         |ide

      |                                                         |函數

+++++++                                          ++++++++ui

地址賦值(                                           地址賦值(this

本身的地址)                                        服務器地址)

+++++++                                          ++++++++

      |                                                         |

      |                                                         |

      |                                                         |

++++++++                                              |

用bind綁定                                                |

socket和地址                                             |

++++++++                                              |

      |                                                         |

      |                                                         |

      |                                                         |

+++++++                                                 |

listen                                                         |

+++++++                                                  |

      |                                                    ++++++++++

      |   <------------------------------          connect 服務器         

      |                                                    ++++++++++

+++++++                                                  |

accept                                                        |

+++++++                                                  |

      |                                                           |

      |                                                    +++++++++

      |                                                     recv 和send

      |                                                     進行數據處理

      |                                                     +++++++++

+++++++++                                                |

用accept獲得                                                 |

的socket進行                                                 |

recv 和 send                                                 |

+++++++++                                                |

      |                                                             |

      |                                                             |

      |                                                             |

+++++++++                                        +++++++++

close socket                                         close socket

+++++++++                                        +++++++++

 

根據以上步驟,服務器端的代碼爲

#include <stdio.h>  
#include <string.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <stdlib.h>  
#include <syslog.h>  
#include <errno.h>  
#define MAX_LISTEN_NUM 5  
#define SEND_BUF_SIZE 100  
#define RECV_BUF_SIZE 100  
#define LISTEN_PORT 1010  
int main()  
{  
  int listen_sock = 0;  
  int app_sock = 0;  
  struct sockaddr_in hostaddr;  
  struct sockaddr_in clientaddr;  
  int socklen = sizeof(clientaddr);  
  char sendbuf[SEND_BUF_SIZE] = {0};  
  char recvbuf[RECV_BUF_SIZE] = {0};  
  int sendlen = 0;  
  int recvlen = 0;  
  int retlen = 0;  
  int leftlen = 0;  
  char *ptr = NULL;  
  memset((void *)&hostaddr, 0, sizeof(hostaddr));  
  memset((void *)&clientaddr, 0, sizeof(clientaddr));  
  hostaddr.sin_family = AF_INET;  
  hostaddr.sin_port = htons(LISTEN_PORT);  
  hostaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  listen_sock = socket(AF_INET, SOCK_STREAM, 0);  
  if(listen_sock < 0)  
  {  
      syslog(LOG_ERR, "%s:%d, create socket failed", __FILE__, __LINE__);  
      exit(1);  
  }  
  if(bind(listen_sock, (struct sockaddr *)&hostaddr, sizeof(hostaddr)) < 0)  
  {  
      syslog(LOG_ERR, "%s:%d, bind socket failed", __FILE__, __LINE__);  
      exit(1);  
  }  
  if(listen(listen_sock, MAX_LISTEN_NUM) < 0)  
  {  
      syslog(LOG_ERR, "%s:%d, listen failed", __FILE__, __LINE__);  
      exit(1);  
  }  
  while(1)  
  {  
      app_sock = accept(listen_sock, (struct sockaddr *)&clientaddr, &socklen);  
      if(app_sock < 0)  
     {  
        syslog(LOG_ERR, "%s:%d, accept failed", __FILE__, __LINE__);  
        exit(1);  
     }  
     sprintf(sendbuf, "welcome %s:%d here!/n", inet_ntoa(clientaddr.sin_addr.s_addr), clientaddr.sin_port);  
     //send data  
     sendlen = strlen(sendbuf) +1;  
     retlen = 0;  
     leftlen = sendlen;  
     ptr = sendbuf;  
     //while(leftlen)  
     {  
         retlen = send(app_sock, ptr, sendlen, 0);  
      if(retlen < 0)  
      {  
          if(errno == EINTR)  
            retlen = 0;  
        else  
            exit(1);  
      }  
      leftlen -= retlen;  
      ptr += retlen;  
     }  
     //receive data  
     recvlen = 0;  
     retlen = 0;  
     ptr = recvbuf;  
     leftlen = RECV_BUF_SIZE -1;  
     //do  
     {  
         retlen = recv(app_sock, ptr, leftlen, 0) ;  
      if(retlen < 0)  
      {  
          if(errno == EINTR)  
            retlen = 0;  
        else  
            exit(1);  
      }  
      recvlen += retlen;  
      leftlen -= retlen;  
      ptr += retlen;  
     }  
     //while(recvlen && leftlen);  
     printf("receive data is : %s", recvbuf);  
    close(app_sock);  
  }  
  close(listen_sock);  
    
  return 0;  
    
    
}  

客戶端代碼爲:

#include <stdio.h>  
#include <string.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <syslog.h>  
#include <errno.h>  
#include <stdlib.h>  
#define MAX_LISTEN_NUM 5  
#define SEND_BUF_SIZE 100  
#define RECV_BUF_SIZE 100  
#define SERVER_PORT 1010  
int main()  
{  
    int sock_fd = 0;  
    char recvbuf[RECV_BUF_SIZE] = {0};  
    char sendbuf[SEND_BUF_SIZE] = {0};  
    int recvlen = 0;  
    int retlen = 0;  
    int sendlen = 0;  
    int leftlen = 0;  
    char *ptr = NULL;  
    struct sockaddr_in ser_addr;  
      
    memset(&ser_addr, 0, sizeof(ser_addr));  
    ser_addr.sin_family = AF_INET;  
    inet_aton("127.0.0.1", (struct in_addr *)&ser_addr.sin_addr);  
    ser_addr.sin_port = htons(SERVER_PORT);  
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);  
    if(sock_fd < 0)  
    {  
        syslog(LOG_ERR, "%s:%d, create socket failed", __FILE__, __LINE__);  
        exit(1);  
    }  
    if(connect(sock_fd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) < 0)  
    {  
        syslog(LOG_ERR, "%s:%d, connect socket failed", __FILE__, __LINE__);  
        exit(1);  
    }  
     //receive data  
     recvlen = 0;  
     retlen = 0;  
     ptr = recvbuf;  
     leftlen = RECV_BUF_SIZE -1;  
     //do  
     {  
         retlen = recv(sock_fd, ptr, leftlen, 0) ;  
      if(retlen < 0)  
      {  
          if(errno == EINTR)  
            retlen = 0;  
        else  
            exit(1);  
      }  
      recvlen += retlen;  
      leftlen -= retlen;  
      ptr += retlen;  
     }  
     //while(recvlen && leftlen);  
     printf("receive data is : %s", recvbuf);  
     sprintf(sendbuf, "hello server/n");  
     //send data  
     sendlen = strlen(sendbuf) +1;  
     retlen = 0;  
     leftlen = sendlen;  
     ptr = sendbuf;  
    // while(leftlen)  
     {  
         retlen = send(sock_fd, ptr, sendlen, 0);  
      if(retlen < 0)  
      {  
          if(errno == EINTR)  
            retlen = 0;  
        else  
            exit(1);  
      }  
      leftlen -= retlen;  
      ptr += retlen;  
     }  
     close(sock_fd);  
      
}  

如今一個簡單的使用tcp的socket通訊的例子已經完成了,這裏有幾個須要說明的問題

1)頭文件:

sys/socket.h   包含了socket相關的函數,如socket,send 和recv, 以及struct sockaddr等

netinet/in.h    包含了地址結構,如struct sockaddr_in

errno.h           包含了errno 和 EINTR

syslog.h         包含了syslog相關的信息,其打印結果在/var/log/messages裏面

2)socket地址

對於IPv4來講,其地址用的是struct sockaddr_in,具體結構以下

struct in_addr {  
  in_addr_t   s_addr;           /* 32-bit IPv4 address */  
                                /* network byte ordered */  
};  
  
struct sockaddr_in {  
  uint8_t         sin_len;      /* length of structure (16) */  
  sa_family_t     sin_family;   /* AF_INET */  
  in_port_t       sin_port;     /* 16-bit TCP or UDP port number */  
                                /* network byte ordered */  
  struct in_addr  sin_addr;     /* 32-bit IPv4 address */  
                                /* network byte ordered */  
  char            sin_zero[8];  /* unused */  
};  
其中sin_len咱們通常不關注,也不填(只有在使用routing socket的時候纔用到,被內核用來處理各類協議簇的地址結構)。
bind, connect, sendto, 和 sendmsg會把socket地址從程序傳遞給內核; 而accept, recvfrom, recvmsg, getpeername, 和
getsockname會把地址從內核傳遞給程序。由於不一樣協議簇的地址結構是不同的,因此必需要有一個通用的指針來傳遞地址,
對於ANSI C來講咱們通常使用void *,可是socket產生早於ANSI C,因此也就沒有使用這個機制,而是使用一個通用的地址結構
struct sockaddr來處理的
struct sockaddr {  
  uint8_t      sa_len;  
  sa_family_t  sa_family;    /* address family: AF_xxx value */  
  char         sa_data[14];  /* protocol-specific address */  
}; 
IPv6的socket地址爲struct sockaddr_in6
 
struct in6_addr {  
  uint8_t  s6_addr[16];          /* 128-bit IPv6 address */  
                                 /* network byte ordered */  
};  
  
#define SIN6_LEN      /* required for compile-time tests */  
  
struct sockaddr_in6 {  
  uint8_t         sin6_len;      /* length of this struct (28) */  
  sa_family_t     sin6_family;   /* AF_INET6 */  
  in_port_t       sin6_port;     /* transport layer port# */  
                                 /* network byte ordered */  
  uint32_t        sin6_flowinfo; /* flow information, undefined */  
  struct in6_addr sin6_addr;     /* IPv6 address */  
                                 /* network byte ordered */  
  uint32_t        sin6_scope_id; /* set of interfaces for a scope */  
};  
 
對於sockaddr-in6來講,咱們不能用通用的地址struct sockaddr來存儲了,而是產用新的通用地址結構struct sockaddr_storage,
這個結構足夠大能夠存儲任何系統支持的地址。
struct sockaddr_storage {  
  uint8_t      ss_len;       /* length of this struct (implementation dependent) */  
  sa_family_t  ss_family;    /* address family: AF_xxx value */  
  /* implementation-dependent elements to provide: 
   * a) alignment sufficient to fulfill the alignment requirements of 
   *    all socket address types that the system support 
   * b) enough storage to hold any type of socket address that the 
   *    system supports. 
   */  
};  
幾種常見的地址結構
3) 相關函數的的length

對於從程序傳地址給內核的函數(如connect),其長度是一個整型值,告訴內核要copy的地址長度。

對於從內核傳遞給程序的函數(如accpt),其長度是一個整型指針,是一個value-result參數。有兩個目的:一告訴內核地址結構的長度,
讓內核在copy的時候不要超過這個長度;二返回內核真正copy的長度。 4)字節序 socket相關的函數都是使用網絡字節序 5)地址轉換函數 inet_aton, inet_ntoa, and inet_addr把IPv4字符串地址轉爲32位的網絡字節序地址 inet_ptonand inet_ntop能夠轉換IPv4和IPv6的地址 6)listen中的backlog 要知道這個值的含義先用說一下,對於一個listen的socket,有兩個隊列:一個是incomplete connection隊列(僅僅收到SYN);
一個是complete connection隊列(三次握手完成)。accept函數就是在complete connection隊列中取一個socket。backlog就是指隊列的個數,
但不行的是各個地方都沒有明肯定義這個值,沒有說明究竟表明了哪一個隊列,或是兩個隊列之和。通常來講能夠 同時處理的鏈接數是backlog的1.5倍,不少地方都用5. 7) getsockname 和 getpeername 這兩個函數能夠與socket關聯的地址,getsockname 和 getpeername分別獲得本身和對端的地址
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen)  
  
int getpeername(int sockfd , struct sockaddr * peeraddr , socklen_t * addrlen );  

FROM: http://blog.csdn.net/wind19/article/details/6156339

相關文章
相關標籤/搜索