前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程。通常我们使用socket进行网络编程,这里将会简单地讲述如何使用socket进行简单的网络编程。
 
一、什么是socket
socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。
 
二、套接字的属性
 
套接字的特性由3个属性确定,它们分别是:域、类型和协议。
 
1、套接字的域
 
它指定套接字通信中使用的网络介质,最常见的套接字域是AF_INET,它指的是Internet网络。当客户使用套接字进行跨网络的连接时,它就需要用到服务器计算机的IP地址和端口来指定一台联网机器上的某个特定服务,所以在使用socket作为通信的终点,服务器应用程序必须在开始通信之前绑定一个端口,服务器在指定的端口等待客户的连接。另一个域AF_UNIX表示UNIX文件系统,它就是文件输入/输出,而它的地址就是文件名。
 
2、套接字类型
 
因特网提供了两种通信机制:流(stream)和数据报(datagram),因而套接字的类型也就分为流套接字和数据报套接字。这里主要讲流套接字。
 
流套接字由类型SOCK_STREAM指定,它们是在AF_INET域中通过TCP/IP连接实现,同时也是AF_UNIX中常用的套接字类型。流套接字提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送的机制。
 
与流套接字相对的是由类型SOCK_DGRAM指定的数据报套接字,它不需要建立连接和维持一个连接,它们在AF_INET中通常是通过UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并一需要总是要建立和维持一个连接。
 
3、套接字协议
只要底层的传输机制允许不止一个协议来提供要求的套接字类型,我们就可以为套接字选择一个特定的协议。通常只需要使用默认值。
 
三、套接字地址
 
每个套接字都有其自己的地址格式,对于AF_UNIX域套接字来说,它的地址由结构sockaddr_un来描述,该结构定义在头文件sys/un.h中,它的定义如下:
 
  1. struct sockaddr_un{
  2. sa_family_t sun_family;//AF_UNIX,它是一个短整型
  3. char        sum_path[];//路径名
  4. };
对于AF_INET域套接字来说,它的地址结构由sockaddr_in来描述,它至少包括以下几个成员:
 
  1. struct sockaddr_in{
  2. short int            sin_family;//AF_INET
  3. unsigned short int    sin_port;//端口号
  4. struct in_addr        sin_addr;//IP地址
  5. };
而in_addr被定义为:
 
  1. struct in_addr{
  2. unsigned long int s_addr;
  3. };
四、基于流套接字的客户/服务器的工作流程
使用socket进行进程通信的进程采用的客户/服务器系统是如何工作的呢?
 
1、服务器端
 
首先服务器应用程序用系统调用socket来创建一个套接安,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。
 
接下来,服务器进程会给套接字起个名字,我们使用系统调用bind来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。
 
然后,系统调用listen来创建一个队列并将其用于存放来自客户的进入连接。
 
最后,服务器通过系统调用accept来接受客户的连接。它会创建一个与原有的命名套接不同的新套接字,这个套接字只用于与这个特定客户端进行通信,而命名套接字(即原先的套接字)则被保留下来继续处理来自其他客户的连接。
 
2、客户端
 
基于socket的客户端比服务器端简单,同样,客户应用程序首先调用socket来创建一个未命名的套接字,然后将服务器的命名套接字作为一个地址来调用connect与服务器建立连接。
 
一旦连接建立,我们就可以像使用底层的文件描述符那样用套接字来实现双向数据的通信。
   
五、流式socket的接口及作用
 
socket的接口函数声明在头文件sys/types.h和sys/socket.h中。
 
1、创建套接字——socket系统调用
 
该函数用来创建一个套接字,并返回一个描述符,该描述符可以用来访问该套接字,它的原型如下:
  1. int socket(int domain, int type, int protocol);
函数中的三个参数分别对应前面所说的三个套接字属性。protocol参数设置为0表示使用默认协议。
 
2、命名(绑定)套接字——bind系统调用
 
该函数把通过socket调用创建的套接字命名,从而让它可以被其他进程使用。对于AF_UNIX,调用该函数后套接字就会关联到一个文件系统路径名,对于AF_INET,则会关联到一个IP端口号。函数原型如下:
  1. int bind( int socket, const struct sockaddr *address, size_t address_len);
成功时返回0,失败时返回-1;
 
3、创建套接字队列(监听)——listen系统调用
 
该函数用来创建一个队列来保存未处理的请求。成功时返回0,失败时返回-1,其原型如下:
  1. int listen(int socket, int backlog);
backlog用于指定队列的长度,等待处理的进入连接的个数最多不能超过这个数字,否则往后的连接将被拒绝,导致客户的连接请求失败。调用后,程序一直会监听这个IP端口,如果有连接请求,就把它加入到这个队列中。
 
4、接受连接——accept系统调用
 
