通过pipe,fifo等可以直接同一台机器上进行进程间通信,而通过网络相连的机器之间进行进程间通信要用到套接字接口,实际上套接字可以用在同一台机器上,也可以用在不同的机器上;也就是既可以内部通信,又可以相互之间通信
套接字是通信端点的抽象,和文件描述符一样,访问套接字也需要套接字描述符,处理文件描述符的read,write都可以处理套接字描述符
1:调用socket函数就会创建一个socket描述符,它唯一标识了一个socket,跟文件描述符一样,后面的操作会用到它,用它作参数来进行一些读写
#include <sys/socket.h> int socket(int domain, int type, int protocol);
函数如果创建成功,则返回套接字描述符;如果失败则返回-1
参数domain表示通信的特性,比如地址格式;在下面的表格里,每个通信域都有独自的地址格式,表示不同域以AF_开头,地址族
域 描述 AF_INET IPV4因特网域 AF_INET6 IPV6因特网域 AF_UNIX UNIX域 AF_UNSPEC 未指定
参数type表示套接字的类型,进一步确定通信特性,下面表格定义了套接字类型
类型 描述 SOCK_DGRAM 长度固定,无连接的不可靠报文传递 SOCK_RAW IP协议的数据包接口 SOCK_SEQPACKET 长度固定,有序,可靠的面向连接报文传递 SOCK_STREAM 有序,可靠,双向的面向连接字节流
参数protocol通常是0,表示给定的域和套接字类型选择默认协议;如果对同一域和套接字类型支持多个协议时,可以使用protocol参数选择一个特定协议;在AF_INET域中套接字类型SOCK_STREAM默认协议是TCP,在AF_INET域中套接字类型SOCK_DGRAM的默认协议是UDP
2:将地址绑定到一个套接字,对于服务器给一个接收客户端请求的套接字绑定一个地址
#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
函数绑定成功返回0,如果失败了就返回-1
参数sockfd也就是socket描述符,通过socket()创建的
参数addr是一个const struct sockaddr *的指针,指向要绑定给sockfd的协议地址,这里的地址必须和创建套接字的时候的地址族格式匹配,IPV4,IPV6和UNIX结构体都不同
参数len表示地址长度
通常服务器在启动的时候,绑定一个大家都知道的地址(ip addr + port)用于提供服务,客户端就可以通过它来连接服务器;但是客户端不需要指定,有哪位已经有系统分配的地址族;所以通常服务器在listen之前调用bind(),而客户端步调用,而在connect()时由系统随机生成一个
网络字节序
由于TCP/IP首部中所有二进制整数在网络中传输的次序为0~7bit,8~15bit,16~23bit,24~31bit顺序传输,因此也称为网络字节序,显然这里只有大端这一种字节序;但是主机字节序却有大端和小端两种字节序,所以在将一个地址绑定到socket的时候,先将主机字节序转换成网络字节序
3:服务器端在调用socket()和bind()之后就会调用listen()来监听这个socket;如果客户端调用connect()发出连接请求,服务器就受到了
#include <sys/socket.h> int listen(int sockfd, int backlog); int connect(int sockfd, const struct sockaddr *addr, socklen_t len);
参数backlog提供了一个提示,用于表示该进程索要入队的连接请求数量
对于connect函数,假如sockfd没有绑定一个地址,connect会给调用者绑定一个默认地址
4:TCP服务端调用了socket(),bind(),listen()后就在监听指定的socket地址了,TCP客户端调用了socket(),connect()后向TCP服务器发送一个连接请求,服务器监听了这个请求之后,会调用accept()函数来接收请求,这样请求就建立好了,之后就可以进行文件读写等操作了
#include <sys/socket.h> int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);
函数如果成功返回socket描述符,假如失败返回-1
成功返回的socket是已连接的socket描述符,而不是第一个参数传进去的服务器最开始创建的;一个服务器只会创建一个监听socket描述符,内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述符,当服务器完成了对客户端的服务,对应已经连接的socket描述符也关闭
下面是一个简单的建立客户端和服务器之间建立连接的例子:
client:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 9999 #define IPADDR "127.0.0.1" int main(int argc, char **argv){ int client_sockfd; struct sockaddr_in server_address; if ((client_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ printf("create socket failed\n"); exit(1); } server_address.sin_family = AF_INET; server_address.sin_port = htons(PORT); if (inet_pton(AF_INET, IPADDR, &server_address.sin_addr) == -1){ printf("inet_pton failed!\n"); exit(1); } if (connect(client_sockfd, (struct sockaddr *)&server_address, sizeof(server_address)) == -1){ printf("connect failed!\n"); exit(1); } close(client_sockfd); return 0; }
server:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 9999 int main(){ int server_sockfd, connect_sockfd; struct sockaddr_in server_address; if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ printf("Create socket failed!\n"); exit(1); } memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); server_address.sin_port = htons(PORT); if (bind(server_sockfd, (struct sockaddr *)&server_address, sizeof(server_address)) == -1){ printf("bind failed!\n"); exit(1); } if (listen(server_sockfd, 10) == -1){ printf("listen socket failed!\n"); exit(1); } if ((connect_sockfd = accept(server_sockfd, (struct sockaddr *)NULL, NULL)) == -1){ printf("connect failed!\n"); exit(1); } printf("Connect sucessfully!\n"); close(connect_sockfd); close(server_sockfd); return 0; }
编译,运行:
服务端: lihui@2015 /cygdrive/d/work $ cc -o client client.c lihui@2015 /cygdrive/d/work $ cc -o server server.c lihui@2015 /cygdrive/d/work $ ./server.exe 光标 客户端: lihui@2015 /cygdrive/d/work $ ./client.exe 服务端: lihui@2015 /cygdrive/d/work $ ./server.exe Connect sucessfully!
这样,一个简单的连接就建立起来了