日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

简单的socket通信实现

發(fā)布時(shí)間:2024/4/18 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 简单的socket通信实现 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、什么是socket網(wǎng)絡(luò)通信?

socket也稱作“套接字”,描述了計(jì)算機(jī)的IP地址和端口,運(yùn)行在計(jì)算機(jī)中的程序之間采用socket進(jìn)行數(shù)據(jù)通信。通信的兩端都有socket,它是一個(gè)通道,數(shù)據(jù)在兩個(gè)socket之間進(jìn)行傳輸。

二、套接字(socket)

TCP提供了流(stream)和數(shù)據(jù)報(bào)(datagram)兩種通信機(jī)制,所以套接字也分為流socket和數(shù)據(jù)報(bào)socket。

流socket的類型是SOCK_STREAM,基于TCP協(xié)議,是一個(gè)有序可靠雙向字節(jié)流的通道,傳輸數(shù)據(jù)不會(huì)丟失、重復(fù)或亂序到達(dá),而且它還有出錯(cuò)后重傳機(jī)制(就像兩個(gè)人在打電話,聊天您一句我一句,有來(lái)有往,沒(méi)聽(tīng)清楚就再說(shuō)一次)。

數(shù)據(jù)報(bào)套接的類型是SOCK_DGRAM,基于UDP協(xié)議。不需要建立和維持連接,并且對(duì)發(fā)送的數(shù)據(jù)長(zhǎng)度有限制,數(shù)據(jù)報(bào)作為一個(gè)單獨(dú)的網(wǎng)絡(luò)消息被傳輸,它可能會(huì)丟失、重復(fù)或錯(cuò)亂到達(dá),UDP不是一個(gè)可靠的協(xié)議,但是它沒(méi)有擁塞控制,效率比較高。

三、簡(jiǎn)單的socket通信過(guò)程

四、客戶/服務(wù)端模式

1、服務(wù)端流程

1)創(chuàng)建服務(wù)端的socket。

2)把服務(wù)端用于通信的ip地址和端口綁定到socket上。

3)把socket設(shè)置為監(jiān)聽(tīng)模式。

4)接受客戶端的連接。

5)與客戶端通信,接收客戶端發(fā)過(guò)來(lái)的報(bào)文后,回復(fù)處理結(jié)果。

6)不斷的重復(fù)第5)步,直到客戶端斷開(kāi)連接。

7)關(guān)閉socket,釋放資源。

示例(server.cpp)

#include <stdio.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>int main() {// 第1步:創(chuàng)建服務(wù)端的socket。int listenfd = socket(AF_INET,SOCK_STREAM,0); // 第2步:把服務(wù)端用于通信的地址和端口綁定到socket上。struct sockaddr_in servaddr; // 服務(wù)端地址信息的數(shù)據(jù)結(jié)構(gòu)。memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET; // 協(xié)議族,在socket編程中只能是AF_INET。servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 任意ip地址。//servaddr.sin_addr.s_addr = inet_addr("118.89.50.198"); // 指定ip地址。servaddr.sin_port = htons(5051); // 指定通信端口。if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 ){ perror("bind"); close(listenfd); return -1; }// 第3步:把socket設(shè)置為監(jiān)聽(tīng)模式。if (listen(listenfd,5) != 0 ) { perror("listen"); close(listenfd); return -1; }// 第4步:接受客戶端的連接。int clientfd; // 客戶端的socket。int socklen=sizeof(struct sockaddr_in); // struct sockaddr_in的大小struct sockaddr_in clientaddr; // 客戶端的地址信息。clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);printf("客戶端(%s)已連接。\n",inet_ntoa(clientaddr.sin_addr));// 第5步:與客戶端通信,接收客戶端發(fā)過(guò)來(lái)的報(bào)文后,回復(fù)ok。char buffer[1024];while (1){memset(buffer,0,sizeof(buffer));if (recv(clientfd,buffer,sizeof(buffer),0)<=0) break; // 接收客戶端的請(qǐng)求報(bào)文。printf("接收:%s\n",buffer);strcpy(buffer,"ok");if (send(clientfd,buffer,strlen(buffer),0)<=0) break; // 向客戶端發(fā)送響應(yīng)結(jié)果。printf("發(fā)送:%s\n",buffer);}// 第6步:關(guān)閉socket,釋放資源。close(listenfd); close(clientfd); }

2、客戶端流程

1)創(chuàng)建客戶端的socket。

2)向服務(wù)器發(fā)起連接請(qǐng)求。

