面向連接的網絡應用程序分爲客戶端和服務器端。服務器端的執行流程一般爲4步,客戶端程序相對簡單,一般需要兩個步驟。
服務器端執行流程4步如下:
(1)調用socket函數,建立一個套接字,該套接字用於接下來的網絡通信。
(2)調用bind函數,將該套接字綁定到一個地址,並制定一個端口號,
(3)調用listen函數,使用該套接字監聽連接請求
(4)當請求來到時,調用accept函數複製該套接字處理請求
客戶端執行流程2步如下:
(1)調用socket函數,創建一個套接字
(2)調用connect函數使用該套接字與服務器進行連接
服務器端和客戶端程序的顯著區別在於客戶端程序不需要調用bind函數,bind函數的作用是將套接字綁定一個IP地址和端口號,因爲這兩個元素可以在網絡環境中唯一地址表示一個進程。如果套接字沒有使用bind函數綁定地址和端口,那麼調用listen函數和connect函數的時候內核會自動爲套接字綁定。由此可知,如果沒有使用bind函數,調用listen函數和connect函數的時候內核會自動爲套接字綁定。看起來好像bind函數是多餘的,但事實並不是這樣。
我們先來看看listen函數和connect是怎麼綁定套接字的,connect函數綁定套接字的時候使用的是一個設置好的地址結構(sockaddr_in)作爲參數,結構中指定了服務器的地址和需要通信的端口號。但是listen函數沒有這個參數,多以listen函數不能夠使用設置好的地址結構,只能由系統設置IP地址和端口號。也就是說在服務器端,如果不使用bind函數的話,創建套接字時使用的是當前系統中空閒端口的套接字。
這樣的話,服務器端的程序不關心客戶端的IP地址,也就說是對應的端口號是內核臨時指派的一個端口,是隨機的,每次執行服務器程序的時候,使用的都是不同的端口。但是在客戶端是需要指定通信的服務器的端口的,如果不使用bind函數,每次的端口是隨機的話,那麼每次重啓服務程序之後都要對客戶端的程序進行調整,這樣做不僅不合理,而且工作量很大,因此在服務器端bind函數作用非常重要。
下面是一個使用bind函數的服務器端程序,可以作爲參考理解。
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <strings.h> #include <ctype.h> #include <sys/socket.h> #include <arpa/inet.h> #define MAX_LINE 100 int main(void) { struct sockaddr_in sin; struct sockaddr_in cin; int l_fd; int c_fd; socklen_t len; char buf[MAX_LINE]; char addr_p[INET_ADDRSTRLEN]; int port = 8000; int n; bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(port); /*創立套接字,使用TCP協議*/ if((l_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("fail to creat socket"); exit(1); } /*將地址和套節字綁定*/ if(bind(l_fd, (struct sockaddr *) &sin, sizeof(sin)) == -1) { perror("fail to bind"); exit(1); } /*開始監聽連接請求*/ if(listen(l_fd, 10) == -1) { perror("fail to listren"); exit(1); } printf("waiting...\n"); while(1) { /*接受連接請求,從此函數中返回後就可以開始通信了*/ if((c_fd = accept(l_fd, (struct sockaddr *) &cin, &len)) == -1) { perror("fail to accept"); exit(1); } /*調用recv函數讀取客戶端傳來的信息,不設置任何特殊的標誌*/ n = recv(c_fd, buf, MAX_LINE, 0); if(n = -1) { perror("fail to receive"); exit(1); } else if(n == 0) { printf("the connect has been closed\n "); close(c_fd); continue; } /*將客戶端地址轉換爲字符串*/ inet_ntop(AF_INET, &cin.sin_addr, addr_p, sizeof(addr_p)); printf("client IP is %s, port is %s \n", addr_p, ntohs(cin.sin_port)); printf("connect is : %s\n", buf); n = strlen(buf); sprintf(buf, "%d", n); /*使用send函數將轉換後的字符串發送給客戶端,不設置任何特殊的標誌*/ n = send(c_fd, buf, strlen(buf) + 1, 0); if(n == -1) { perror("fail to send"); exit(1); } if(cloes(c_fd) == -1) { perror("fail to close"); exit(1); } } return 0; }