基于Linux進程間通信方式之socket(TCP/IP)通信

一陈轿、進程

1.進程概念

對于操作系統(tǒng)而言圈纺,每運行一個程序,系統(tǒng)會創(chuàng)建一個進程麦射,在這個過程中蛾娶,進行資源分配和調(diào)度。

2.進程通信

由于在操作系統(tǒng)內(nèi)部潜秋,不同進程間相互獨立運作蛔琅,其內(nèi)部資源獨立,在特殊需求下要求進程間相互通信峻呛,而進程通信便是進程間進行信息交流和同步的機制罗售。

3.進程通信原理

每個進程的用戶地址空間都是獨立的,一般而言是不能互相訪問的钩述,但內(nèi)核空間是每個進程都共享的寨躁,所以進程之間要通信必須通過內(nèi)核。如圖1所示:


如圖1


二牙勘、Socket基于TCP/IP通信模型

Socket通信不僅可以跨網(wǎng)絡與不同主機的進程間通信职恳,還可以在同主機上進程間通信。

1.不同主機間的通信流程方面。

如圖2所示放钦。


如圖2

第一步:服務端和客戶端各自初始化socket。

int socket(int domain, int type, int protocal)

詳細介紹創(chuàng)建socket方法:

domain:即協(xié)議域葡幸,又稱為協(xié)議族最筒,常用的協(xié)議族有AF_INET贺氓、AF_INET6蔚叨、AF_LOCAL(或稱AF_UNIX床蜘,Unix域socket)、AF_ROUTE等蔑水。

協(xié)議族決定了socket的地址類型邢锯,在通信中必須采用對應的地址,如AF_INET決定了要用ipv4地址(32位的)與端口號(16位的)的組合搀别、AF_UNIX決定了要用一個絕對路徑名作為地址丹擎。

type:指定socket類型。常用的socket類型:SOCK_STREAM歇父、SOCK_DGRAM蒂培、SOCK_RAW、SOCK_PACKET榜苫、SOCK_SEQPACKET等护戳。

protocol:指定協(xié)議。常用的協(xié)議:IPPROTO_TCP垂睬、IPPTOTO_UDP媳荒、IPPROTO_SCTP、IPPROTO_TIPC等驹饺,它們分別對應TCP傳輸協(xié)議钳枕、UDP傳輸協(xié)議、STCP傳輸協(xié)議赏壹、TIPC傳輸協(xié)議鱼炒。

根據(jù)type和protocol組合的不同又分為SOCK_STREAM和SOCK_SEQPACKET是面向鏈接的類型,因此protocol也應該選擇面向鏈接的tcp協(xié)議蝌借。當protocol為0時田柔,會自動選擇type對應的默認協(xié)議。

第二步驟:服務端綁定IP地址和端口骨望。

int bind(int socket, const struct sockaddr *address, socklen_t address_len)

sockfd:是通過socket()函數(shù)創(chuàng)建的唯一標識一個socket硬爆。bind()函數(shù)就是給這個描述字綁定一個名字。

addr:指向要綁定給sockfd的協(xié)議地址擎鸠。這個地址結構根據(jù)地址創(chuàng)建socket時的地址協(xié)議族的不同而不同缀磕,如ipv4對應的是:

struct sockaddr_in {

????sa_family_t ???sin_family; /* address family: AF_INET */

????in_port_t ?????sin_port; ??/* port in network byte order */

????struct in_addr sin_addr; ??/* internet address */

};

/* Internet address. */

struct in_addr {

????uint32_t ??????s_addr; ????/* address in network byte order */

};

第三步:服務端進行監(jiān)聽;

int listen(int sockfd, int backlog)

第四步:服務端調(diào)用accept劣光,客戶端調(diào)用 connect袜蚕,建立鏈接。

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

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

第五步:客戶端調(diào)用send寫入數(shù)據(jù)绢涡;服務端調(diào)用 recv讀取數(shù)據(jù)牲剃。

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

int recv( SOCKET s, char FAR *buf, int len, int flags);

2.同主機內(nèi)部進程間的通信

UNIX Domain Socket是在socket的框架上發(fā)展出一種IPC機制,盡管網(wǎng)絡socket只要將lookback地址設置為127.0.0.1雄可,便能進行同一臺主機的進程間通訊凿傅,但是UNIX Domain Socket用于IPC更有效率:不需要經(jīng)過網(wǎng)絡協(xié)議棧缠犀,不需要打包拆包、計算校驗和聪舒、維護序號和應答等辨液,只是將應用層數(shù)據(jù)從一個進程拷貝到另一個進程。UNIX Domain Socket在通信流程上與socket流程保持一致箱残,但是部分有所區(qū)別滔迈。

SOCK_STREAM 式本地套接字的通信雙方均需要具有本地地址,其中服務器端的本地地址需要明確指定被辑,指定方法是使用 struct sockaddr_un 類型的變量燎悍。

#define UNIX_PATH_MAX ???108