3)與服務(wù)端通信,發(fā)送一個(gè)報(bào)文后等待回復(fù),然后再發(fā)下一個(gè)報(bào)文。

4)不斷的重復(fù)第3)步,直到全部的數(shù)據(jù)被發(fā)送完。

5)第4步:關(guān)閉socket,釋放資源。

示例(client.cpp)

#include <stdio.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>int main() {// 第1步:創(chuàng)建客戶端的socket。int sockfd = socket(AF_INET,SOCK_STREAM,0); // 第2步:向服務(wù)器發(fā)起連接請(qǐng)求。struct hostent* h; if ( (h = gethostbyname("118.89.50.198")) == 0 ) // 指定服務(wù)端的ip地址。{ perror("gethostbyname"); close(sockfd); return -1; }struct sockaddr_in servaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(5051); // 指定服務(wù)端的通信端口。memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);if (connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0) // 向服務(wù)端發(fā)起連接清求。{ perror("connect"); close(sockfd); return -1; }char buffer[1024];// 第3步:與服務(wù)端通信,發(fā)送一個(gè)報(bào)文后等待回復(fù),然后再發(fā)下一個(gè)報(bào)文。for (int ii=0;ii<3;ii++){memset(buffer,0,sizeof(buffer));sprintf(buffer,"這是第%d個(gè)超級(jí)女生,編號(hào)%03d。",ii+1,ii+1);if (send(sockfd,buffer,strlen(buffer),0)<=0) break; // 向服務(wù)端發(fā)送請(qǐng)求報(bào)文。printf("發(fā)送:%s\n",buffer);memset(buffer,0,sizeof(buffer));if (recv(sockfd,buffer,sizeof(buffer),0)<=0) break; // 接收服務(wù)端返回的結(jié)果。printf("接收:%s\n",buffer);}// 第4步:關(guān)閉socket,釋放資源。close(sockfd); }

在運(yùn)行程序之前,必須保證服務(wù)器的防火墻已經(jīng)開(kāi)通了網(wǎng)絡(luò)訪問(wèn)策略

五、注解

1、 說(shuō)明

在socket通信的客戶端和服務(wù)器的程序里,有多種數(shù)據(jù)結(jié)構(gòu),調(diào)用了多個(gè)函數(shù),涉及到很多方面的知識(shí),這篇文章主要是了解socket通信的過(guò)程、每段代碼的用途和函數(shù)調(diào)用的功能,對(duì)于一些復(fù)雜的結(jié)構(gòu)體和函數(shù)的參數(shù),我會(huì)在另一篇文章詳細(xì)講解。

2、服務(wù)端程序綁定地址

如果服務(wù)器有多個(gè)網(wǎng)卡,多個(gè)IP地址,socket通信可以指定用其中一個(gè)地址來(lái)進(jìn)行通信,也可以任意ip地址。

1)指定ip地址的代碼

m_servaddr.sin_addr.s_addr = inet_addr("192.168.149.129"); // 指定ip地址

2)任意ip地址的代碼

m_servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 本主機(jī)的任意ip地址

在實(shí)際開(kāi)發(fā)中,采用任意ip地址的方式比較多。

3、服務(wù)端程序綁定的通信端口

m_servaddr.sin_port = htons(5000); // 通信端口

4、客戶端程序指定服務(wù)端的ip地址

struct hostent* h; // ip地址信息的數(shù)據(jù)結(jié)構(gòu) if ( (h = gethostbyname("192.168.149.129")) == 0 ) { perror("gethostbyname"); close(sockfd); return -1; }

5、客戶端程序指定服務(wù)端的通信端口

servaddr.sin_port = htons(5000);

6、send函數(shù)

send函數(shù)用于把數(shù)據(jù)通過(guò)socket發(fā)送給對(duì)端。不論是客戶端還是服務(wù)端,應(yīng)用程序都用send函數(shù)來(lái)向TCP連接的另一端發(fā)送數(shù)據(jù)。

函數(shù)聲明:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

sockfd為已建立好連接的socket。

buf為需要發(fā)送的數(shù)據(jù)的內(nèi)存地址,可以是C語(yǔ)言基本數(shù)據(jù)類型變量的地址,也可以數(shù)組、結(jié)構(gòu)體、字符串,內(nèi)存中有什么就發(fā)送什么。

len需要發(fā)送的數(shù)據(jù)的長(zhǎng)度,為buf中有效數(shù)據(jù)的長(zhǎng)度。

flags填0, 其他數(shù)值意義不大。

