进程间通信比较
- 进程间通信的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享内存、Socket、Streams等。其中Socket和Streams支持不同主机上两个进程IPC
- 管道(包括无名管道和命名管道)、消息队列、信号量、共享内存
- 特点:依赖于Linux内核
- 局限:无法多机通信,也无法跨平台
- Socket(暂不讨论Streams)
- 特点:依赖于网络
- 优点:多机通信,支持跨平台通信
- 管道(包括无名管道和命名管道)、消息队列、信号量、共享内存
网络
- 地址
- 找到设备
- 端口号
- 区分网络服务->协议(ftp,http,socket…)
socket套接字
-
用的比较多的协议
- TCP(连接可靠)
- 面向连接(如A和B打电话,A能收得到B也能收得到)
- UDP(连接不可靠,但是数据量大如:传输视频)
- 面向报文(如A给B发短信,A不知道B是否能得收到)
- TCP(连接可靠)
-
socket服务器和客户端开发步骤
- 服务器
- 创建套接字
- 为套接字添加信息(IP地址和端口号)
- 监听网络连接
- 监听到有客户端接入,接受一个连接
- 数据交互
- 关闭套接字,断开连接
- 客户端
- 创建套接字
- 连接
- 写
- 读
- 关闭
- ‘
- 服务器
-
相关API
-
int socket(int domain,int type,int protocol);
创建套接字
-
domain:指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)
- AF_INET IPv4 因特网域
- AF_INET6 IPv6 因特网域
- AF_UNIX Unix 域
- AF_ROUTE 路由套接字
- AF_KEY 密钥套接字
- AF_UNSPEC 未指定
-
type:指定socke的类型
-
SOCK_STREAM:
- 流式套接字提供可靠的、面向连接的通信流;它使用TCP 协议,从而保证了数据传输的正确性和顺序性
-
SOCK DGRAM:
- 数据报套接字定义了一种无连接的服,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP
-
SOCK_RAW:
- 允许程序使用低层协议,原始套接字允许对底层协议如IP或 ICNP·进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。
-
-
protocol(通常值为0)
- 0选择tyoe类型对应的默认协议
- IPPROTO_TCP TCP传输协议
- IPPROTO_UDP UDP传输协议
- IPPROTO_SCTO SCTP传输协议
- IPPROTO_TIPC TIPC传输协议
-
返回值:网络文件描述符
-
-
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
用于绑定IP地址和端口号到socktfd)
参数 说明 int sockfd 网络文件描述符 const struct sockaddr *addr struct sockaddr和struct sockaddr_in两个结构体可选(一般都选用_in结构体,但是函数原型定义的是sockaddr,所以需要强制类型转换) socklen_t addrlen 地址长度 -
地址转换API
-
int inet_aton(const char*straddr,struct in_addr *addrp);
把字符串形式192.168.x.x转换为网络能识别的格式
- const char*straddr:ASCII表示的IP地址
- struct in_addr *addrp:转换出的IP地址
- 返回值:成功,返回值非零,如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以他的值会被忽略
-
char * inet_ntoa(struct in_addr inaddr);
把网络格式IP地址转换为字符串形式
- struct in_addr inaddr:需要转换的IP地址
- 返回值:返回一个字符指针。否则,返回NULL
-
-
监听函数
- int listen(int sockdf,int backlog);
- int listen(int sockdf,int backlog);
-
链接函数
- int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
- int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
-
数据收发
-
客户端连接
-
使用案例
多方消息收发
-
服务端
#include <stdio.h> #include <stdlib.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <unistd.h> /* int socket(int domain,int type,int protocol); int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen); int inet_aton(const char*straddr,struct in_addr *addr); char *inet_ntoa(struct in_addr inaddr); int listen(int sockdf,int backlog); int accept(int sockfd,struct sockaddr*addr,socklen_t*addrlen); int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen); */ int main(int argc,char **argv) { int fd = 0; int c_fd=0; int clen =0; int nread =0; char readBuf[128]; char Ack[128]={0}; struct sockaddr_in server_addr; struct sockaddr_in c_addr; memset(&server_addr,0,sizeof(struct sockaddr_in)); memset(&c_addr,0,sizeof(struct sockaddr_in)); clen = sizeof(struct sockaddr_in); if(argc != 3){ perror("argc is no good\n"); exit(-1); } //1.socket fd = socket(AF_INET,SOCK_STREAM,0); if(fd == -1){ perror("socket"); exit(-1); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(atoi(argv[2]));//5-9k随便选一个 inet_aton(argv[1],&server_addr.sin_addr);//本机IP地址 //2.bind bind(fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr_in)); //3.listen listen(fd,10); while(1){ c_fd=accept(fd,(struct sockaddr *)&c_addr,&clen);//连接之后的操作需要用c_fd来操作,父进程等待客户端连接,没有客户端连接时阻塞 if(c_fd == -1){ perror("accept"); } printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr)); //4.accept if(fork()==0){//子进程,当有客户端连接的时候会创建子进程,有多少个客户端就会创建多少个子进程 if(fork() == 0){ while(1){ memset(Ack,0,sizeof(Ack)); printf("input: "); fgets(Ack,128,stdin); //6.write write(c_fd,Ack,strlen(Ack)); } } while(1){ memset(readBuf,0,sizeof(readBuf)); nread = read(c_fd,readBuf,128); if (nread == -1){ perror("read"); } else{ printf("get message:%d,%s\n",nread,readBuf); } } } //5.read } return 0; }
-
客户端
#include <stdio.h> #include <stdlib.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <unistd.h> /* int socket(int domain,int type,int protocol); int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen); int inet_aton(const char*straddr,struct in_addr *addr); char *inet_ntoa(struct in_addr inaddr); int listen(int sockdf,int backlog); int accept(int sockfd,struct sockaddr*addr,socklen_t*addrlen); int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen); */ int main(int argc,char **argv) { int fd = 0; int nread =0; char readBuf[128]={0}; char Ack[128]={0}; struct sockaddr_in client_addr;; memset(&client_addr,0,sizeof(struct sockaddr_in)); if(argc != 3){ perror("argc is no good\n"); exit(-1); } //1.socket fd = socket(AF_INET,SOCK_STREAM,0); if(fd == -1){ perror("socket"); exit(-1); } client_addr.sin_family = AF_INET; client_addr.sin_port = htons(atoi(argv[2]));//5-9k随便选一个 inet_aton(argv[1],&client_addr.sin_addr);//本机IP地址 //2.connect if(connect(fd,(struct sockaddr*)&client_addr,sizeof(struct sockaddr_in))==-1){ perror("connect"); exit(-1); } while(1){ if(fork()==0){ while(1){ memset(Ack,0,sizeof(Ack)); printf("input: "); fgets(Ack,128,stdin);//也会阻塞 //gets(Ack); //3.write服务端先读,那客户端先写 write(fd,Ack,strlen(Ack));//希望有机会就写 } } while(1){ //4.read memset(readBuf,0,sizeof(readBuf)); nread = read(fd,readBuf,128);//读不到会阻塞 if (nread == -1){ perror("read"); } else{ printf("get from server message:%d,%s\n",nread,readBuf); } } } return 0; }
使用: ./server.out 192.168.1.103 8555 //先让服务器跑起来(我是在同一电脑测试) 端口号5-9k选一个 ./client.out 192.168.1.103 8555 //再运行客户端 程序还存在许多问题比如:把进程结束了端口号缺还是被占用了,这个时候就要换端口号了。服务器不能针对某个客户端进行回复,客户端也不能针对某一客户端发消息,数据都是公开的。
-
-
字节序
-
字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
-
小端字节序(x86平台采用)
- 将低序字节存储在起始地址
假设数据:0x01020304
内存(以字节为单位) 数据二进制表示 数据十进制表示 0x…4003 0000 0001 01 0x…4002 0000 0010 02 0x…4001 0000 0011 03 0x…4000 0000 0100 04 -
大端字节序(网络字节序采用)
- 将高序字节存储在起始地址
假设数据:0x01020304
内存(以字节为单位) 数据二进制表示 数据十进制表示 0x…4003 0000 0100 04 0x…4002 0000 0011 03 0x…4001 0000 0010 02 0x…4000 0000 0001 01
-
补充
-
搜索头文件
-
-
cd /usr/include到这个目录底下
-
grep “struct sockaddr_in {” * -nir (搜索struct sockaddr_in这个结构体所在头文件)
- *表示在当前目录
- n是显示行号
- i是不区分大小写
- r是递归的意思
-
vi linux/in +259可以直接跳到第259行
-
-