一、进程是如何进行网络通信的?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. TL-WN725N v2.0 树莓派驱动

    TL-WN725N 1.0版Pi是可以直接识别的,但是后来TL-WN725N又出了个2.0版的,要用第三方驱动. 安装步骤如下: wget https://dl.dropboxusercontent. ...

  2. [转载]WCF系列_分布式事务(下)

    浏览到chnking的WCF的分布式事务处理不错,转载过来分享一下. 1. WCF分布式事务例子这里也用转账的例子说事.用户在系统A和系统B都有账户,账户间的资金可以互转,系统A的资金减少多少,系统B ...

  3. asp.net Frameset框架集的嵌套使用

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Frame.aspx.cs& ...

  4. C++ 模板和 C# 泛型之间的区别(C# 编程指南)

    C# 泛型和 C++ 模板都是用于提供参数化类型支持的语言功能. 然而,这两者之间存在许多差异. 在语法层面上,C# 泛型是实现参数化类型的更简单方法,不具有 C++ 模板的复杂性. 此外,C# 并不 ...

  5. c语言------第一次作业,分支,顺序结构

    1.1思维导图 1.2本章学习体会及代码量学习体 1.2.1学习体会 初次接触C语言,由于比较懒惰,感觉学习脚步跟不上身边的同学,也比较困扰.但伴随着pta上多次显示的##编译错误##,坚持不懈地问舍 ...

  6. “全栈2019”Java多线程第六章:中断线程interrupt()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option)

    今天运行Redis时发生错误,错误信息如下: org.springframework.dao.InvalidDataAccessApiUsageException: MISCONF Redis is ...

  8. 微信小程序ofo小黄车+thinkphp5.0打造全栈应用

    原文地址:https://www.imooc.com/article/20310 ofo至今还没有微信小程序(很费解),每次用ofo都得去支付宝,很不方便,我用微信用的比较多,无意间在简书上面看到某人 ...

  9. Bootstrap Table使用方法详解

    http://www.jb51.net/article/89573.htm bootstrap-table使用总结 bootstrap-table是在bootstrap-table的基础上写出来的,专 ...

  10. 架构师养成记--37.简单shell编程

    一.HelloWord.sh echo 表示打印,可以在sh文件中写诸如pwd.ls 这样的命令,使用命令的时候尽量使用全路径. #!/bin/sh #this is my first sh echo ...