unix域

服务器和客户端之间要实现通讯,先规定好一个协议。

要重视协议这个东西,日后开发肯定会用到,有时间好好学一下;

 

1、UNIX域?

TCP/IP协议,可以用于不同计算机之间的通讯,使用对应的IP和端口来找到对方,其实是一种进程间通讯,那能否在同一个电脑上面

不同进程间进行通讯?答案肯定是可以的,UNIX域是一个结合了套接字和管道通讯的一种进程间通讯的方式。它也有UDP/TCP的通讯的

方式,这两种方式都是可靠的,都不会造成数据的丢失,或者数据发送顺序的混乱。

 

在电脑本地实现进程间通讯的一种套接字。

 

 

2、无名UNIX域

无名:有亲缘关系的进程间通讯。

有名:文件系统中,有一个文件或者节点,两个不同进程通过打开这个文件或者节点实现进程间通讯。

 

 

创建一个无名UNIX域:

 

#include <sys/types.h>          /* See NOTES */

        #include <sys/socket.h>

 

        int socketpair(int domain, int type ,int protocol,  int sv[2]);

第一个参数:表示所需要使用的协议族,AF_UNIX, AF_LOCAL    建立UNIX的协议

第二个参数:表示创建套接字的类型:SOCK_STREAM (流式的套接字)   SOCK_DGRAM(数据报的套接字),这两种通讯方式

因为是使用在本机上的父子进程间通讯,所以都是可靠的。

第三个参数:表示类型,这里都是用0。

第四个参数:用来保存通讯所用到的socket的文件描述符。这种通讯是双工,在使用之前先将一端关闭,使用另外一端来通讯。     

 

返回值:成功返回0

失败返回-1

示例代码:

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <unistd.h>

 

int main()

{

//声明这两个fd用于父子进程间通讯

int fds[2];

char wbuf[12]="0123456789";

char rbuf[12]={0};

int ret;

int pid;

 

//创建本地域的套接字

ret = socketpair(AF_UNIX, SOCK_STREAM,0,fds);

if(ret < 0)

{

perror("socketpair fail");

return -1;

}

//创建子进程

pid = fork();

printf("pid=%d,getpid=%d,getppid=%d\n",pid,getpid(),getppid());//看下面的打印现象;

if(pid < 0)

{

perror("fork fail");

return -1;

}

else if(pid == 0)

{

//在子进程里面先写一个字符串给父进程

close(fds[0]);

write(fds[1],wbuf,12);

//再从父进程读一个字符串

read(fds[1],rbuf,12);

printf("child read from parent :%s\n",rbuf);

}else

{

//先关闭其中一个端口

close(fds[1]);

//先读子进程的数据

read(fds[0],rbuf,12);

printf("parent read from child :%s\n",rbuf);

memset(wbuf,0,12);

strncpy(wbuf,"xiejianming",strlen("xiejianming"));

//再把另外一个信息写给子进程

write(fds[0],wbuf,strlen("xiejianming"));

 

waitpid(pid,NULL,0);

}

return 0;

 

}

 

pid=3781,getpid=3780,getppid=2452

pid=0,getpid=3781,getppid=3780//对照理解子进程,父进程;

 

 

 

3、有名UNIX域

1)流式的套接字 (TCP)

(1)、客户端

A、创建套接字 socket

int clientfd = socket( AF_UNIX, SOCK_STREAM, 0 );

B、初始化地址结构体

通用地址结构体

            struct sockaddr {

                sa_family_t sa_family; //指定所用的协议族。

                char        sa_data[14]; //这里是具体的地址填充的空间。

            }

对于UNIX域来说,它也有自己的地址结构体,这个地址结构体在<linux/un.h>

#define UNIX_PATH_MAX   108

 

struct sockaddr_un {

    __kernel_sa_family_t sun_family; /* AF_UNIX */

    char sun_path[UNIX_PATH_MAX];   /* pathname */ 有名UNIX域,这个节点在文件系统哪里 可以是随意的一个文件,和有名管道差不多,这个结点不能建在share文件下面,windows系统下面没有这种节点机制;

(不能在共享目录下创建这个节点!!!!!)

};

 

 

C、使用连接函数连接服务器  connect

#include <sys/types.h>          /* See NOTES */

        #include <sys/socket.h>

 

        int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

第一个参数:就是第一步新建的socket返回的fd。

第二个参数:指定你要链接的服务器的地址,就是第二步创建的地址。

第三个参数:这个地址的长度。

 

返回值:

如果成功返回0;

如果失败返回-1;

 

D、连接服务器成功之后,就可以使用读写函数去通讯

 