函數(shù)返回已發(fā)送的字符數(shù)。出錯(cuò)時(shí)返回-1,錯(cuò)誤信息errno被標(biāo)記。

注意,就算是網(wǎng)絡(luò)斷開(kāi),或socket已被對(duì)端關(guān)閉,send函數(shù)不會(huì)立即報(bào)錯(cuò),要過(guò)幾秒才會(huì)報(bào)錯(cuò)。

如果send函數(shù)返回的錯(cuò)誤(<=0),表示通信鏈路已不可用。

7、recv函數(shù)

recv函數(shù)用于接收對(duì)端socket發(fā)送過(guò)來(lái)的數(shù)據(jù)。

recv函數(shù)用于接收對(duì)端通過(guò)socket發(fā)送過(guò)來(lái)的數(shù)據(jù)。不論是客戶端還是服務(wù)端,應(yīng)用程序都用recv函數(shù)接收來(lái)自TCP連接的另一端發(fā)送過(guò)來(lái)數(shù)據(jù)。

函數(shù)聲明:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

sockfd為已建立好連接的socket。

buf為用于接收數(shù)據(jù)的內(nèi)存地址,可以是C語(yǔ)言基本數(shù)據(jù)類型變量的地址,也可以數(shù)組、結(jié)構(gòu)體、字符串,只要是一塊內(nèi)存就行了。

len需要接收數(shù)據(jù)的長(zhǎng)度,不能超過(guò)buf的大小,否則內(nèi)存溢出。

flags填0, 其他數(shù)值意義不大。

如果socket的對(duì)端沒(méi)有發(fā)送數(shù)據(jù),recv函數(shù)就會(huì)等待,如果對(duì)端發(fā)送了數(shù)據(jù),函數(shù)返回接收到的字符數(shù)。出錯(cuò)時(shí)返回-1,錯(cuò)誤信息errno被標(biāo)記。如果socket被對(duì)端關(guān)閉,返回值為0。

如果recv函數(shù)返回的錯(cuò)誤(<=0),表示通信通道已不可用。

8、服務(wù)端有兩個(gè)socket

對(duì)服務(wù)端來(lái)說(shuō),有兩個(gè)socket,一個(gè)是用于監(jiān)聽(tīng)的socket,還有一個(gè)就是客戶端連接成功后,由accept函數(shù)創(chuàng)建的用于與客戶端收發(fā)報(bào)文的socket。

9、程序退出時(shí)先關(guān)閉socket

socket是系統(tǒng)資源,操作系統(tǒng)打開(kāi)的socket數(shù)量是有限的(默認(rèn)是1024,具體可以通過(guò)ulimit -a命令查看),在程序退出之前必須關(guān)閉已打開(kāi)的socket,就像關(guān)閉文件指針一樣,就像delete已分配的內(nèi)存一樣,極其重要。

值得注意的是,關(guān)閉socket的代碼不能只在main函數(shù)的最后,那是程序運(yùn)行的理想狀態(tài),還應(yīng)該在main函數(shù)的每個(gè)return之前關(guān)閉。

六、用到的庫(kù)函數(shù)

1、socket()

socket函數(shù)用于創(chuàng)建一個(gè)新的socket,也就是向系統(tǒng)申請(qǐng)一個(gè)socket資源。socket函數(shù)用戶客戶端和服務(wù)端。

函數(shù)聲明:

int socket(int domain, int type, int protocol);

參數(shù)說(shuō)明:

domain:協(xié)議域,又稱協(xié)議族(family)。常用的協(xié)議族有AF_INET、AF_INET6、AF_LOCAL(或稱AF_UNIX,Unix域Socket)、AF_ROUTE等。協(xié)議族決定了socket的地址類型,在通信中必須采用對(duì)應(yīng)的地址,如AF_INET決定了要用ipv4地址(32位的)與端口號(hào)(16位的)的組合、AF_UNIX決定了要用一個(gè)絕對(duì)路徑名作為地址。

type:指定socket類型。常用的socket類型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式socket(SOCK_STREAM)是一種面向連接的socket,針對(duì)于面向連接的TCP服務(wù)應(yīng)用。數(shù)據(jù)報(bào)式socket(SOCK_DGRAM)是一種無(wú)連接的socket,對(duì)應(yīng)于無(wú)連接的UDP服務(wù)應(yīng)用。

protocol:指定協(xié)議。常用協(xié)議有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分別對(duì)應(yīng)TCP傳輸協(xié)議、UDP傳輸協(xié)議、STCP傳輸協(xié)議、TIPC傳輸協(xié)議。

