socket编程

socket编程(二):

进程间通讯

1、struct sockaddr_un

在 UNIX domain 中, socket 地址以路径名来表示, domain 特定的 socket 地址结构的定义如下所示:

struct sockaddr_un {
   sa_family_t sun_family;    /*Always AF_UNIX*/
   char     sun_path[108];    /*Null-terminated socket pathname */
}

为将一个 UNIX domain socket 绑定到一个地址上,需要初始化一个sockaddr_un 结构,然后将指向这个结构的一个(转换)指针作为 addr 参数传入 bind()并将 addrlen 指定为这个结构的大小,如程序:

int fd = 0;
const char *path = "/mp/mysock"
struct sockaddr_un serv_addr;

fd = socket(AF_UNIX, SOCK_STREAM, 0);

memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strncpy(serv_addr.sun_path, path, sizeof(serv_addr.sun_path) - 1);

if (bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    close(fd);

使用 memset()调用来确保结构中所有字段的值都为 0。

当用来绑定 UNIX domain socket 时, bind()会在文件系统中创建一个条目(因此作为 socket路径名的一部分的目录需要可访问和可写。)文件的所有权将根据常规的文件创建规则来确定。这个文件会被标记为一个 socket。当使用ls –l列出时, UNIX domain socket 在第一列将会显示类型 s,而 ls –F 则会在 socket 路径名后面附加上一个等号(=)。
注意:

  • 无法将一个 socket 绑定到一个既有路径名上(bind()会失败并返回 EADDRINUSE 错误)。
  • 通常会将一个 socket 绑定到一个绝对路径名上,这样这个 socket 就会位于文件系统中的一个固定地址处。
  • 一个 socket 只能绑定到一个路径名上,相应地,一个路径名只能被一个 socket 绑定。
  • 无法使用 open()打开一个 socket。
  • 当不再需要一个 socket 时可以使用 unlink()(或 remove())删除其路径名条目(通常也应该这样做)。

2、流 socket

下面讲解一个简单的使用了进程间通讯中的流socket 的客户端-服务器应用程序。

服务端代码:

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/un.h>

#define BUF_SIZE 8192

int main(int argc, char *argv[])
{
    int fd = 0;
    int newfd = -1;
    struct sockaddr_un serv_addr;
    const char *path = "/mp/mysock";
	
	//01、创建一个用于接待的套接字
    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if(fd < 0){
        perror("socket");
        exit(1);
    }
	//02、填充结构体信息并进行绑定
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    strncpy(serv_addr.sun_path, path, sizeof(serv_addr.sun_path) - 1);
    if(bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
        perror("bind");
        exit(1);
    }
	
	//03、调用listen()把主动套接字变成被动套接字
	//并设置最大能改接受BACKLOG个客户端
    if(listen(fd, 6)<0){
        perror("listen");
        exit(1);
    }

	//04、阻塞等待客户端发起连接请求
	while(1){
	    //通过fd的监听,accept接受客户端连接,
		newfd = accept(fd, NULL, 0);
		if(newfd<0){
			perror("accept");
			exit(1);
		}

		//05读写(收发)消息
		//这里的读写是从newfd
		int ret = -1;
		char buf[BUFSIZ];
		while(1){
			//因为要循环读且内容存放在buf数组里有必要清零操作
			bzero(buf, BUF_SIZE);
			ret = read(newfd, buf, BUF_SIZE-1);
			if(ret<0){
				perror("read");
				break;
			}
			if(ret == 0){//如果收到的是0说明对方已经关闭了
				break;
			}
			printf("收到:%s\n", buf);
		}
		//如果newfd使用完了,必须关闭newfd,否责内存泄漏
		close(newfd);
	}
	close(fd);
	
	return 0;
}

客户端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/un.h>

#define BUF_SIZE 8192

int main(int argc, char *argv[])
{
	int fd = -1;
	struct sockaddr_un  ds_addr;
	const char *path = "/mp/mysock";
	
	//01创建用于通讯的流式套接字,SOCK_STREAM
	fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if(fd < 0){
		perror("socket");
		return -1;
	}	
	//02填充结构体信息
	memset(&ds_addr, 0, sizeof(ds_addr));
	ds_addr.sun_family = AF_UNIX;
	strncpy(ds_addr.sun_path, path, sizeof(ds_addr.sun_path) - 1);
	
	//03使用connect发起连接请求
	if( connect(fd, (struct sockaddr*)&ds_addr, sizeof(ds_addr)) <0){
		perror("connect");
		exit(1);
	}
	char buf[BUF_SIZE];
	//自己定义一个存放数据的buf作为缓冲区
	int ret = -1;
	//定义变量存放发送函数的返回值
	while(1){
		//从键盘输入内容存放到buf里
		//每次循环都使用他有必要给他清零
		bzero(buf, BUF_SIZE);
		if(fgets(buf, BUF_SIZE-1, stdin)==NULL){
			//从键盘获取输入存放到buf数组中
			perror("fgets");
			continue;
			//如果获取出错重新获取
		}
		
		//发送数据
		ret = write(fd, buf, strlen(buf));
		if(ret<0){
			perror("send");
			exit(1);
		}
		if(!ret){//说明对方关闭了
			break;
		}
	}
	//通讯完成可以关闭fd回收资源
	close(fd);	
	
	return 0;
}

运行结果:
客户端发送:

服务端接收:
在这里插入图片描述
注意:
在这里插入图片描述
在服务器终止之后, socket 路径名会继续存在。所以服务器应该在调用 bind()之前使用remove()删除 socket 路径名的所有既有实例。如果没有这样做,那么bind()调用在下一次调用服务器时创建了这个 socket 路径名时就会失败。

3、数据报 socket

对于 进程间通讯来讲,数据报的传输是在内核中发生的,并且也是可靠的。所有消息都会按序被递送并且也不会发生重复的状况。

下面讲解一个简单的使用了进程间通讯中的数据报socket 的客户端-服务器应用程序。

服务端代码:

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/un.h>

#define BUF_SIZE 10

int main(int argc, char *argv[])
{
	int fd =-1;
	int len;
	int numbytes;
	const char *path = "/mp/mysock";
	struct sockaddr_un  serv_addr,client_addr;
	
	//01创建套接字
	fd=socket(AF_UNIX, SOCK_DGRAM, 0);
	if(fd < 0){
		perror("socket");
		return -1;
	}

	//02绑定服务器自身的信息
	remove(path);
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    strncpy(serv_addr.sun_path, path, sizeof(serv_addr.sun_path) - 1);
    if(bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
        perror("bind");
        exit(1);
    }
	char buf[BUF_SIZE];
   //03接收信息	
	while(1){
		len = sizeof(serv_addr);
		bzero(buf,BUF_SIZE);
		numbytes = recvfrom(fd, buf, BUF_SIZE-1, 0, (struct sockaddr*)&client_addr, &len);
		if(numbytes < 0){
			perror("recvfrom");
			continue;
		}
	    printf("Receive: %s \n", buf);
	}
	close(fd);

	return 0;
}

客户端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/un.h>

#define BUF_SIZE 15

int main(int argc, char *argv[])
{
	int fd =-1;
	struct sockaddr_un  ds_addr;
	const char *path = "/mp/mysock";
	
	//01创建用于通讯的报式套接字
	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
	if(fd < 0){
		perror("socket");
		return -1;
	}	
	//02填充结构体信息
	memset(&ds_addr, 0, sizeof(ds_addr));
	ds_addr.sun_family = AF_UNIX;
	strncpy(ds_addr.sun_path, path, sizeof(ds_addr.sun_path) - 1);
	
	//03使用connect发起连接请求
	if( connect(fd,(struct sockaddr*)&ds_addr,sizeof(ds_addr)) <0){
		perror("connect");
		exit(1);
	}
	char buf[BUF_SIZE];
	//自己定义一个存放数据的buf作为缓冲区
	int ret = -1;
	//定义变量存放发送函数的返回值
	while(1){
		//从键盘输入内容存放到buf里
		//每次循环都使用他有必要给他清零
		bzero(buf,BUF_SIZE);
		if(fgets(buf,BUF_SIZE-1,stdin)==NULL){
			//从键盘获取输入存放到buf数组中
			perror("fgets");
			continue;
			//如果获取出错重新获取
		}
		
		//发送数据
		ret = sendto(fd, buf, strlen(buf), 0, (struct sockaddr*)&ds_addr, sizeof(ds_addr));
		if(ret<0){
			perror("sendto");
			exit(1);
		}
		if(!ret){//说明对方关闭了sizeof(ds_addr)
			break;
		}
	}
	//通讯完成可以关闭fd回收资源
	close(fd);	
	
	return 0;
}

运行结果:
客户端发送:
在这里插入图片描述
服务端接收:
在这里插入图片描述
注意:
服务端接收结果显示当接收端有意在 recvfrom()调用中指定了一个比消息更小的 length 值时消息会被静默地截断。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值