Linux 进程间套接字通信(Socket)编程
转载链接:https://blog.csdn.net/violet_echo_0908/article/details/49670901
姓名:罗学元 学号:21181214375 学院:广州研究院
【嵌牛导读】Linux进程间套接字通信编程
【嵌牛鼻子】Linux 进程间套接字及通信编程
【嵌牛提问】Linux进程间套接字编程如何进行,包含哪些部分
一、流式socket的接口及作用
socket的接口函数声明在头文件
1.创建套接字——socket系统调用
该函数来创建一个套接字,并返回一个描述符,该描述符可以用来访问该套接字,其原型如下:
int socket(int domain, int type, int protocol)
函数中的三个参数分别对应前面所说的三个套接字属性。protocol参数设置为0表示使用默认协议。
2.命名(绑定)套接字——bind系统调用
该函数把通过socket调用创建的套接字命名,从而让它可以被其他进程使用。对于AF_UNIX,调用该函数后套接字就会关联到一个文件系统路径名,对于AF_INF,则会关联到一个IP端口号。函数原型如下:
int bind(int socket, const struct sockaddr *address, size_t a ddress_len)
成功时返回0,失败时返回-1;
3.创建套接字队列(监听)——listen系统调用
该函数用来创建一个队列来保存未处理的请求。成功时返回0,失败时返回-1。其原型如下:
int listen(int socket, int backlog)
backlog用于指定队列的长度,等待处理的进入连续的个数最多不能超过这个数字,否则往后的连接将被拒绝,导致客户的连接请求失败。调用后,程序一直会监听这个IP端口,如果有连接请求,就把它加入到这个队列中。
4.接受连接——accept系统调用
该系统调用用来等待客户建立对该套接字的连接。accept系统调用只有当客户程序试图连接到由socket参数指定的套接字上时才返回,也就是说,如果套接字队列中没有未处理的连接,accept将阻塞直到有客户建立连接为止。accept函数将创建一个新套接字来与该客户进行通信,并且返回新套接字的描述符,新套接字的类型和服务器监听套接字类型是一样的。它的原型如下:
int accept(int socket, struct sockaddr *address, size_t *address_len)
1
address为连接客户端的地址,参数address_len指定客户结构的长度,如果客户地址的长度超过这个值,它将会截断。
5.请求连接——connect系统调用
该系统调用用来让客户程序通过在一个未命名套接字和服务器监听
套接字之间建立连接的方法来连接到服务器。它的原型如下:
int connect(int socket, const struct sockaddr *address, size_t
address_len)
参数socket指定的套接字连接到参数address指定的服务器套接字。
成功时返回0,失败时返回-1。
6.关闭socket——close系统调用
该系统调用用来终止服务器和客户上的套接字连接,我们应该总是在连接的两段(服务器和客户)关闭套接字。
二、进程使用流式socket进行通信
1.服务器程序:
#include
#include
#include
#include
#include
#include
#include
int main()
{
int server_sockfd = -1;
int client_sockfd = -1;
int client_len = 0;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
//创建流套接字
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
//设置服务器接收的连接地址和监听的端口
server_addr.sin_family = AF_INET; //指定网络套接字
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //接受所有IP地址的连接
server_addr.sin_port = htons(9736); //绑定到9736端口
//绑定(命名)套接字
bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(server_sockfd, 5); //创建套接字队列,监听套接字
signal(SIGCHLD, SIG_IGN); //忽略子进程停止或退出信号
while(1){
char ch = '\0';
client_len = sizeof(client_addr);
printf("Server waiting\n");
//接受连接,创建新的套接字
client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_len);
if(fork()==0){
//子进程中,读取客户端发过来的信息,处理信息,再发送给客户端
read(client_sockfd, &ch, 1);
sleep(5);
ch++;
write(client_sockfd, &ch, 1);
close(client_sockfd);
}
else{
//父进程中,关闭套接字
close(client_sockfd);
}
}
}
这是一个服务器程序,它首先创建套接字,然后绑定一个端口再监听套接字,忽略子进程的停止消息等,然后它进入循环,一直循环检查是否有客户连接到服务器,如果有,则调用fork创建一个子进程来处理请求。利用read系统调用来读取客户端发来的信息,利用write系统调用来向客户端发送信息。这个服务器的工作非常简单,就是把客户发过来的字符+1,再发送回给客户。
2.客户端程序:
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd = -1;
int len = 0;
struct sockaddr_in address;
int result;
char ch = 'A';
//创建流套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0); //设置要连接的服务器的信息
address.sin_family = AF_INET; //使用网络套接字
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //服务器地址
address.sin_port = htons(9736); //服务器所监听的端口
len = sizeof(address);
result = connect(sockfd, (struct sockaddr*)&address, len); //连接到服务器
if(result == -1)
{
perror("ops:client\n");
exit(1);
}
write(sockfd, &ch, 1); //发送请求给服务器
read(sockfd, &ch, 1); //从服务器获取数据
printf("char from server = %c\n", ch);
close(sockfd);
exit(0);
}
这是一个客户程序,它同样要先创建套接,然后连接到指定IP端口服务器,如果连接成功,就用write来发送信息给服务器,再用read获取服务器处理后的信息,再输出。
三.函数解释
1.socket
功能:创建一个套接口()。
原型:
#include
SOCKET PASCAL FAR socket( int af, int type, int protocol);
af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。
type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
SOCK_STREAM提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP。
SOCK_DGRAM支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,为Internet地址族使用UDP。
SOCK_STREAM类型的套接口为全双向的字节流。对于流类套接口,在接收或发送数据前必需处于已连接状态。用connect()调用建立与另一套接口的连接,连接成功后,即可用send()和recv()传送数据。当会话结束后,调用closesocket()。带外数据根据规定用send()和recv()来接收。
实现SOCK_STREAM类型套接口的通讯协议保证数据不会丢失也不会重复。如果终端协议有缓冲区空间,且数据不能在一定时间成功发送,则认为连接中断,其后续的调用也将以WSAETIMEOUT错误返回。
SOCK_DGRAM类型套接口允许使用sendto()和recvfrom()从任意端口发送或接收数据报。如果这样一个套接口用connect()与一个指定端口连接,则可用send()和recv()与该端口进行数据报的发送与接收。
2.htonl(), ntohl(), htons(), ntohs() 函数
在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题。这是就可能用到htons(), ntohl(), ntohs(),htons()这4个函数。
网络字节顺序与本地字节顺序之间的转换函数:
htonl()–“Host to Network Long”
ntohl()–“Network to Host Long”
ntohs()–“Network to Host Short”
之所以需要这些函数是因为计算机数据表示存在两种字节顺序:NBO与HBO
网络字节顺序NBO(Network Byte Order): 按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。主机字节顺序(HBO,Host Byte Order): 不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。
如Intel x86结构下, short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 12
如IBM power PC结构下, short型数0x1234表示为12 34, int型数0x12345678表示为12 34 56 78
由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字节顺序,其实就是如同power pc那样的顺序. 在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换.
3.int socket(int domain, int type,int protocol)
domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等). AF_UNIX只能够用于单一的Unix系统进程间通信,而AF_INET是针对Internet的,因而可以允许在远程主机之间通信
type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等) SOCK_STREAM表明我们用的是TCP协议,这样会提供按顺序的,可靠,双向,面向连接的比特流. SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信.
socket为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看errno可知道出错的详细情况
4.int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
sockfd:是由socket调用返回的文件描述符.
addrlen:是sockaddr结构的长度.
my_addr:是一个指向sockaddr的指针.
有sockaddr的定义
struct sockaddr
{
unisgned short as_family;
char sa_data[14];
};
不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sockaddr_in) 来代替.在其中有sockaddr_in的定义
struct sockaddr_in{
unsigned short sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
}
我们主要使用Internet所以sin_family一般为AF_INET,sin_addr设置为INADDR_ANY表示可以和任何的主机通信,sin_port是我们要监听的端口号.sin_zero[8]是用来填充的. bind将本地的端口同socket返回的文件描述符捆绑在一起.成功是返回0,失败的情况和socket一样
5.int listen(int sockfd,int backlog)
sockfd:是bind后的文件描述符.
backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时, 使用这个表示可以介绍的排队长度.
listen函数将bind的文件描述符变为监听套接字.返回的情况和bind一样.
6.int accept(int sockfd, struct sockaddr *addr,int *addrlen)
sockfd:是listen后的文件描述符.
addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了.
bind,listen和accept是服务器端用的函数,accept调用时,服务器端的程序会一直阻塞到有一个客户程序发出了连接. accept成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描述符写信息了. 失败时返回-1
7.int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)
sockfd:socket返回的文件描述符.
serv_addr:储存了服务器端的连接信息.其中sin_add是服务端的地址
addrlen:serv_addr的长度
connect函数是客户端用来同服务端连接的.成功时返回0,sockfd是同服务端通讯的文件描述符失败时返回-1
总的来说网络程序是由两个部分组成的–客户端和服务器端.它们的建立步骤一般是:
服务器端
socket–>bind–>listen–>accept
客户端
socket–>connect