一、进程是如何进行网络通信的?socket通信的过程?

  同一机器上的不同进程之间的通信方式有很多种,主要使用消息传递或共享内存。而跨网络的进程是几乎都是使用socket通信,例如web服务器,QQ。

socket即是一种特殊的文件,操作系统提供了一些socket函数就是对其进行的操作(读/写IO、打开、关闭),进程间的通信就是靠读写各自的socket完成的。

通信的过程

server:

  1. 使用socket()系统调用创建一个指定类型和协议套接字
  2. 使用bind()系统调用给创建的socket命名,这个名字就是通常所说的服务器地址(ip地址+端口号),例如服务器的80端口
  3. 使用listen()系统调用,监听来自客户端的连接。
  4. 使用accept()系统调用,接受来自客户端的连接,这个调用一直处于阻塞状态,直到有客户端的连接。
  5. 向连接建立的socket里面读写数据(通信)

  注意:server最初会创建一个socktet,收到连接请求后(accept())之后会创建一个与原有的命名套接字不同的套接字。这个新的套接字只与这个特定的client通信,而命名套接字会保留下来继续处理来自client的连接。

client:

  1. 使用socket()系统调用创建一个指定类型和协议的套接字。
  2. 使用connect()将1中创建的socket连接到服务器的地址。
  3. 使用系统调用发送和接受数据,最简单的是read( )和write()函数。

二、主要知识点和系统调用介绍

  1. int socket(int domain, int type, int protocol); 创建指定类型的socket,两个进程能够通信,必须使用相同域和类型的套接字。

  • domain:主要有AF_INET,AF_INIT6,分别表示IPv4、IPv6域
  • type:SOCK_STREAM 表示有序、可靠、双向的面向连接的字节流 ;SOCK_DGRAM 表示长度固定的、无连接的不可靠的报文传递。
  • protocol:一般是0,系统会根据前面的域名和类型,选择合适的协议如TCP、UDP协议等。

2.  int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • sockfd:创建scoket时返回的socket描述符。类似于文件描述符号。
  • addr:socket绑定的地址
  • addrlen:第二个参数是指针,第三个参数是长度

地址格式:

struct sockaddr_in {

    sa_family_t    sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};

注意:通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。

这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

3.其它

  • int listen(int sockfd, int backlog); //监听socket的描述符,backlog表示最到连接数
  • int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); //客户端连接服务器的地址
  • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //服务器接受连接
  • read()/write()
  • recv()/send()
  • readv()/writev()
  • recvmsg()/sendmsg()
  • recvfrom()/sendto()  

三、简单易学的socket程序示例

一个简单的示例,创建AF_INET型域和SOCK_STREAM面向连接的socket字,server开启服务,client请求连接,向server发送消息,server收到消息后,回应client,结束连接,也关闭服务器。
代码中有注释,基本上是安装前面socket通信的步骤写的代码。
 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> void error(const char *msg)
{
perror(msg);
exit();
} int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < ) {
fprintf(stderr,"ERROR, no port provided\n");
exit();
} //create a socktet
sockfd = socket(AF_INET, SOCK_STREAM, );
if (sockfd < )
error("ERROR opening socket"); //bind an address to that socktet
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < )
error("ERROR on binding"); // listen to the socktet
listen(sockfd,); //accept connection and create a corresponding new socket
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < )
error("ERROR on accept"); //communication with the new sockfd(read and write data)
bzero(buffer,);
n = read(newsockfd,buffer,);
if (n < ) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(newsockfd,"I got your message",);
if (n < ) error("ERROR writing to socket"); //ends the connection
close(newsockfd); //ends server
close(sockfd);
return ;
}
 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> void error(const char *msg)
{
perror(msg);
exit();
} int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server; char buffer[];
if (argc < ) {
fprintf(stderr,"usage %s hostname port\n", argv[]);
exit();
}
portno = atoi(argv[]); //create a socket
sockfd = socket(AF_INET, SOCK_STREAM, );
if (sockfd < )
error("ERROR opening socket"); server = gethostbyname(argv[]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit();
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno); //connect
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < )
error("ERROR connecting");
printf("Please enter the message: ");
bzero(buffer,);
fgets(buffer,,stdin); //write and read data
n = write(sockfd,buffer,strlen(buffer));
if (n < )
error("ERROR writing to socket");
bzero(buffer,);
n = read(sockfd,buffer,);
if (n < )
error("ERROR reading from socket");
printf("%s\n",buffer); //end this connection
close(sockfd);
return ;
}

使用方法:./server 3000 ./client localhost 3000 一般使用2000~65536之间的端口号

 上面的代码虽然能让人很快理解,进程之间的网络通信是怎么进行的。但是server只是接收一次消息,就马上结束退出。

而实际中的server是一直在运行的,并且能够同时接收多个client的访问,典型的做法是server每次收到连接请求式都fork一个新的子进程来处理连接请求。

为了避免产生zombie进程,需要在程序中使用signal(SIGCHLD,SIG_IGN);使得父进程无视子进程的die。

改进后的代码:

 /* A simple server in the internet domain using TCP
The port number is passed as an argument
This version runs forever, forking off a separate
process for each connection
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h> void dostuff(int); /* function prototype */
void error(const char *msg)
{
perror(msg);
exit();
} int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, pid;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr; if (argc < ) {
fprintf(stderr,"ERROR, no port provided\n");
exit();
}
sockfd = socket(AF_INET, SOCK_STREAM, );
if (sockfd < )
error("ERROR opening socket"); bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < )
error("ERROR on binding"); listen(sockfd,);
clilen = sizeof(cli_addr);
signal(SIGCHLD,SIG_IGN);
while () {
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < )
error("ERROR on accept");
pid = fork();
if (pid < )
error("ERROR on fork");
if (pid == ) {
//close(sockfd);
dostuff(newsockfd);
exit();
}
else close(newsockfd);
} /* end of while */
close(sockfd);
return ; /* we never get here */
} /******** DOSTUFF() *********************
There is a separate instance of this function
for each connection. It handles all communication
once a connnection has been established.
*****************************************/
void dostuff (int sock)
{
int n;
char buffer[]; bzero(buffer,);
n = read(sock,buffer,);
if (n < ) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(sock,"I got your message",);
if (n < ) error("ERROR writing to socket");
}