对数据收发:read/recv  write/send.

 

E、关闭

关闭:close

 

 

(2)、服务器

A、创建套接字 socket

int clientfd = socket( AF_UNIX, SOCK_STREAM, 0 );

B、初始化地址结构体

通用地址结构体

            struct sockaddr {

                sa_family_t sa_family; //指定所用的协议族。

                char        sa_data[14]; //这里是具体的地址填充的空间。

            }

对于UNIX域来说,它也有自己的地址结构体,这个地址结构体在<linux/un.h>

#define UNIX_PATH_MAX   108

 

struct sockaddr_un {

    __kernel_sa_family_t sun_family; /* AF_UNIX */

    char sun_path[UNIX_PATH_MAX];   /* pathname */ 有名UNIX域,这个节点在文件系统哪里

(不能在共享目录下创建这个节点!!!!!)

};

C、绑定 bind

        #include <sys/types.h>          /* See NOTES */

        #include <sys/socket.h>

 

        int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

第一个参数:就是前面返回的文件描述符。

第二个参数:指定我们通讯的地址,这个是一个通用的结构体,它的地址,由指定的协议族去决定的。

第三个参数:指定的是第二个参数地址的大小。

返回值:

如果成功返回0;

如果失败返回-1;

 

 

D、监听:listen

 

        #include <sys/types.h>          /* See NOTES */

                #include <sys/socket.h>

 

        int listen(int sockfd, int backlog);

第一个参数:前面新建的fd。

第二个参数:监听队列上最大的请求数。

返回值:

如果成功返回0;

如果失败返回-1;

 

 

E、接受链接:accept。

#include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

 

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

第一个参数:前面新建的fd。

第二个参数:用来保存客户端的地址信息。

第三个参数:用来保存客户端地址的长度。

 

返回值:成功的话返回一个新的fd,然后服务器就可以通过这个新的fd和客户端进行通讯。

如果失败返回 -1;

 

 

 

F、对数据收发:read、recv  ,发送:write、send。

 

ssize_t read(int fd, void *buf, size_t count);

ssize_t write(int fd, const void *buf, size_t count);

第一个参数:是前面accept函数返回的新的fd。代表我们要和对应的客户端进行通讯。

第二个参数:是我们要发送或者接收的buffer对应的地址。

第三个参数:对应是我们要读取或者写入的数据按字节计算的长度。

 

返回值:成功的话,返回值的大小代表实际读取或者写入的字节数。这个返回值,和count不一定相等。

如果出错的话,返回-1;

 

        #include <sys/types.h>

        #include <sys/socket.h>

 

        ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

第一个参数:是前面accept函数返回的新的fd。代表我们要和对应的客户端进行通讯。

第二个参数:是我们要发送或者接收的buffer对应的地址。

第三个参数:对应是我们要读取或者写入的数据按字节计算的长度。

第四个参数:一般都是0;//send,recv函数比write,read函数就是多了一个参数0;

返回值:成功的话,返回值的大小代表实际读取或者写入的字节数。这个返回值,和count不一定相等。

如果出错的话,返回-1;

 

G、关闭:close

TCP服务器端:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <linux/un.h>

#include <sys/types.h>

#include <sys/socket.h>

 

 

int main()

{

// 第一步,创建这个UNIX域套接字

int serverfd = socket( AF_UNIX, SOCK_STREAM, 0 );

 

//第二步,声明地址结构体,并且初始化

struct sockaddr_un serveraddr,clientaddr;

int ret;

memset(&serveraddr,0,sizeof(serveraddr));

memset(&clientaddr,0,sizeof(clientaddr));

serveraddr.sun_family = AF_UNIX;

//注意这个路径一定要存在的,并且不能放在共享目录下面

strcpy(serveraddr.sun_path,"/tmp/unix");//可以是随意路径的文件,程序运行后能查到这些节点,系统创建的

 

unlink("/tmp/unix");//删除

 

//第三步,绑定服务器的地址

ret = bind(serverfd, (struct sockaddr *)&serveraddr,sizeof(serveraddr) );

if(ret < 0)

{

perror("server bind fail ");

close(serverfd);

return -1;

}

//第四步,监听服务器的fd,看是否有连接进来

ret = listen(serverfd,5);

if(ret < 0)

{

perror("listen bind fail ");

close(serverfd);

return -1;

}

int size = sizeof(clientaddr);

//第五步,如果有连接进来,就接收它

int clientfd = accept(serverfd,(struct sockaddr *)&clientaddr, &size);//第二个参数存储客户端的地址信息

 

char buf[128];

while(1)

{

//第六步,就可以互相通讯了。

bzero(buf,128);

read(clientfd,buf,128);

printf("Read from client:%s\n",buf);

printf("Please input a string:\n");

bzero(buf,128);

fgets(buf,128,stdin);

write(clientfd,buf,128);

}

//第七步,通讯结束后,关闭对应的文件描述符。

close(clientfd);

close(serverfd);

 

}