struct sockaddr_un {

????sa_family_t sun_family; ??????????????/* AF_UNIX */

????char ???????sun_path[UNIX_PATH_MAX]; ?/* pathname */

};

需要注意的是其socket進程通信命名方式有兩種:

第一種是普通的命名:

//name the server socket

????server_addr.sun_family = AF_UNIX;

????strcpy(server_addr.sun_path,"/tmp/UNIX.domain");

????server_len = sizeof(struct sockaddr_un);

client_len = server_len;1

第二種是抽象命名空間:

#define SERVER_NAME @socket_server

//name the socket

??server_addr.sun_family = AF_UNIX;

??strcpy(server_addr.sun_path, SERVER_NAME);

??server_addr.sun_path[0]=0;

??//server_len = sizeof(server_addr);

??server_len = strlen(SERVER_NAME) + offsetof(struct sockaddr_un, sun_path);

二、代碼采用同主機的進程通信方式盼理,即UNIX Domain Socket方式间涵。

以下部分主要代碼:

客戶端源文件:

#include "mainwindow.h"

#include "ui_mainwindow.h"

#include<qdebug.h>

extern "C"

{

#include <sys/types.h>

#include <sys/un.h>

#include <sys/socket.h>

#include <stdio.h>

#include <unistd.h>

#include <errno.h>

}

MainWindow::MainWindow()

{

}

void ?MainWindow::thread_run()

{ ??

????while(1) ?

????{

????????sleep(1);

????????if(_need_to_connect)// 需要重連接

????????{

????????????qDebug()<<"client thread";

????????????_deconstruct_socket();

????????????_construct_socket();

????????}

????????else

????????{ ?

????????????senddata();

????????}

????}

}


void MainWindow::senddata()

{

????char snd_buf[1024];

????qDebug()<<"client connect sucessful!";

????memset(snd_buf,0,1024);

????strcpy(snd_buf,"the message sended 戶!");

????//send info server

????ssize_t len=write(_sockfd,snd_buf,sizeof(snd_buf));

????if(len<=0)

????{ ?

????????perror("send data ?error!");

????????_need_to_connect=true;

????}

}


int MainWindow::_construct_socket()

{

????if(-1 != _sockfd)

????{ ?

????????return FORK_CTRL_RET_SOCK_ERR;

????}

????struct sockaddr_un addr;

????socklen_t addr_len(sizeof(addr));

????int ret(-1);


????// 設置阻塞方式send的超時

????struct timeval send_timeout = {1, 0}; //1秒

????// 設置阻塞方式connect的超時

????struct timeval connect_timeout = {5, 0}; // 5秒

????// socket

????_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

????if(-1 == _sockfd)

????{

????????return FORK_CTRL_RET_SOCK_ERR;

????}

????addr.sun_family = AF_UNIX;

????strncpy(addr.sun_path, SOCK_PROTECT, sizeof(addr.sun_path));

????// 設置阻塞方式下的connect超時

????setsockopt(_sockfd, SOL_SOCKET, SO_SNDTIMEO,

????(char *)&connect_timeout, sizeof(struct timeval));

????// send是在Mx主線程中執(zhí)行的,設置阻塞方式下的send超時

????setsockopt(_sockfd, SOL_SOCKET, SO_SNDTIMEO,

????(char *)&send_timeout, sizeof(struct timeval));

// connect

????ret = connect(_sockfd, (struct sockaddr *)&addr, addr_len);

????if(-1 == ret)

????{

????????return FORK_CTRL_RET_SOCK_ERR;

????}

????qDebug()<<"client connect succes!";

????// 設置為非阻塞工作模式榜揖,必須等到connect返回以后再設置勾哩。

????//fcntl(_sockfd, F_SETFL, O_NONBLOCK);

????_need_to_connect = false;

????return FORK_CTRL_RET_SUCCESS;

}


/******************************************************************************

釋放和守護進程通信的socket及相關資源

******************************************************************************/

void MainWindow::_deconstruct_socket()

{ ?

????::close(_sockfd);

????// 即使sockfd為-1也沒關系。 ??

????_sockfd = -1;

????sleep(3);

????// 關閉socket后需要休眠举哟,以確保網(wǎng)絡資源的釋放

????return;

}

MainWindow::~MainWindow()

{

}


服務端源文件:

#include "mainwindow.h"

#include "ui_mainwindow.h"

#include<qdebug.h>

extern "C"

{

#include <sys/types.h>

#include <sys/un.h>

#include <sys/socket.h>

#include <stdio.h>


#include <unistd.h>

#include <errno.h>

}

MainWindow::MainWindow()

{ ??

_need_to_connect=true;

_accept_fd=-1;

_sockfd=-1;

}

void ?MainWindow::thread_run()

{

while(1)

{

sleep(1);

if(_need_to_connect) //需要重連接

{

//QDebug("1111111111111111");

qDebug()<<"sever";

_deconstruct_socket();

_construct_socket();

}

else

{

getdata();

senddata();

}

}

}

void MainWindow::senddata()

