看了两天socket,东西比较多,简单总结下。
进程间通信方法有管道、信号量、共享内存、消息队列等,这些机制都依靠一台计算机系统的共享资源实现。资源可以是文件系统空间、共享的物理内存或者消息队列,只有运行在同一台机器上的进程才可以使用它们。socket也是进程间通信的一种方法,但是除上述通信以外,还包括了计算机网络通信。同一台机器上的进程之间可以使用套接字通信,不同机器之间也可以通过套接字实现进程通信,这样就支持分布在网络的客户/服务器系统。
1.什么是套接字
套接字是一种通信机制,客户/服务器系统开发工作既可以在本地单机上进行,也可以跨网络进行。linux所提供的打印服务、连接数据库、远程登录和用于文件传输的ftp通常都是通过socket来进行通信。
套接字与管道的区别是套接字明确的将客户与服务器区分开来,可以将多个客户连接到一个服务器上。
套接字主要应用于网络通信,网络的层次也是我们需要了解的。
网络层次从底层到上层分为媒介---->数据链路层---->网络层---->传输层---->应用层。
数据链路层提供硬件接口,网络层包括IP协议,传输层包括TCP/UDP协议,应用层即用户进程应用程序。而socket位于应用层与传输层之间,可以看做一个抽象的层次,一组接口。应用程序通过socket将数据送到传输层,而不用管具体的TCP/IP协议。
2.套接字的连接
套接字连接位于客户端和服务器端。服务器端服务器进程要用socket()系统调用创建一个套接字(服务器套接字),该套接字不可与其他进程共享。然后服务器需要初始化套接字,与端口绑定(bind)、对端口进行监听(listen)、调用accept(进行阻塞)、等待客户连接。这时如果客户端有初始化一个socket,建立与服务器的连接(connect),若连接成功,客户端与服务器端就连接成功。客户端发送数据请求,服务器端接收请求并处理,再把回应数据发送给客户端,客户端读取数据,最后关闭连接,结束交互。客户端需要服务器的地址和要发送的数据,服务器端需要启动socket和监听等。
3.套接字属性
套接字属性包括域、类型和协议。套接字用地址作为他的名字,地址随着域的不同而不同。
套接字常用的域为AF_INET,指的是internet网络,其底层协议IP协议只有一个族地址,使用IP地址来指定网络中的计算机,其域名最终转换为IP地址。当客户使用套接字进行跨网络连接时,就需要使用到服务器计算机的IP地址。服务器计算机可能同时有多个服务正在运行,客户可以通过IP端口来指定一台联网机器上的某个特定服务。对于系统外,需要通过IP地址和端口号的组合来确定IP端口。套接字在开始通信前需要绑定一个端口号。另一种域为AF_UNIX域,UNIX文件系统域,未联网的计算机的套接字可以使用这个域,底层协议是文件输入输出,地址就是文件名。
套接字类型有流(stream)和数据包(datagram)。
套接字协议默认情况下为0。
4.服务器进程
服务器程序接受来自客户程序的连接,在调用socket创建套接字后要命名该套接字,对于AF_INET套接字,名字为与客户连接的特定网络有关的服务标识符,linux将进入针对特定端口号的连接转到正确的服务器进程。系统调用bind来命名,创建监听队列,然后服务器等待客户连接到这个名字的套接字。
1)变量的定义
- int server_sockfd, client_sockfd;//定义客户端socket描述符和服务器socket描述符
- int server_len, client_len;
- struct sockaddr_in server_address;//服务器套接字地址
- struct sockaddr_in client_address;//客户端套接字地址
2)为服务器创建一个未命名的socket socket()
- server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//域AF_INET 类型SOCK_STREAM 协议默认
3)命名socket
bind() socket的命名要通过socket的地址。每个套接字的域都有自己的地址格式。对于AF_INET域,地址由结构sockaddr_in来指定。一个AF_INET套接字由他的域、IP地址和端口号来完全确定。所有套接字的行为类似文件描述符,通过一个唯一的整数来区分。
- struct sockaddr_in
- {
- short int sin_family; //AF_INET
- unsigned short int sin_port;//端口号
- struct sin_addr;//网络地址
- } ;
- //IP地址结构in_addr
- struct in_addr
- {
- unsigned long int s_addr;
- };
套接字命名通过bind函数,调用成功返回0,失败返回-1.
- server_address.sin_family = AF_INET;
- server_address.sin_addr.s_addr = inet_addr("127.0.0.1");//主机服务器IP地址
- server_address.sin_port = 9734;//服务器IP端口
- server_len = sizeof(server_address);地址结构长度
- bind(server_sockfd, (struct sockaddr *)&server_address, server_len);//把参数server_address中的地址分配给文件描述符server_sockfd关联的未命名套接字。
4)创建一个连接队列,开始等待用户进行连接
listen()listen函数用于创建一个队列来保存未处理的请求,使得能够在套接字上接受进入的连接。当服务器程序正在忙于处理前一个客户请求时,后续的客户连接放入队列中等待处理。
listen成功返回0,失败-1.
- listen(server_sockfd, 5);//最多连接个数为5
- while(1) {
- char ch;
- printf("server waiting\n");
5)接受连接
accept()accept()函数只有当客户程序试图连接到由socket参数指定的套接字上时才返回。当有客户程序接入时,accept函数将创建一个新的套接字来与该客户进行通信,并返回新套接字的描述符。
- client_len = sizeof(client_address);
- client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
套接字需要先由bind命名,listen调用分配给他一个连接队列。连接客户的地址被放入client_address指向的结构中,client_len指定客户结构的长度。
6)对client_sockfd套接字上的客户进行读写操作
- read(client_sockfd, &ch, 1);
- ch++;
- write(client_sockfd, &ch, 1);
- close(client_sockfd);
5.客户端进程套接字客户程序,创建一个未命名的套接字,然后连接到服务器套接字。
1)变量定义
- int sockfd;//套接字描述符
- int len;
- struct sockaddr_in address;
- int result;
- char ch = 'A';
2)为客户创建一个套接字
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
3)根据服务器的情况给套接字命名
- address.sin_family = AF_INET;
- address.sin_addr.s_addr = inet_addr("127.0.0.1");//IP地址与端口要与服务器一致
- address.sin_port = 9734;
- len = sizeof(address);
4)将客户套接字连接到服务器套接字上
connect()客户程序通过在一个未命名套接字和服务监听套接字之间建立连接的方法来连接到服务器。
- result = connect(sockfd, (struct sockaddr *)&address, len);
- if(result == -1)
- {
- perror("oops: client2");
- exit(1);
- }
参数socket指定的套接字连接到参数address指定的服务器套接字,address指向的结构的长度由参数由len指定。成功返回0,否则-1.
5)连接成功,通过socket进行读写
- write(sockfd, &ch, 1);
- read(sockfd, &ch, 1);
- printf("char from server = %c\n", ch);
- close(sockfd);
- exit(0);
6.运行结果
运行服务器进程时,程序停在server waiting,等待客户接入,这时运行客户端进程时,客户端得到打印信息
char from server = B
服务器程序创建一个AF_INET域的套接字,这个套接字绑定到选定的端口上。指定的地址决定允许建立连接的计算机。这里只把通信限制在本地主机上。
阅读(526) | 评论(0) | 转发(0) |