第一個(gè)參數(shù)填A(yù)F_INET,第二個(gè)參數(shù)只能填SOCK_STREAM,第三個(gè)參數(shù)只能填0。

除非系統(tǒng)資源耗盡,socket函數(shù)一般不會(huì)返回失敗。

2、gethostbyname()

把ip地址或域名轉(zhuǎn)換為hostent 結(jié)構(gòu)體表達(dá)的地址。

函數(shù)聲明:

struct hostent *gethostbyname(const char *name);

參數(shù)name,域名或者主機(jī)名,例如"192.168.1.3"、"www.baidu.com"等。

返回值:如果成功,返回一個(gè)hostent結(jié)構(gòu)指針,失敗返回NULL。

gethostbyname只用于客戶端。

gethostbyname只是把字符串的ip地址轉(zhuǎn)換為結(jié)構(gòu)體的ip地址,只要地址格式?jīng)]錯(cuò),一般不會(huì)返回錯(cuò)誤。函數(shù)失敗不會(huì)設(shè)置errno的值。

3、connect()

向服務(wù)器發(fā)起連接請(qǐng)求。

函數(shù)聲明:

int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);

函數(shù)說(shuō)明:connect函數(shù)用于將參數(shù)sockfd 的socket 連至參數(shù)serv_addr
指定的服務(wù)端,參數(shù)addrlen為sockaddr的結(jié)構(gòu)長(zhǎng)度。

返回值:成功則返回0, 失敗返回-1, 錯(cuò)誤原因存于errno 中。

connect函數(shù)只用于客戶端。

如果服務(wù)端的地址錯(cuò)了,或端口錯(cuò)了,或服務(wù)端沒(méi)有啟動(dòng),connect一定會(huì)失敗。

4、bind()

服務(wù)端把用于通信的地址和端口綁定到socket上。

函數(shù)聲明:

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

參數(shù)sockfd,需要綁定的socket。

參數(shù)addr,存放了服務(wù)端用于通信的地址和端口。

參數(shù)addrlen表示addr結(jié)構(gòu)體的大小。

如果綁定的地址錯(cuò)誤,或端口已被占用,bind函數(shù)一定會(huì)報(bào)錯(cuò),否則一般不會(huì)返回錯(cuò)誤。

5、listen()

listen函數(shù)把主動(dòng)連接套接字變?yōu)楸粍?dòng)連接的套接字,使得這個(gè)socket可以接受其它socket的連接請(qǐng)求,從而成為一個(gè)服務(wù)端的socket。

函數(shù)聲明:

int listen(int sockfd, int backlog);

返回:0-成功, -1-失敗

sockfd:已經(jīng)被bind過(guò)的套接字。socket函數(shù)返回的套接字是一個(gè)主動(dòng)連接的套接字,在服務(wù)端的編程中,程序員希望這個(gè)套接字可以接受外來(lái)的連接請(qǐng)求,也就是被動(dòng)等待客戶端來(lái)連接。由于系統(tǒng)默認(rèn)時(shí)認(rèn)為一個(gè)套接字是主動(dòng)連接的,所以需要通過(guò)某種方式來(lái)告訴系統(tǒng),程序員通過(guò)調(diào)用listen函數(shù)來(lái)完成這件事。

backlog:這個(gè)參數(shù)涉及到一些網(wǎng)絡(luò)的細(xì)節(jié),填5、10都行,一般不超過(guò)30。

當(dāng)調(diào)用listen之后,服務(wù)端的套接字就可以調(diào)用accept來(lái)接受客戶端的連接請(qǐng)求。

listen函數(shù)一般不會(huì)返回錯(cuò)誤。

6、accept()

服務(wù)端接受客戶端的連接。

函數(shù)聲明:

int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

sockfd:已經(jīng)被listen過(guò)的套接字。

addr: 用于存放客戶端的地址信息,用sockaddr結(jié)構(gòu)體表達(dá),如果不需要客戶端的地址,可以填0。

addrlen:用于存放addr參數(shù)的長(zhǎng)度,如果addr為0,addrlen也填0。

accept:函數(shù)等待客戶端的連接,如果沒(méi)有客戶端連上來(lái),它就一直等待,這種方式稱之為阻塞。

accept等待到客戶端的連接后,創(chuàng)建一個(gè)新的套接字,函數(shù)返回值就是這個(gè)新的套接字,服務(wù)端使用這個(gè)新的套接字和客戶端進(jìn)行報(bào)文的收發(fā)。

