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

來源:互聯網
上載者:User

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

server                                                  client

 

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

建立socket                                          建立socket

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

      |                                                         |

      |                                                         |

      |                                                         |

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

地址賦值(                                           地址賦值(

自己的地址)                                        伺服器位址)

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

      |                                                         |

      |                                                         |

      |                                                         |

++++++++                                              |

用bind綁定                                                |

socket和地址                                             |

++++++++                                              |

      |                                                         |

      |                                                         |

      |                                                         |

+++++++                                                 |

listen                                                         |

+++++++                                                  |

      |                                                    ++++++++++

      |   <------------------------------          connect 伺服器         

      |                                                    ++++++++++

+++++++                                                  |

accept                                                        |

+++++++                                                  |

      |                                                           |

      |                                                    +++++++++

      |                                                     recv 和send

      |                                                     進行資料處理

      |                                                     +++++++++

+++++++++                                                |

用accept得到                                                 |

的socket進行                                                 |

recv 和 send                                                 |

+++++++++                                                |

      |                                                             |

      |                                                             |

      |                                                             |

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

close socket                                         close socket

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

 

根據以上步驟,伺服器端的代碼為

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

 

 

用戶端代碼為:

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

 

 

現在一個簡單的使用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 ); 



















 

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.