socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。与流套接字不同,用类型SOCK_DGRAM指定的,它不需要建立连接和维持一个连接。本文示例代码为本地两个进程之间的通信,创建socket域为AF_UNIX。
服务端
首先服务器应用程序用系统调用socket来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。接下来,服务器进程会给套接字起个名字(监听),使用系统调用bind来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。然
后系统调用recvfrom来接收来自客户程序发送过来的数据。如果需要,服务器程序对数据进行相应的处理,再通过系统调用sendto把处理后的数据发送回客户程序。需要特别之处的chmod函数,如果没有改变文件的属性,服务端可以获得客户端发过来的信息,但是发送回客户端将出现errno=EADV的错误。即要实现双向通信,chmod函数的调用时很有必要的。
示例代码:
#include <sys/socket.h> #include <sys/un.h> #include <sys/types.h> #include <errno.h> int main(int argc ,char *argv[]) { int sockfd = 0; struct sockaddr_un addr; struct sockaddr_un from; socklen_t fromlen = sizeof(from); char *reply = NULL; const int reply_size = 40; char recv_buf[256] = ""; int reply_len = 0; char fname[10]="tmp/test"; unlink(fname); addr.sun_family = AF_UNIX; strcpy(addr.sun_path,fname); sockfd = socket(AF_UNIX,SOCK_DGRAM,0); if(sockfd < 0 ) { printf("socket error"); } if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)) < 0) { printf("bind error"); close(sockfd); return 0; } printf("Bind is ok\n"); if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { printf("chmod[ctrl_interface/ifname]"); } res = recvfrom(sockfd,recv_buf,sizeof(recv_buf),0,(struct sockaddr*)&from, &fromlen); recv_buf[res] = '\0'; printf("Recv: %s\n",recv_buf); reply = malloc(reply_size); if (reply == NULL) { sendto(sockfd, "FAIL\n", 5, 0, (struct sockaddr *) &from,fromlen); printf("malloc error.\n"); return 0; } memset(reply,0,reply_size); memcpy(reply, "OK.", 3); reply_len = 3; res = sendto(sockfd, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); printf("res=%d.errno = %d\n",res,errno); free(reply); close(sockfd); }客户端
首先客户端应用程序也是用系统调用socket来创建一个套接字,然后调用connect()连接就服务器。收发数据可用recvfrom()和sendto(),也可用recv()和send()。由于recvfrom和recv调用时阻塞的,但是我们又不想阻塞当前进程,即要用非阻塞方式调用,那么可用fcntl()来设定进程为非阻塞形式,select来查询服务端返回的信息。使用Select可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
#define PATH "tmp/test" int main(int argc ,char *argv[]) { int connect_fd = 0; struct sockaddr_un addr; int ret,res; int flags; char snd_buf[24],recv_buf[32]; fd_set rfds; struct timeval tv; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path,PATH); connect_fd = socket(AF_UNIX,SOCK_DGRAM,0); if(connect_fd < 0) { printf("socket error\n"); return 0; } //connect server ret=connect(connect_fd,(struct sockaddr*)&addr,sizeof(addr)); if(ret==-1) { printf("cannot connect to the server\n"); close(connect_fd); return 0; } flags = fcntl(connect_fd, F_GETFL); if (flags >= 0) { flags |= O_NONBLOCK; if (fcntl(connect_fd, F_SETFL, flags) < 0) { printf("fcntl(ctrl->s, O_NONBLOCK)"); } } memset(snd_buf,0,24); strcpy(snd_buf,"message from client"); //send info server send(connect_fd,snd_buf,sizeof(snd_buf),0); //read memset(recv_buf,0,32); for(;;){ tv.tv_sec = 10; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(connect_fd, &rfds); res = select(connect_fd + 1, &rfds, NULL, NULL, &tv); if (res < 0) return res; if (FD_ISSET(ctrl->s, &rfds)) { res = recv(connect_fd, recv_buf, sizeof(recv_buf), 0); if (res < 0) return res; if (res > 0 && reply[0] == '<') { continue; } printf("ret=%d.errno = %d,receive info:%s\n",ret,errno,recv_buf); break; } else { return -2; } } close(connect_fd); }