四、总结

socket几乎是网络间进程通信的唯一手段,它的通信是非常简单并且重要的,需要记住。虽然在实际的大型的网络服务器中,不会使用每次都使用socke系统调用t编程,而是使用包装过的库,但是还是需要了解。

当让要想成为一个C++高手还是需要熟悉某种网络库,能力强的话可以自己包装实现一个网络库。但是我觉得新手还是先看看人家的库~~如:比如boost-asio、比如libevent,boost-asio

参考:

  1.http://www.linuxhowtos.org/C_C++/socket.htm?userrate=2

  2.http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html

linux进程间的网络通信的更多相关文章

  1. Linux进程间的通信

    一.管道 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: A. 管道是半双工的,数据只能向一个方向流动: B. 需要双工通信时,需要建立起两个管道: C. 只能用于父子进程或者兄弟 ...

  2. Linux 进程间通讯方式 pipe()函数 (转载)

    转自:http://blog.csdn.net/ta893115871/article/details/7478779 Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道( ...

  3. PHP与Linux进程间的通信

    进程间通信预计是公司考察应届毕业生的必考点(嵌入式行业).当然非常多公司考的是算法. 不查阅资料,我脑子里能想到的 [1] 管道, (有名.无名) [2] 父子进程 [3] System V (消息队 ...

  4. linux进程间的通信方式

    linux进程间的通信 进程间的通信就是不同的进程之间传播或交换信息,进程的用户空间是互相独立,进程之间可以利用系统空间交换信息. 管道 允许将一个进程的标准输出和另一个进程的标准输入连接在一起,主要 ...

  5. Linux 进程间通讯详解一

    进程间的通讯 两台主机间的进程通讯 --socket 一台主机间的进程通讯 --管道(匿名管道,有名管道) --System V进程间通信(IPC)包括System V消息队列,System V信号量 ...

  6. Linux进程间通讯的几种方式的特点和优缺点,和适用场合

    http://blog.csdn.net/jeffcjl/article/details/5523569 由于不同的进程运行在各自不同的内存空间中.一方对于变量的修改另一方是无法感知的.因此.进程之间 ...

  7. linux进程间通讯-System V IPC 信号量

    进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的很多其它内容,能够阅读我的还有一篇文章:Linux进程间通信--使用信号.以下就进入信号量的 ...

  8. Linux 进程间通讯

    一.Linux 下进程间通讯方式 1)管道(Pipe)及有名管道(named pipe): 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

  9. Linux进程间的通信方式和原理

    进程的概念 进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放.可以认为进程是一个程序的一次执行过程. 进程通信的概念 进程用户空间是 ...

随机推荐

  1. AngularJs2 构建简单的英雄编辑器

    参考上一篇文章的步骤,重新把相关环境准备.目录结构走一遍. 这一次我们要开始创建真正的Angularjs项目了. 显示我们的英雄 我们要在应用中显示英雄数据 我们来为 AppComponent 添加两 ...

  2. 对象初始化的完整过程(C#)

    1.静态构造函数 在引入本文的主题之前,我们先来铺垫一下吧,看看静态构造函数的概念及用途. C#中允许创建无参数构造函数,该函数仅执行一次.它一般被用来初始化静态字段.CLR不能保证在某个特定时刻执行 ...

  3. Cesium Language (CZML) 入门1 — CZML Structure(CZML的结构)

    原文:https://github.com/AnalyticalGraphicsInc/cesium/wiki/CZML-Structure CZML是一种用来描述动态场景的JSON架构的语言,主要用 ...

  4. ASP.NET中Onclick和OnserverClick事件的区别 (转)

    对于服务器按钮控件(即<asp:Button>类型的按钮): 服务器响应事件:OnClick 客户端响应属性:OnClientClick 对于html按钮控件(即<input typ ...

  5. MaxScript镜像函数

    看到有网友需要写的,其实镜像就是缩放改为负数 Fn MirrorObject argObjects argAxisName = ( local axisNames = #(#x,#y,#z) do ( ...

  6. 【OCP 12c】最新CUUG OCP-071考试题库(62题)

    62.(13-17)choose the best answer: You need to list the employees in DEPARTMENT_ID 30 in a single row ...

  7. Spring框架注解

    这四个注解,功能都是一样的,都是用来创建对象的. 但是为什么有这么四个吗?Spring中提供了三个@Component的衍生注解:(功能目前来讲是一样的) @Controller      :WEB层 ...

  8. BZOJ 1934--善意的投票(最小割)

    1934: [Shoi2007]Vote 善意的投票 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 2354  Solved: 1471[Submit][ ...

  9. php中ajax实例,用到json

    调用的博客园苏恒锋的文章.先收藏,在学习: http://www.cnblogs.com/in-loading/archive/2012/05/18/2508123.html 程序中两个文件jsonT ...

  10. 记一次MBR锁机病毒分析

    有一天,在机缘巧合之下我获得了一个锁机软件(是多巧合阿喂!),然后兴高采烈的把它拖入了虚拟机里蹂躏(>_<!). 很巧,软件有虚拟机检测... Emmmm好吧,随便过一下... 我用的虚拟 ...