该系统调用用来等待客户建立对该套接字的连接。accept系统调用只有当客户程序试图连接到由socket参数指定的套接字上时才返回,也就是说,如果套接字队列中没有未处理的连接,accept将阻塞直到有客户建立连接为止。accept函数将创建一个新套接字来与该客户进行通信,并且返回新套接字的描述符,新套接字的类型和服务器监听套接字类型是一样的。它的原型如下:
  1. int accept(int socket, struct sockaddr *address, size_t *address_len);
address为连接客户端的地址,参数address_len指定客户结构的长度,如果客户地址的长度超过这个值,它将会截断。
 
5、请求连接——connect系统调用
 
该系统调用用来让客户程序通过在一个未命名套接字和服务器监听套接字之间建立连接的方法来连接到服务器。它的原型如下:
  1. int connect(int socket, const struct sockaddr *address, size_t address_len);
参数socket指定的套接字连接到参数addres指定的服务器套接字。成功时返回0,失败时返回-1.
 
6、关闭socket——close系统调用
 
该系统调用用来终止服务器和客户上的套接字连接,我们应该总是在连接的两端(服务器和客户)关闭套接字。
 
六、进程使用流式socket进行通信
下面用多个客户程序和一个服务器程序来展示进程间如何利用套接字进行通信。
 
sockserver.c是一个服务器程序,它首先创建套接字,然后绑定一个端口再监听套接字,忽略子进程的停止消息等,然后它进入循环,一直循环检查是否有客户连接到服务器,如果有,则调用fork创建一个子进程来处理请求。利用read系统调用来读取客户端发来的信息,利用write系统调用来向客户端发送信息。这个服务器的工作非常简单,就是把客户发过来的字符+1,再发送回给客户。
 
sockclient.c是一个客户程序,它同样要先创建套接,然后连接到指定IP端口服务器,如果连接成功,就用write来发送信息给服务器,再用read获取服务器处理后的信息,再输出。
 
服务器sockserver.c的源代码如下:
 
  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <netinet/in.h>
  5. #include <signal.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. int main()
  9. {
  10. int server_sockfd = -1;
  11. int client_sockfd = -1;
  12. int client_len = 0;
  13. struct sockaddr_in server_addr;
  14. struct sockaddr_in client_addr;
  15. //创建流套接字
  16. server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  17. <span style="white-space:pre">  </span>//设置服务器接收的连接地址和监听的端口
  18. server_addr.sin_family = AF_INET;//指定网络套接字
  19. server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//接受所有IP地址的连接
  20. server_addr.sin_port = htons(9736);//绑定到9736端口
  21. //绑定(命名)套接字
  22. bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
  23. //创建套接字队列,监听套接字
  24. listen(server_sockfd, 5);
  25. //忽略子进程停止或退出信号
  26. signal(SIGCHLD, SIG_IGN);
  27. while(1)
  28. {
  29. char ch = '\0';
  30. client_len = sizeof(client_addr);
  31. printf("Server waiting\n");
  32. //接受连接,创建新的套接字
  33. client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_len);
  34. if(fork() == 0)
  35. {
  36. //子进程中,读取客户端发过来的信息,处理信息,再发送给客户端
  37. read(client_sockfd, &ch, 1);
  38. sleep(5);
  39. ch++;
  40. write(client_sockfd, &ch, 1);
  41. close(client_sockfd);
  42. exit(0);
  43. }
  44. else
  45. {
  46. //父进程中,关闭套接字
  47. close(client_sockfd);
  48. }
  49. }
  50. }
客户sockclient.c的源代码如下:
 
  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <netinet/in.h>
  5. #include <arpa/inet.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. int main()
  9. {
  10. int sockfd = -1;
  11. int len = 0;
  12. struct sockaddr_in address;
  13. int result;
  14. char ch = 'A';
  15. //创建流套接字
  16. sockfd = socket(AF_INET, SOCK_STREAM, 0);
  17. //设置要连接的服务器的信息
  18. address.sin_family = AF_INET;//使用网络套接字
  19. address.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器地址
  20. address.sin_port = htons(9736);//服务器所监听的端口
  21. len = sizeof(address);
  22. //连接到服务器
  23. result = connect(sockfd, (struct sockaddr*)&address, len);
  24. if(result == -1)
  25. {
  26. perror("ops:client\n");
  27. exit(1);
  28. }
  29. //发送请求给服务器
  30. write(sockfd, &ch, 1);
  31. //从服务器获取数据
  32. read(sockfd, &ch, 1);
  33. printf("char form server = %c\n", ch);
  34. close(sockfd);
  35. exit(0);
  36. }
运行结果如下:
 
 
在本例子中,我们启动了一个服务器程序和三个客户程序,从运行的结果来看,客户端发送给服务器程序的所有请求都得到了处理,即把A变成了B。对于服务器和客户程序之间使用的read和write系统调用跟使用命名管道时阻塞的read、write系统调用一样。例如客户程序调用read时,如果服务器程序没有向指定的客户程序的socket中写入信息,则read调用会一直阻塞。
 
