套接字是一種通信機制,憑借這種機制,客戶/服務器系統的開發工作既可以在本地單機上進行,也可以跨網絡進行。
套接字的特性有三個屬性確定,它們是:域(domain),類型(type),和協議(protocol)。套接字還用地址作為它的名字。地址的格式隨域(又被稱為協議族,protocol family)的不同而不同。每個協議族又可以使用一個或多個地址族定義地址格式。
1.套接字的域
域指定套接字通信中使用的網絡介質。最常見的套接字域是AF_INET,它是指Internet網絡,許多Linux局域網使用的都是該網絡,當然,因特網自身用的也是它。其底層的協議——網際協議(IP)只有一個地址族,它使用一種特定的方式來指定網絡中的計算機,即IP地址。
在計算機系統內部,端口通過分配一個唯一的16位的整數來表示,在系統外部,則需要通過IP地址和端口號的組合來確定。
2.套接字類型
流套接字(在某些方面類似域標準的輸入/輸出流)提供的是一個有序,可靠,雙向字節流的連接。
流套接字由類型SOCK_STREAM指定,它們是在AF_INET域中通過TCP/IP連接實現的。他們也是AF_UNIX域中常見的套接字類型。
數據包套接字
與流套接字相反,由類型SOCK_DGRAM指定的數據包套接字不建立和維持一個連接。它對可以發送的數據包的長度有限制。數據報作為一個單獨的網絡消息被傳輸,它可能會丟失,復制或亂序到達。
數據報套接字實在AF_INET域中通過UDP/IP連接實現,它提供的是一種無需的不可靠服務。
3.套接字協議
只要底層的傳輸機制允許不止一個協議來提供要求的套接字類型,我們就可以為套接字選擇一個特定的協議。
創建套接字
socket系統調用創建一個套接字并返回一個描述符,該描述符可以用來訪問該套接字。
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain , int type , int protocol);
創建的套接字是一條通信線路的一個端點。domain參數指定協議族,type參數指定這個套接字的通信類型,protocol參數指定使用的協議。
domain參數可以指定的協議族如下
域 說明
AF_UNIX UNIX域協議(文件系統套接字)
AF_INET ARPA因特網協議(UNIX網絡套接字)
AF_ISO ISO標準協議
AF_NS 施樂(XEROX)網絡系統協議
AF_IPX NOVELL IPX協議
AF_APPLETALKAppletalk DDS
最常見的套接字域是AF_UNIX和AF_INET,前者用于通過Unix和Linux文件系統實現的本地套接字,后者用于Unix網絡套接字。AF_INET套接字可以用于通過包括因特網在內的TCP/IP網絡進行通信的程序。微軟Windows系統的winsock接口也提供了對這個套接字域的訪問功能。
socket函數的參數type指定用于新套接字的通信特性。它的取值包括SOCK_STREAM和SOCK_DGRAM。
SOCK_STREAM是一個有序、可靠、面向連接的雙字節流。通過TCP連接來實現。
SOCK_DGRAM是數據包服務,我們可以用它來發送最大長度固定的消息。但消息是否會被正確傳遞或消息是否不會亂序到達沒有保證。
套接字地址結構
結構struct sockaddr_un 定義了一種通用的套接字地址,它的類型是:
struct sockaddr_un
{
sa_family_t sun_family; /*AF_UNIX*/
char sun_path; /*pathname*/
};
這是一種通用的定義,一般都不用。TCP/IP使用的是自己的結構體struct sockaddr_in,格式如下:
struct sockaddr_in
{
short int sin_family; //地址類型,一般為AF_INET
unsigned short int sin_port; //端口號
struct in_addr sin_addr; //IP地址
};
這里的struct in_addr的定義如下:
struct in_addr
{
unsigned long int s_addr;
};
結構體sockaddr和sockaddr_in的長度都是16字節。一般在編TCP/IP程序時,一般使用結構體sockaddr_in來設置地址,然后在需要的時候,通過強制類型轉換成sockaddr類型。
建立連接
函數connect用來在一個指定的套接字上創建一個連接,函數原型:
[cpp] view plain copy print?
-
int connect(int socket, const struct sockaddr *address, size_t address_len);
參數sockfd是一個由函數socket創建的套接字;
參數address是一個地址結構,需要連接的地址;
參數address_len為參數addr_addr的長度。
函數執行成功返回0,有錯誤發生則返回-1。
如果套接字類型是TCP,則該函數用于向服務器發出連接請求,服務器的IP地址和端口號由參數serv_addr指定;如果套接字類型是UDP,則該函數并不建立真正的連接,它只是告訴內核與該套接字進行通信的目的地址(由第二個參數指定),只有該目的地址發來的數據才會被該socket接收。
通常一個面向連接的套接字只能調用一次connect函數;而對于無連接的套接字則可以多次調用connect函數以改變與目的地址的綁定。
在套接字上監聽
函數listen把套接字轉化為被動監聽,函數原型:
int listen(int s, int backlog);
參數s指定了一個套接字;
參數backlog指定了該連接隊列的最大長度,如果已達到最大,則之后的連接請求將被服務器拒絕。
函數執行成功趕回0,有錯誤發生則返回-1。
由函數socket創建的套接字是主動套接字,這種套接字可以用來主動請求連接到某個服務器上。(通過connect()函數)。
作為服務器端的程序,通常在某個端口上監聽等待來自客戶端的連接請求。在服務器端,一般是先調用函數socket創建一個主動套接字,然后調用函數bind將該套接字綁定到某個端口上,接著再調用函數listen將該套接字轉化為監聽套接字,等待來自于客戶端的連接請求。
函數listen只是將套接字設置為傾聽模式以等待連接請求,它并不能接收連接請求,真正的接收客戶端連接請求的是accept()函數。
接收連接
函數accept用來接收一個連接請求,函數原型:
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
參數s是由socket創建,經函數bind綁定到本地某一端口上,然后通過函數listen轉化而來的監聽套接字;
參數addr用來保存發起連接請求的主機的地址和端口;
參數addrlen是addr所指向的結構體的大小。
函數執行成功返回一個新的代表客戶端的套接字,出錯則返回-1。
只能對面向連接的套接字使用accept函數。accept執行成功時,將創建一個新的套接字,并且這個新的套接字分配一個套接字描述符,并返回這個新的套接字描述符。這個新的套接字描述符與打開文件返回的文件描述符類似,進程可以利用這個新的套接字描述符與客戶端交換數據,參數s所指定的套接字繼續等待客戶端的連接請求。
[cpp] view plain copy print?
-
/* Make the necessary includes and set up the variables. */
-
-
#include <sys/types.h>
-
#include <sys/socket.h>
-
#include <stdio.h>
-
#include <sys/un.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
-
int main()
-
{
-
int sockfd;
-
int len;
-
struct sockaddr_un address;
-
int result;
-
char ch = 'A';
-
-
/* Create a socket for the client. */
-
-
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
-
-
/* Name the socket, as agreed with the server. */
-
-
address.sun_family = AF_UNIX;
-
strcpy(address.sun_path, "server_socket");
-
len = sizeof(address);
-
-
/* Now connect our socket to the server's socket. */
-
-
result = connect(sockfd, (struct sockaddr *)&address, len);
-
-
if(result == -1) {
-
perror("oops: client1");
-
exit(1);
-
}
-
-
/* We can now read/write via sockfd. */
-
-
write(sockfd, &ch, 1);
-
read(sockfd, &ch, 1);
-
printf("char from server = %c\n", ch);
-
close(sockfd);
-
exit(0);
-
}
[cpp] view plain copy print?
-
/* Make the necessary includes and set up the variables. */
-
-
#include <sys/types.h>
-
#include <sys/socket.h>
-
#include <stdio.h>
-
#include <sys/un.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
-
int main()
-
{
-
int server_sockfd, client_sockfd;
-
int server_len, client_len;
-
struct sockaddr_un server_address;
-
struct sockaddr_un client_address;
-
-
/* Remove any old socket and create an unnamed socket for the server. */
-
-
unlink("server_socket");
-
server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
-
-
/* Name the socket. */
-
-
server_address.sun_family = AF_UNIX;
-
strcpy(server_address.sun_path, "server_socket");
-
server_len = sizeof(server_address);
-
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
-
-
/* Create a connection queue and wait for clients. */
-
-
listen(server_sockfd, 5);
-
while(1) {
-
char ch;
-
-
printf("server waiting\n");
-
-
/* Accept a connection. */
-
-
client_len = sizeof(client_address);
-
client_sockfd = accept(server_sockfd,
-
(struct sockaddr *)&client_address, &client_len);
-
-
/* We can now read/write to client on client_sockfd. */
-
-
read(client_sockfd, &ch, 1);
-
ch++;
-
write(client_sockfd, &ch, 1);
-
close(client_sockfd);
-
}
-
}
原創文章,作者:NddTx99521,如若轉載,請注明出處:http://www.www58058.com/37187