Socket的中文翻譯為“插座”,在計(jì)算機(jī)世界里稱(chēng)為套接字。Socket最初是作為網(wǎng)絡(luò)上不同主機(jī)之間進(jìn)程的通信接口,后來(lái)應(yīng)用越來(lái)越廣,在同一主機(jī)上的不同進(jìn)程之間通信也可以用Socket。簡(jiǎn)單來(lái)說(shuō),當(dāng)網(wǎng)絡(luò)上不同主機(jī)之間的兩個(gè)進(jìn)程(A、B)采用Socket進(jìn)行通信時(shí),那么它們之間需要建立一個(gè)通信端點(diǎn),即創(chuàng)建Socket,創(chuàng)建Socket時(shí)就分配端口號(hào)和網(wǎng)絡(luò)地址。當(dāng)進(jìn)程A向進(jìn)程B發(fā)送數(shù)據(jù)時(shí),那么進(jìn)程A必須要知道進(jìn)程B的網(wǎng)絡(luò)地址及端口號(hào)。
Socket采用C/S模型進(jìn)行設(shè)計(jì)的,即Client/Server,面向客戶端—服務(wù)器模型。
每一個(gè)Socket都用一個(gè)半相關(guān)描述:
{協(xié)議,本地地址,本地端口}
一個(gè)完整的Socket則用一個(gè)相關(guān)描述:
{協(xié)議,本地地址,本地端口,遠(yuǎn)程地址,遠(yuǎn)程端口}
一、Socket的類(lèi)型
Socket有三種類(lèi)型:
1、字節(jié)流套接字(SOCK_STREAM)
字節(jié)流的套接字可以提供可靠的數(shù)據(jù)傳輸、面向連接的通訊流。數(shù)據(jù)按何種順序發(fā)送,就按何種順序接收。例如,當(dāng)我們按順序發(fā)送A-B-C,那么在數(shù)據(jù)到達(dá)接收端時(shí),它的順序也是A-B-C。字節(jié)流套接字采用的是TCP(Transmission Control Protocol)協(xié)議。保證了數(shù)據(jù)傳輸?shù)目煽啃浴?/p>
2、數(shù)據(jù)報(bào)套接字(SOCK_DGRAM)
數(shù)據(jù)報(bào)套接字定義了一種無(wú)連接的服務(wù)。所謂無(wú)連接服務(wù),簡(jiǎn)單來(lái)說(shuō),即在發(fā)送數(shù)據(jù)時(shí),無(wú)需在收發(fā)兩端建立類(lèi)似TCP那樣的握手連接,在發(fā)送時(shí),將數(shù)據(jù)打包,然后加上遠(yuǎn)程IP地址,即可把該數(shù)據(jù)包發(fā)送出去。
數(shù)據(jù)通過(guò)相互獨(dú)立的報(bào)文進(jìn)行傳輸。并且是無(wú)序的、不可靠的傳輸。
3、原始套接字(SOCK_ROW)
原始套接字是我們需要關(guān)心的,因?yàn)槲覀兊腟ocket CAN采用的即是原始套接字。該接口允許對(duì)較底層協(xié)議進(jìn)行操作,如IP、ICMP等。原始套接字常用于檢驗(yàn)新的協(xié)議實(shí)現(xiàn)或訪問(wèn)現(xiàn)有服務(wù)中配置的新設(shè)備。
套接字的工作流程如下:
先啟動(dòng)服務(wù)器,通過(guò)調(diào)用socket()函數(shù)建立一個(gè)套接字,然后調(diào)用bind()函數(shù)將該套接字和本地網(wǎng)絡(luò)地址聯(lián)系在一起,再調(diào)用listen()函數(shù)使套接字做好偵聽(tīng)的準(zhǔn)備,并規(guī)定它的請(qǐng)求隊(duì)列的長(zhǎng)度,之后就調(diào)用accept()函數(shù)來(lái)接收連接??蛻舳嗽诮⑻捉幼种缶涂烧{(diào)用 connect()和服務(wù)器建立連接。連接一旦建立,客戶端和服務(wù)器之間就可以通過(guò)調(diào)用recv()/recvfrom()函數(shù)和send()/sendto函數(shù)來(lái)進(jìn)行發(fā)收數(shù)據(jù)。最后,待數(shù)據(jù)傳送結(jié)束后,雙方調(diào)用close()函數(shù)關(guān)閉套接字。
下面我們來(lái)寫(xiě)兩個(gè)簡(jiǎn)單的基于Socket的CAN應(yīng)用程序,但是我們采用的是SOCK_ROW,因此在套接字工作流程上有區(qū)別于SOCK_STREAM和SOCK_DGRAM。由于Socket采用C/S模型進(jìn)行設(shè)計(jì)的,所以我們的這兩個(gè)程序也分別為Server和Client。
首先是server端的程序,我們需要寫(xiě)一個(gè)服務(wù)器的程序,該程序接收來(lái)自客戶端發(fā)來(lái)的數(shù)據(jù),代碼如下:
int can_recv()
{
int sock_fd;
unsigned long nbytes, len;
struct sockaddr_can addr;
struct ifreq ifr;
/*為了能夠接收CAN報(bào)文,我們需要定義一個(gè)CAN數(shù)據(jù)格式的結(jié)構(gòu)體變量*/
struct can_frame frame;
struct can_frame *ptr_frame;
/*建立套接字,設(shè)置為原始套接字,原始CAN協(xié)議*/
sock_fd = socket(PF_CAN,SOCK_RAW,CAN_RAW);
/*以下是對(duì)CAN接口進(jìn)行初始化,如設(shè)置CAN接口名,即當(dāng)我們用ifconfig命令時(shí)顯示的名字*/
strcpy(ifr.ifr_name,"can0");
ioctl(sock_fd, SIOCGIFINDEX, &ifr);
printf("can0 can_ifindex = %x\n",ifr.ifr_ifindex);
/*設(shè)置CAN協(xié)議*/
addr.can_family = AF_CAN;
addr.can_ifindex = 0;
/*將剛生成的套接字與網(wǎng)絡(luò)地址進(jìn)行綁定*/
bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
/*開(kāi)始接收數(shù)據(jù)*/
nbytes = recvfrom(sock_fd, &frame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr, &len);
/*get interface name of the received CAN frame*/
ifr.ifr_ifindex = addr.can_ifindex;
ioctl(sock_fd, SIOCGIFNAME, &ifr);
printf("Received a CAN frame from interface %s\n",ifr.ifr_name);
/*將接收到的CAN數(shù)據(jù)打印出來(lái),其中ID為標(biāo)識(shí)符,DLC為CAN的字節(jié)數(shù),DATA為1幀報(bào)文的字節(jié)數(shù)*/
printf("CAN frame:\n ID = %x\n DLC = %x\n" \
"DATA = %s\n",frame.can_id,frame.can_dlc,frame.data);
ptr_frame = &frame;
return 0;
}
接下來(lái)是CAN的發(fā)送程序,即客戶端,代碼如下:
int can_send()
{
int sock_fd;
unsigned long nbytes;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
/*建立套接字,設(shè)置為原始套接字,原始CAN協(xié)議*/
sock_fd = socket(PF_CAN,SOCK_RAW,CAN_RAW);
/*以下是對(duì)CAN接口進(jìn)行初始化,如設(shè)置CAN接口名,即當(dāng)我們用ifconfig命令時(shí)顯示的名字*/
strcpy((char *)(ifr.ifr_name), "can0");
ioctl(sock_fd, SIOCGIFINDEX, &ifr);
printf("can0 can_ifindex = %x\n", ifr.ifr_ifindex);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
/*將剛生成的套接字與CAN套接字地址進(jìn)行綁定*/
bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
/*設(shè)置CAN幀的ID號(hào),可區(qū)分為標(biāo)準(zhǔn)幀和擴(kuò)展幀的ID號(hào)*/
frame.can_id = 0x1122;
strcpy((char *)frame.data,"hello");
frame.can_dlc = strlen(frame.data);
printf("Send a CAN frame from interface %s\n", ifr.ifr_name);
/*開(kāi)始發(fā)送數(shù)據(jù)*/
nbytes = sendto(sock_fd, &frame, sizeof(struct can_frame), 0, (struct sockaddr*)&addr, sizeof(addr));
return 0;
}
上面兩個(gè)程序看完后,大家可能會(huì)有疑問(wèn),為什么這兩個(gè)程序沒(méi)有l(wèi)isten()和accept()函數(shù)呢?其實(shí)這兩個(gè)程序是獨(dú)立的運(yùn)行的,并不像字節(jié)流套接字(SOCK_STREAM)和數(shù)據(jù)報(bào)套接字(SOCK_DGRAM),需要先運(yùn)行服務(wù)器進(jìn)行偵聽(tīng)。SOCK_STREAM和SOCK_DGRAM的兩個(gè)server和client程序是通過(guò)網(wǎng)絡(luò)相互收發(fā)數(shù)據(jù)。而CAN的socket的server和client程序收發(fā)數(shù)據(jù)的對(duì)象是CAN總線。server從CAN總線上接收數(shù)據(jù),client將數(shù)據(jù)發(fā)到CAN總線上,當(dāng)CAN總線上有數(shù)據(jù)時(shí),server才能接收數(shù)據(jù),當(dāng)CAN總線空閑時(shí),client才能將數(shù)據(jù)發(fā)送出去。
以上是對(duì)套接字的簡(jiǎn)單理解,并附上socket CAN的簡(jiǎn)單上層應(yīng)用代碼。
-
CAN
+關(guān)注
關(guān)注
57文章
2756瀏覽量
463818 -
服務(wù)器
+關(guān)注
關(guān)注
12文章
9199瀏覽量
85524 -
Socket
+關(guān)注
關(guān)注
0文章
212瀏覽量
34722
原文標(biāo)題:為了能夠?qū)ocket CAN的深入理解,我們需要了解Socket的機(jī)制
文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論