一陈轿、進程
1.進程概念
對于操作系統(tǒng)而言圈纺,每運行一個程序,系統(tǒng)會創(chuàng)建一個進程麦射,在這個過程中蛾娶,進行資源分配和調(diào)度。
2.進程通信
由于在操作系統(tǒng)內(nèi)部潜秋,不同進程間相互獨立運作蛔琅,其內(nèi)部資源獨立,在特殊需求下要求進程間相互通信峻呛,而進程通信便是進程間進行信息交流和同步的機制罗售。
3.進程通信原理
每個進程的用戶地址空間都是獨立的,一般而言是不能互相訪問的钩述,但內(nèi)核空間是每個進程都共享的寨躁,所以進程之間要通信必須通過內(nèi)核。如圖1所示:
二牙勘、Socket基于TCP/IP通信模型
Socket通信不僅可以跨網(wǎng)絡與不同主機的進程間通信职恳,還可以在同主機上進程間通信。
1.不同主機間的通信流程方面。
如圖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()
{
}