Tcp客户端:

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <linux/un.h>

#include <sys/types.h>

#include <sys/socket.h>

 

 

int main()

{

// 第一步,创建这个UNIX域套接字

int clientfd = socket( AF_UNIX, SOCK_STREAM, 0 );

 

//第二步,声明地址结构体,并且初始化

struct sockaddr_un serveraddr;

int ret;

memset(&serveraddr,0,sizeof(serveraddr));

serveraddr.sun_family = AF_UNIX;

strcpy(serveraddr.sun_path,"/tmp/unix");

 

//第三步,连接服务器

ret = connect(clientfd,( struct sockaddr * )&serveraddr, sizeof(serveraddr));//第二个参数里面有地址信息;

if(ret < 0)

{

perror("client connect server fail");

close(clientfd);

return -1;

}

char buf[128];

while(1)

{

//第四步,进行读写操作//tcp通过客户端套接字

bzero(buf,128);

printf("Please input a string:\n");

fgets(buf,128,stdin);

write(clientfd,buf,128);

bzero(buf,128);

read(clientfd,buf,128);

printf("Read from server:%s\n",buf);

}

//第五步,关闭文件描述符

close(clientfd);

 

}

 

 

 

 

 

 

2)UNIX域 UDP通讯方式

 

(A)、服务器

 

(1)、创建socket

int serverfd = socket( AF_UNIX, SOCK_DGRAM,0 );

 

(2)、配置好自己的地址结构体。

初始化地址结构体

通用地址结构体

            struct sockaddr {

                sa_family_t sa_family; //指定所用的协议族。

                char        sa_data[14]; //这里是具体的地址填充的空间。

            }

对于UNIX域来说,它也有自己的地址结构体,这个地址结构体在<linux/un.h>

#define UNIX_PATH_MAX   108

 

struct sockaddr_un {

    __kernel_sa_family_t sun_family; /* AF_UNIX */

    char sun_path[UNIX_PATH_MAX];   /* pathname */ 有名UNIX域,这个节点在文件系统哪里

(不能在共享目录下创建这个节点!!!!!)

};

 

(3)、绑定自己的地址结构体。 bind

 

        #include <sys/types.h>          /* See NOTES */

        #include <sys/socket.h>

 

        int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

第一个参数:就是前面返回的文件描述符。

第二个参数:指定我们通讯的地址,这个是一个通用的结构体,它的地址,由指定的协议族去决定的。

第三个参数:指定的是第二个参数地址的大小。

返回值:

如果成功返回0;

如果失败返回-1;

(4)、等待客户端发信息给服务器。(服务器是被动接收数据)

等待客户端发信息过来 recvfrom

 

#include <sys/types.h>

#include <sys/socket.h>

 

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

第一个参数:是前面socket函数返回的fd。代表我们要和对应的客户端进行通讯。

第二个参数:是我们要发送或者接收的buffer对应的地址。

第三个参数:对应是我们要读取或者写入的数据按字节计算的长度。

第四个参数:一般都是0;

第五个参数:用来保存客户端的地址信息。

第六个参数:就是前面这个地址的长度。

返回值:成功的话,返回值的大小代表实际读取的字节数。这个返回值,和count不一定相等。

如果出错的话,返回-1;

 

 

(5)、可以进行数据的收发。

sendto

#include <sys/types.h>

#include <sys/socket.h>

 

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

第一个参数:是前面socket函数返回的fd。代表我们要和对应的客户端进行通讯。

第二个参数:是我们要发送或者接收的buffer对应的地址。

第三个参数:对应是我们要读取或者写入的数据按字节计算的长度。

第四个参数:一般都是0;

第五个参数:这个是我们要将数据发送到哪里?这个就是接收方的地址。这个参数一定要指定。

第六个参数:就是前面这个地址的长度。

返回值:成功的话,返回值的大小代表实际发送的字节数。这个返回值,和count不一定相等。

如果出错的话,返回-1;

 

 

(6)、通话结束,关闭连接。 close(fd);

 

 

 

UDP服务器端:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <linux/un.h>

#include <sys/types.h>

#include <sys/socket.h>

 

int main()

