這篇文章主要介紹socket套接字的案例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
創(chuàng)新互聯(lián)公司主要從事網(wǎng)站制作、成都網(wǎng)站建設、網(wǎng)頁設計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務。立足成都服務隴南,10年網(wǎng)站建設經(jīng)驗,價格優(yōu)惠、服務專業(yè),歡迎來電咨詢建站服務:18982081108IP地址、端口號……
socket API
實現(xiàn)UDP客戶端/服務器
前面介紹過,本地的進程間通信(IPC)有很多種方式,常見的總結以下幾點:
1、管道(包括無名管道和命名管道); 2、消息隊列; 3、信號量; 4、共享存儲。 5、……( Socket和Streams支持不同主機上的兩個進程IPC)。認識網(wǎng)絡層通信過程:初識IP:
(IP就是:Internet協(xié)議IP)
在通信時,IP有源IP和目的IP之分,
對比寄快遞:網(wǎng)絡通信相當于收發(fā)快遞,IP就是收件/發(fā)件人地址,僅僅知道地址還不行,還要知道派送人是誰?這就對比于網(wǎng)絡中的端口號概念,端口號標識了一個進程,告訴操作系統(tǒng),當前這個數(shù)據(jù)交給哪一個程序進行解析。
端口號(port)是傳輸層協(xié)議的內容。
端口號是一個2字節(jié)16位的整數(shù);
端口號用來標識一個進程,告訴操作系統(tǒng),當前這個數(shù)據(jù)交給哪一個程序進行解析;
IP地址 + 端口號能標識網(wǎng)絡上的某一臺主機的某一個進程;
一個端口號只能被一個進程占用。
進程有唯一的pid標識,端口號也能標識進程;
一個進程可以綁定多個端口號,一個端口號不能被多個進程綁定。
TCP:傳輸層協(xié)議(TCP/IP)的數(shù)據(jù)段中有兩個端口號,分別叫做源端口號和目的端口號,就是在描述“數(shù)據(jù)是誰的?發(fā)給誰?”
(TCP)傳輸控制協(xié)議,面向連接。是一種提供可靠數(shù)據(jù)傳輸?shù)耐ㄓ脜f(xié)議。
傳輸層協(xié)議
有連接
可靠傳輸
面向字節(jié)流
(UDP)用戶數(shù)據(jù)報協(xié)議,是一個面向無連接的協(xié)議。采用該協(xié)議不需要兩個應用程序先建立連接。UDP協(xié)議不提供差錯恢復,不能提供數(shù)據(jù)重傳,因此該協(xié)議傳輸數(shù)據(jù)安全性差。
傳輸層協(xié)議
無連接
不可靠傳輸
面向數(shù)據(jù)報
其實很容易理解這個問題,就是C語言中比較講究的大小端問題。
發(fā)送機按內存地址從低到高順序發(fā)送;
接收主機按內存地址從低到高順序保存;
TCP/IP規(guī)定:網(wǎng)絡數(shù)據(jù)流應采用大端字節(jié)序,即地地址高字節(jié);
不論主機是大端機還是小端機,都必須遵循TCP/IP規(guī)定;
如果發(fā)送機是小端,就先將數(shù)據(jù)轉成大端再發(fā)送。
//創(chuàng)建socket文件描述符 (TCP/UDP,客戶端+服務器) int socket(int domain, int type, int protocol);
參數(shù)1(domain): 選擇創(chuàng)建的套接字所用的協(xié)議族;
AF_INET : IPv4協(xié)議;
AF_INET6: IPv6協(xié)議;
AF_LOCAL: Unix域協(xié)議;
AF_ROUTE:路由套接口;
AF_KEY :密鑰套接口。
參數(shù)2(type):指定套接口類型,所選類型有:
SOCK_STREAM:字節(jié)流套接字;
SOCK_DGRAM : 數(shù)據(jù)報套接字;
SOCK_RAW : 原始套接口。
procotol: 使用的特定協(xié)議,一般使用默認協(xié)議(NULL)。
//綁定端口號 (TCP/IP,服務器) int bind(int socket, const struct sockaddr *address, socklen_t address_len);
參數(shù)1(socket) : 是由socket()調用返回的并且未作連接的套接字描述符(套接字號)。
參數(shù)2(address):指向特定協(xié)議的地址指針。
參數(shù)3(address_len):上面地址結構的長度。
返回值:沒有錯誤,bind()返回0,否則SOCKET_ERROR。
//開始監(jiān)聽socket (TCP,服務器) int listen(int socket, int backlog);
參數(shù)1(sockfd):是由socket()調用返回的并且未作連接的套接字描述符(套接字號)。
參數(shù)2(backlog):所監(jiān)聽的端口隊列大小。
//接受請求 (TCP,服務器) int accept(int socket, struct sockaddr* address, socklen_t* address_len);
參數(shù)1(socket) : 是由socket()調用返回的并且未作連接的套接字描述符(套接字號)。
參數(shù)2(address):指向特定協(xié)議的地址指針。
參數(shù)3(addrlen):上面地址結構的長度。
返回值:沒有錯誤,bind()返回0,否則SOCKET_ERROR。
//建立連接 (TCP,客戶端) int connect(int sockfd, const struct struct sockaddr *addr, aocklen_t addrlen);
//關閉套接字 int close(int fd);
簡單的TCP網(wǎng)絡程序:參數(shù)(fd):是由socket()調用返回的并且未作連接的套接字描述符(套接字號)。
socket API是一層抽象的網(wǎng)絡編程接口,適用于各種底層網(wǎng)絡協(xié)議,如IPv4,IPv6,……
服務器代碼:
#include#include #include #include #include #include #include using namespace std; #define SERVER_PORT 5050 //端口號 #define SERVER_IP "192.168.3.254" //服務器ip #define QUEUE_SIZE 5 //所監(jiān)聽端口隊列大小 int main(int argc, char *argv[]) { //創(chuàng)建一個套接字,并檢測是否創(chuàng)建成功 int sockSer; sockSer = socket(AF_INET, SOCK_STREAM, 0); if(sockSer == -1){ perror("socket"); } //設置端口可以重用,可以多個客戶端連接同一個端口,并檢測是否設置成功 int yes = 1; if(setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){ perror("setsockopt"); } struct sockaddr_in addrSer,addrCli; //創(chuàng)建一個記錄地址信息的結構體 addrSer.sin_family = AF_INET; //所使用AF_INET協(xié)議族 addrSer.sin_port = htons(SERVER_PORT); //設置地址結構體中的端口號 addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); //設置其中的服務器ip //將套接字地址與所創(chuàng)建的套接字號聯(lián)系起來。并檢測是否綁定成功 socklen_t addrlen = sizeof(struct sockaddr); int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen); if(res == -1) perror("bind"); listen(sockSer, QUEUE_SIZE); //監(jiān)聽端口隊列是否由連接請求,如果有就將該端口設置位可連接狀態(tài),等待服務器接收連接 printf("Server Wait Client Accept......\n"); //如果監(jiān)聽到有連接請求接受連接請求。并檢測是否連接成功,成功返回0,否則返回-1 int sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &addrlen); if(sockConn == -1) perror("accept"); else { printf("Server Accept Client OK.\n"); printf("Client IP:> %s\n", inet_ntoa(addrCli.sin_addr)); printf("Client Port:> %d\n",ntohs(addrCli.sin_port)); } char sendbuf[256]; //申請一個發(fā)送緩存區(qū) char recvbuf[256]; //申請一個接收緩存區(qū) while(1) { printf("Ser:>"); scanf("%s",sendbuf); if(strncmp(sendbuf,"quit",4) == 0) //如果所要發(fā)送的數(shù)據(jù)為"quit",則直接退出。 break; send(sockConn, sendbuf, strlen(sendbuf)+1, 0); //發(fā)送數(shù)據(jù) recv(sockConn, recvbuf, 256, 0); //接收客戶端發(fā)送的數(shù)據(jù) printf("Cli:> %s\n",recvbuf); } close(sockSer); //關閉套接字 return 0; }
客戶端代碼:
#include簡單的UDP網(wǎng)絡程序:#include #include #include #include #include #include using namespace std; #define SERVER_PORT 5050 #define SERVER_IP "192.168.3.254" int main(int argc, char *argv[]) { //創(chuàng)建客戶端套接字號,并檢測是否創(chuàng)建成功 int sockCli; sockCli = socket(AF_INET, SOCK_STREAM, 0); if(sockCli == -1) perror("socket"); //創(chuàng)建一個地址信息結構體,并對其內容進行設置 struct sockaddr_in addrSer; addrSer.sin_family = AF_INET; //使用AF_INET協(xié)議族 addrSer.sin_port = htons(SERVER_PORT); //設置端口號 addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); //設置服務器ip bind(sockCli,(struct sockaddr*)&addrCli, sizeof(struct sockaddr)); //將套接字地址與所創(chuàng)建的套接字號聯(lián)系起來 //創(chuàng)建一個與服務器的連接,并檢測連接是否成功 socklen_t addrlen = sizeof(struct sockaddr); int res = connect(sockCli,(struct sockaddr*)&addrSer, addrlen); if(res == -1) perror("connect"); else printf("Client Connect Server OK.\n"); char sendbuf[256]; //申請一個發(fā)送數(shù)據(jù)緩存區(qū) char recvbuf[256]; //申請一個接收數(shù)據(jù)緩存區(qū) while(1) { recv(sockCli, recvbuf, 256, 0); //接收來自服務器的數(shù)據(jù) printf("Ser:> %s\n",recvbuf); printf("Cli:>"); scanf("%s",sendbuf); if(strncmp(sendbuf,"quit", 4) == 0) //如果客戶端發(fā)送的數(shù)據(jù)為"quit",則退出。 break; send(sockCli, sendbuf, strlen(sendbuf)+1, 0); //發(fā)送數(shù)據(jù) } close(sockCli); //關閉套接字 return 0; }
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
參數(shù)1(sockfd):是由socket()調用返回的并且未作連接的套接字描述符(套接字號)
參數(shù)2(buf):指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針
參數(shù)3(len):緩沖區(qū)長度。
**參數(shù)4(flags):**flags的值或為0,或為其他
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
參數(shù)1(sockfd):是由socket()調用返回的并且未作連接的套接字描述符(套接字號)
參數(shù)2(buf):指向存有接收數(shù)據(jù)的緩沖區(qū)的指針
參數(shù)3(len):緩沖區(qū)長度
**參數(shù)4(flags):**flags的值或為0,或為其他
服務器端代碼:
#include#include #include #include #include #include int main() { //創(chuàng)建一個套接字,并檢測是否創(chuàng)建成功 int sockSer = socket(AF_INET, SOCK_DGRAM, 0); if(sockSer == -1) perror("socket"); struct sockaddr_in addrSer; //創(chuàng)建一個記錄地址信息的結構體 addrSer.sin_family = AF_INET; //使用AF_INET協(xié)議族 addrSer.sin_port = htons(5050); //設置地址結構體中的端口號 addrSer.sin_addr.s_addr = inet_addr("192.168.3.169"); //設置通信ip //將套接字地址與所創(chuàng)建的套接字號聯(lián)系起來,并檢測是否綁定成功 socklen_t addrlen = sizeof(struct sockaddr); int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen); if(res == -1) perror("bind"); char sendbuf[256]; //申請一個發(fā)送數(shù)據(jù)緩存區(qū) char recvbuf[256]; //申請一個接收數(shù)據(jù)緩存區(qū) struct sockaddr_in addrCli; while(1) { recvfrom(sockSer,recvbuf,256,0,(struct sockaddr*)&addrCli, &addrlen); //從指定地址接收客戶端數(shù)據(jù) printf("Cli:>%s\n",recvbuf); printf("Ser:>"); scanf("%s",sendbuf); sendto(sockSer,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&addrCli, addrlen); //向客戶端發(fā)送數(shù)據(jù) } return 0; }
客戶端代碼:
#include#include #include #include #include #include int main() { //創(chuàng)建一個套接字,并檢測是否創(chuàng)建成功 int sockCli = socket(AF_INET, SOCK_DGRAM, 0); if(sockCli == -1){ perror("socket"); } addrSer.sin_family = AF_INET; //使用AF_INET協(xié)議族 addrSer.sin_port = htons(5050); //設置地址結構體中的端口號 addrSer.sin_addr.s_addr = inet_addr("192.168.3.169"); //設置通信ip socklen_t addrlen = sizeof(struct sockaddr); char sendbuf[256]; //申請一個發(fā)送數(shù)據(jù)緩存區(qū) char recvbuf[256]; //申請一個接收數(shù)據(jù)緩存區(qū) while(1){ //向客戶端發(fā)送數(shù)據(jù) printf("Cli:>"); scanf("%s",sendbuf); sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrSer, addrlen); 接收來自客戶端的數(shù)據(jù) recvfrom(sockCli, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrSer, &addrlen); printf("Ser:>%s\n", recvbuf); } return 0; }
以上是socket套接字的案例分析的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道!