七、流式套接字给我印象
给我的感觉是流式套接字很像命名管道,但是它却可以使不在同一台计算机而通过网络连接的不同计算机上的进程进行通信,功能真是非常的强大。

Linux进程间通信——使用流套接字的更多相关文章

  1. Linux进程间通信——使用数据报套接字

    前一篇文章, Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报 ...

  2. IPC——流套接字通信

    Linux进程间通信——使用流套接字 前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进 ...

  3. Linux进程间通信(八):流套接字 socket()、bind()、listen()、accept()、connect()、read()、write()、close()

    前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程.通常我们使用socket进行网络编 ...

  4. Linux网络编程之套接字基础

    1.套接字的基本结构 struct sockaddr 这个结构用来存储套接字地址. 数据定义: struct sockaddr { unsigned short sa_family; /* addre ...

  5. Linux学习笔记30——套接字

    一 什么是套接字 套接字是一种通信机制,凭借这种机制,客户/服务器系统的开发既可以在本地单机上进行,也可以跨网络进行. 二 套接字属性 套接字的特性由3个属性确定,它们是:域,类型和协议   1 套接 ...

  6. Linux网络编程——原始套接字实例:MAC 头部报文分析

    通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...

  7. linux 进程通信 :流套接字

    消息队列是可以实现没有共同关系的进程之间的通信.Socket则可以实现不同计算机的不同进程之间的通信. //地址的结构体 struct sockaddr_in{ short int sin_famil ...

  8. Linux网络通信(TCP套接字编写,多进程多线程版本)

    预备知识 源IP地址和目的IP地址 IP地址在上一篇博客中也介绍过,它是用来标识网络中不同主机的地址.两台主机进行通信时,发送方需要知道自己往哪一台主机发送,这就需要知道接受方主机的的IP地址,也就是 ...

  9. [linux basic基础]----套接字

    套接字是一种通信机制,凭借这种机制client/server系统的开发者既可以在本地机器上进行,也可以跨网络进行. 1,服务器应用程序用系统调用socket来创建一个套接字,他是系统分配给服务器进程的 ...

随机推荐

  1. 在非MFC的win 32程序里面能够使用CString类

    论坛有会员用到了.,今天给大家说说CSring如何在非mfc下的调用第一:先要包含 #include "afx.h" 包含之后会报windows.h重复定义我们需要把这个头文件包含 ...

  2. iphone6闪存检测

    iPhone6自从发布以后一直又不少的诟病和非议,比如一机难求,容易掰弯,程序崩溃等, 甚至传出了苹果将要召回这些问题设备,最近有人终于查出了iPhone6安装大量程序后崩溃的原因,原因就是大容量的i ...

  3. Chart.js | HTML5 Charts for your website.

    Chart.js | HTML5 Charts for your website. Chart.js

  4. 360WIFI下使用Fiddler抓取手机APP流量

    过程其实很简单,仅仅做个记录.过程适用Android和ios手机 fidder的原理如下: 下面开始步骤: 1.在fidder中设置代理:Tools--->Fidder Options,设置端口 ...

  5. [置顶] think in java interview番外篇-谈程序员如何修练英语

    一.程序员对英语能力的重视度和能力要求应该是在各行各业中排在比较靠前的 这样说吧,英语程度的好坏直接影响着一个程序员的编程.开发.创新能力. 道理很简单: 1. 计算机和软件是用英语创造出来的 2. ...

  6. java面试题集2

    JAVA面试题-CORE JAVA部分          1.  在main(String[] args)方法内是否可以调用一个非静态方法? 答案:不能 2.  同一个文件里是否可以有两个public ...

  7. ubuntu 文件readonly的问题: W10: Warning: Changing a readonly file 解决办法

    日前,笔者遇到一个奇怪且让人蛋疼的问题,借用别人的话"大家在linux上编辑文件的时候,明明是使用的root登录的,可是这种至高无上的权限在按下i的时候被那串红色错误亵渎了W10: Warn ...

  8. mvc 日历控件

    第二个是日历控件,在网上查了一个普通的日历控件,也生成了下拉的日历样子,但是一些脚本比如选择年月,需要一些时间,最后只好套用了My97 DatePicker,这样以来其实简单多了. 第一步:下载 My ...

  9. SQL*Plus break与compute的简单用法

    SQL*Plus break与compute的简单用法在SQL*Plus提示符下输出求和报表,我们可以借助break与compute两个命令来实现.这个两个命令简单易用,可满足日常需求,其实质也相当于 ...

  10. Splash界面布局与代码实现(一)

    xml界面布局代码: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns ...