Unix domain socket(unix域协议)是在单个主机上执行客户/服务器通信的一种方法,是进程之间本地通信IPC的一种。它提供面向流和面向数据包两种API接口,类似于TCP和UDP,面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。
因需要使用本地socket文件进行通信,故总结了该部分内容。
首先,socket的连接地址是一个文件的路径而不是一个包含服务地址和端口号的元组;第二,当socket关闭后,socket文件不会被删除,该文件代表一个永久的socket连接,所以每次服务器启动时,都需要手动删除该文件。第三,address family需要使用AF_UNIX而不是AF_INET,因为AF_UNIX会在bind时会生成一个文件,而AF_INET不会。
(1)TCP/IP server
import socket
import sys
import os
server_address = './uds_socket'
# Make sure the socket does not already exist
try:
os.unlink(server_address)
except OSError:
if os.path.exists(server_address):
raise
# Create a UDS socket
sock = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
# Bind the socket to the port
print >>sys.stderr, 'starting up on %s' % server_address
sock.bind(server_address)
# Listen for incoming connections
sock.listen(1)
while True:
# Wait for a connection
print >>sys.stderr, 'waiting for a connection'
connection, client_address = sock.accept()
try:
print >>sys.stderr, 'connection from', client_address
# Receive the data in small chunks and retransmit it
while True:
data = connection.recv(16)
print >>sys.stderr, 'received "%s"' % data
if data:
print >>sys.stderr, 'sending data back to the client'
connection.sendall(data)
else:
print >>sys.stderr, 'no more data from', client_address
break
finally:
# Clean up the connection
connection.close()
(2)TCP/IP client
import socket
import sys
# Create a UDS socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# Connect the socket to the port where the server is listening
server_address = './uds_socket'
print >>sys.stderr, 'connecting to %s' % server_address
try:
sock.connect(server_address)
except socket.error, msg:
print >>sys.stderr, msg
sys.exit(1)
try:
# Send data
message = 'This is the message. It will be repeated.'
print >>sys.stderr, 'sending "%s"' % message
sock.sendall(message)
amount_received = 0
amount_expected = len(message)
while amount_received < amount_expected:
data = sock.recv(16)
amount_received += len(data)
print >>sys.stderr, 'received "%s"' % data
finally:
print >>sys.stderr, 'closing socket'
sock.close()
(3)运行结果:
(4)可以控制多socket文件的访问权限
(5)父子进程间通信
socketpair()创建一对连接socket,用于父子进程间的通信。
import socket
import os
parent, child = socket.socketpair()
pid = os.fork()
if pid:
print 'in parent, sending message'
child.close()
parent.sendall('ping')
response = parent.recv(1024)
print 'response from child:', response
parent.close()
else:
print 'in child, waiting for message'
parent.close()
message = child.recv(1024)
print 'message from parent:', message
child.sendall('pong')
child.close()
使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。
UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
(1)tcp_server.c:
#include
#include
#include
#include
#include
#include
int main()
{
/* delete the socket file */
unlink("server_socket");
/* create a socket */
int server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un server_addr;
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, "server_socket");
/* bind with the local file */
bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
/* listen */
listen(server_sockfd, 5);
char ch;
int client_sockfd;
struct sockaddr_un client_addr;
socklen_t len = sizeof(client_addr);
while(1)
{
printf("server waiting:\n");
/* accept a connection */
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);
/* exchange data */
read(client_sockfd, &ch, 1);
printf("get char from client: %c\n", ch);
++ch;
write(client_sockfd, &ch, 1);
/* close the socket */
close(client_sockfd);
}
return 0;
}
(2)tcp_client.c:
#include
#include
#include
#include
#include
#include
int main()
{
/* create a socket */
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un address;
address.sun_family = AF_UNIX;
strcpy(address.sun_path, "server_socket");
/* connect to the server */
int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
if(result == -1)
{
perror("connect failed: ");
exit(1);
}
/* exchange data */
char ch = 'A';
write(sockfd, &ch, 1);
read(sockfd, &ch, 1);
printf("get char from server: %c\n", ch);
/* close the socket */
close(sockfd);
return 0;
}
(3)运行结果:
QString HostConnectVM::getResult(QString command, QString socketFile)
{
QString resultStr = "";
QFileInfo fileInfo(socketFile);
if (!fileInfo.exists()) {
qDebug() << "socketFile do not exist, please set socketfile first";
resultStr = "error5";
return resultStr;
}
/* create a socket */
struct sockaddr_un address;
address.sun_family = AF_UNIX;
strcpy(address.sun_path, socketFile.toStdString().data());
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1) {
qDebug() << "create socket failed";
resultStr = "error1";
return resultStr;
}
/* connect to the server */
int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
if(result == -1) {
qDebug() << "connect socket failed";
resultStr = "error2";
return resultStr;
}
if((result = write(sockfd, command.toStdString().data(), command.length())) != command.length()) {
qDebug() << "write socket failed";
resultStr = "error3";
return resultStr;
}
char buff[10240] = {0};
if ((result = read(sockfd, buff, 10240)) == -1) {
qDebug() << "read socket failed";
resultStr = "error4";
return resultStr;
}
resultStr = buff;
resultStr = resultStr.trimmed();
close(sockfd);
return resultStr;
}
(1)可参考文章http://pymotw.com/2/socket/uds.html和http://blog.csdn.net/bingqingsuimeng/article/details/8470029
(2)还需根据实际应用使用不同的代码,上述代码也须不断的完善调整。
(3)若有不足,请留言,在此先感谢!