{

????char snd_buf[1024];

????memset(snd_buf,0,1024);

????strcpy(snd_buf,"it's ddddddddd!");

//send info server

????qDebug()<<"dddddddd";

????ssize_t len=write(_accept_fd,snd_buf,sizeof(snd_buf));

????qDebug()<<"sever fd="<<_accept_fd;

????//debug("client is sending data !\n");

}

void MainWindow::getdata()

{



????char recv_buf[1024];

????memset(recv_buf,0,1024);

????int num=read(_accept_fd,recv_buf,sizeof(recv_buf));

????//printf("Message from client (%d)) :%s/n",num,recv_buf);

????qDebug()<<"receive data len="<<num;

????if(num<=0)

????{

????????unlink(SOCK_PROTECT);

????????_need_to_connect=true;

????????return;

????}

????qDebug()<<"see:"<<recv_buf;

}

int MainWindow::_construct_socket()

{

????if(-1 != _sockfd)

????{

????????return FORK_CTRL_RET_SOCK_ERR;

????}

????struct sockaddr_un addr;

????socklen_t addr_len(sizeof(addr));

????struct sockaddr_un clt_addr;

????socklen_t clt_addr_len(sizeof(clt_addr));

????int ret(-1);

//設置阻塞方式send的超時

????struct timeval send_timeout = {1, 0}; //1秒

//設置阻塞方式connect的超時

????struct timeval connect_timeout = {5, 0}; // 5秒

????// socket

????_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

????if(-1 == _sockfd)

????{

????????return FORK_CTRL_RET_SOCK_ERR;

????}

????unlink(SOCK_PROTECT);

????addr.sun_family = AF_UNIX;

????strncpy(addr.sun_path, SOCK_PROTECT, sizeof(addr.sun_path));

????qDebug()<<"sever connecting!";

#if 1

????ret=bind(_sockfd,(struct sockaddr*)&addr,sizeof(addr));

????if(ret==-1)

????{



????????unlink(SOCK_PROTECT);

????????return FORK_CTRL_RET_SOCK_ERR;

????}

????//listen sockfd

????ret=listen(_sockfd,1);

????if(ret==-1)

????{

????????unlink(SOCK_PROTECT);

????????return FORK_CTRL_RET_SOCK_ERR;

????}

qDebug()<<"server waiting!";

????//have connect request use accept

????_accept_fd=accept(_sockfd,(struct sockaddr*)&clt_addr,&clt_addr_len);

????qDebug()<<"server connect succes!";

????if(_accept_fd<0)

????{

????????unlink(SOCK_PROTECT);

????????return FORK_CTRL_RET_SOCK_ERR;

????}

#endif

????qDebug()<<"sever connect sucessful!";

//設置為非阻塞工作模式思劳,必須等到connect返回以后再設置。

????????//fcntl(_sockfd, F_SETFL, O_NONBLOCK);

????_need_to_connect = false;

????return FORK_CTRL_RET_SUCCESS;

}

/******************************************************************************

釋放和守護進程通信的socket及相關資源

******************************************************************************/

void MainWindow::_deconstruct_socket()

{

::close(_sockfd);//即使sockfd為-1也沒關系妨猩。

????_sockfd = -1;

::close(_accept_fd);//即使sockfd為-1也沒關系潜叛。

????_accept_fd = -1;

sleep(3);//關閉socket后需要休眠,以確保網(wǎng)絡資源的釋放

????return;

}

MainWindow::~MainWindow()

{

}

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壶硅,一起剝皮案震驚了整個濱河市威兜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌庐椒,老刑警劉巖椒舵,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異约谈,居然都是意外死亡笔宿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門棱诱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泼橘,“玉大人,你說我怎么就攤上這事迈勋【婷穑” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵靡菇,是天一觀的道長重归。 經(jīng)常有香客問我米愿,道長,這世上最難降的妖魔是什么提前? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮泳唠,結果婚禮上狈网,老公的妹妹穿的比我還像新娘。我一直安慰自己笨腥,他們只是感情好拓哺,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著脖母,像睡著了一般士鸥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谆级,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天烤礁,我揣著相機與錄音,去河邊找鬼肥照。 笑死脚仔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的舆绎。 我是一名探鬼主播鲤脏,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吕朵!你這毒婦竟也來了猎醇?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤努溃,失蹤者是張志新(化名)和其女友劉穎硫嘶,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梧税,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡音半,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贡蓖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曹鸠。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖斥铺,靈堂內(nèi)的尸體忽然破棺而出彻桃,到底是詐尸還是另有隱情,我是刑警寧澤晾蜘,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布邻眷,位于F島的核電站眠屎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏肆饶。R本人自食惡果不足惜改衩,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望驯镊。 院中可真熱鬧葫督,春花似錦、人聲如沸板惑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冯乘。三九已至洽胶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間裆馒,已是汗流浹背姊氓。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喷好,地道東北人他膳。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像绒窑,于是被迫代替她去往敵國和親棕孙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容