accept在等待的過(guò)程中,如果被中斷或其它的原因,函數(shù)返回-1,表示失敗,如果失敗,可以重新accept。

七:注意事項(xiàng)

listen()、connect0和accept()函數(shù)

1)服務(wù)端在調(diào)用listen()之前,客戶端不能向服務(wù)端發(fā)起連接請(qǐng)求。
2)服務(wù)端調(diào)用listen()函數(shù)后,服務(wù)端的socket開(kāi)始監(jiān)聽(tīng)客戶端的連接。
3)客戶端調(diào)用connect()函數(shù)向服務(wù)端發(fā)起連接請(qǐng)求。
4)在TCP底層,客戶端和服務(wù)端握手后建立起通信通道,如果有多個(gè)客戶端請(qǐng)求,在服務(wù)端就會(huì)形成一個(gè)已準(zhǔn)備好的連接隊(duì)列。
5)服務(wù)端調(diào)用accept()函數(shù)從隊(duì)列中獲取一個(gè)已準(zhǔn)備好的連接,函數(shù)返回一個(gè)新的socket, 新的socket用于與客戶端通信,listen的socket只負(fù)責(zé)監(jiān)聽(tīng)客戶端的連接請(qǐng)求。

本文參考c語(yǔ)言技術(shù)網(wǎng)的文章加以總結(jié)。

總結(jié)

以上是生活随笔為你收集整理的简单的socket通信实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 伊人久久97 | 亚洲小说图片区 | 日本一区二区三区视频在线观看 | 亚洲国产视频一区二区 | 97国产精东麻豆人妻电影 | 91精品国产综合久久久蜜臀粉嫩 | 欧美暧暧视频 | 色婷婷激情综合 | 欧美片17c07.com | 国产精品第一区 | 全国男人的天堂网 | 日韩精品视频一区二区 | 国 产 黄 色 大 片 | 超在线视频 | 秋霞福利视频 | 国产无遮挡免费观看视频网站 | 韩国禁欲系高级感电影 | 超清纯大学生白嫩啪啪 | 丰满少妇xbxb毛片日本 | 超碰在线91 | 91成人在线视频 | 亚洲欧洲综合在线 | av网子| 日韩有码专区 | 一个人看的www日本高清视频 | 中文在线a√在线 | 国产亚洲视频在线 | 久久精品在线免费观看 | 丁香激情六月 | 日日夜夜天天综合 | 国产又爽又黄又嫩又猛又粗 | 国产精品一二三四五区 | 99久久视频 | 日本熟妇一区二区 | 91亚洲综合 | 亚洲视频成人 | 韩国一二三区 | 99一区二区三区 | 麻豆午夜视频 | 尤物视频在线播放 | 国产女主播一区二区三区 | 干b视频在线观看 | 香蕉av一区二区 | 狠狠亚洲| 国产女厕一区二区三区在线视 | 夜夜春影院 | 免费黄色链接 | 国产精品国产三级国产普通话对白 | 中文字幕在线观看精品 | 成人免费看视频 | 亚洲经典av | 中文字幕在线观看网 | 光棍福利视频 | 亚洲免费综合 | aa黄色片| 中文字幕+乱码+中文乱码91 | 日韩美女中文字幕 | 四色永久访问 | a级片日本 | 操操操免费视频 | 欧美成人性生活片 | 不卡av电影在线观看 | 人妻在客厅被c的呻吟 | 在线免费观看一区二区 | 一区二区三区四区高清视频 | 免费特级黄毛片 | 中文字幕视频一区二区 | 午夜小视频网站 | 亚洲av无码一区二区三区dv | 午夜影院久久 | 日韩av专区片 | 色天天色综合 | 性激情视频 | 色噜噜狠狠一区二区三区牛牛影视 | 久久com | 97超碰成人| 色哟哟一区 | 狠狠狠狠狠干 | 日本黄大片在线观看 | 亚洲清纯国产 | 手机av在线免费观看 | 亚洲精品乱码久久久久 | 亚洲色图另类小说 | av网址网站 | 日韩视频a | 性视频播放免费视频 | 亚洲免费精品视频在线观看 | 欧美大片在线看 | a毛片网站 | 国产人人爽| 天天看片天天干 | 视频在线观看免费大片 | 天天草比| av在线影音| 爱啪啪导航 | 欧美成人一区二区 | 黄色日批网站 | 亚洲美女啪啪 | 日本视频免费 |