{

//第一步,创建一个本地域的fd

int serverfd = socket( AF_UNIX, SOCK_DGRAM, 0 );

if(serverfd < 0)

{

perror("create socket fail");

return -1;

}

 

//第二步,设置服务器自己的地址

struct sockaddr_un serveraddr,clientaddr;

memset(&serveraddr,0,sizeof(serveraddr));

memset(&clientaddr,0,sizeof(clientaddr));

serveraddr.sun_family = AF_UNIX;

strcpy(serveraddr.sun_path,"/tmp/unixserver");

 

unlink("/tmp/unixserver");

 

//第三步,绑定服务器自己的地址

int ret = bind(serverfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

if(ret < 0)

{

perror("server bind fail");

close(serverfd);

return -1;

}

char buf[128];

int size = sizeof(clientaddr);

while(1)

{

//第四步,等待客户端发送消息过来

bzero(buf,128);

ret = recvfrom(serverfd, buf, 128, 0 ,(struct sockaddr *)&clientaddr, &size );

printf("Recv from client ret=%d buf=%s  clientpath:%s\n",ret,buf,clientaddr.sun_path);//clientaddr结构体里面保存了客户端的地址;

//第五步,将收到消息再发回去给客户端

ret = sendto(serverfd,buf,128,0,(struct sockaddr *)&clientaddr, size);

printf("Send to client ret=%d buf=%s\n",ret,buf);

}

//第六步,通讯结束,关闭连接。

close(serverfd);

 

}

 

 

 

 

(B)、客户端:

(1)、创建socket

(2)、配置服务器的地址。

(3)、还需要配置并且绑定自己的地址!!!!! 如果没有绑定自己地址的话,服务器就发不了数据给客户端

(4)、往这个地址发数据。

(5)、接收服务器发过来的数据。

(6)、通讯结束,关闭连接

Udp客户端unix

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <linux/un.h>

#include <sys/types.h>

#include <sys/socket.h>

 

int main()

{

//第一步,创建一个UNIX域的socket

int ret;

int clientfd = socket(AF_UNIX, SOCK_DGRAM, 0 );

if(clientfd < 0)

{

perror("client socket create fail");

return -1;

}

//第二步,设置好服务器的地址,填好收件人的地址

struct sockaddr_un serveraddr,clientaddr;

memset(&serveraddr,0,sizeof(serveraddr));

memset(&clientaddr,0,sizeof(clientaddr));

serveraddr.sun_family = AF_UNIX;

strcpy(serveraddr.sun_path,"/tmp/unixserver");

//还要填好发件人的地址,要不然服务器不知道客户端的地址是多少

clientaddr.sun_family = AF_UNIX;

strcpy(clientaddr.sun_path,"/tmp/unixclient");//注意地址是不一样的

unlink("/tmp/unixclient");

//第三步,要绑定自己的地址

ret = bind(clientfd,(struct sockaddr *)&clientaddr,sizeof(clientaddr));

if(ret < 0)

{

perror("client bind error");

}

 

char buf[128];

 

int size = sizeof(serveraddr);

while(1)

{

//第四步,设置好地址之后,就可以通讯了

bzero(buf,128);

fgets(buf,128,stdin);

ret = sendto(clientfd,buf,128,0,(struct sockaddr *)&serveraddr,size);

bzero(buf,128);

recvfrom(clientfd,buf,128,0,(struct sockaddr *)&serveraddr,&size);

printf("Recv from server :%s\n",buf);

}

//第五步,通讯结束关闭

close(clientfd);

}

 

 

项目:实现一个FTP ( File Transmission Protocol )服务器。

 

 

1、启动服务器

2、启动客户端

3、客户端可以查看服务器指定的ftp目录内有什么文件。

4、客户端可以下载这个目录里面的文件到本地。

 

启动服务器

/home/gec/ftp/ --->  1.c   2.c   3.c  a  b  helloworld

 

$  ./ftp_client --------->先建立与服务器TCP的连接

|  FTP> ls                              --------->发送一个命令给服务器

|  1.c   2.c   3.c  a  b helloworld --------->服务器解析这个命令,ls就是要查看ftp目录内包含有什么文件,然后通过opendir,把结果保存到一个字符数组,然后再发回来给客户端

|  FTP> get  helloworld --------->这里又发送一个get命令给服务器,把文件名提取出来,然后再和ftp路径名合成一个绝对路径,有这个路径之后,我们就可以打开这个文件,然后读取,并且发送给客户端。

|  #################################

|  FTP> exit

 

$ ls

$ helloworld

$chmod +x helloworld

$./helloworld

$  hello world !

 

服务器和客户端之间要实现通讯,先规定好一个协议。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/weixin_41723504/article/details/81516930