网络编程1

来源:互联网 发布:全景图拼合软件 编辑:程序博客网 时间:2024/06/07 00:03
1. 网络中进程之间如何通信

网间进程通信要解决的是不同主机进程间的相互通信问题(可把同机进程通信看作是其中的特例)。为此,首先要解决的是网间进程标识问题。同一主机上,不同进程可用进程号(process ID)唯一标识。但在网络环境下,各主机独立分配的进程号不能唯一标识该进程。 其次,操作系统支持的网络协议众多,不同协议的工作方式不同,地址格式也不同。因此,网间进程通信还要解决多重协议的识别问题。 

TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

 

 

2. 什么是TCP/IPUDP

 

  TCP/IPTransmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。    

     TCP/IP协议存在于OS中,网络服务通过OS提供,在OS中增加支持TCP/IP的系统调用——Berkeley套接字,如SocketConnectSendRecv

UDPUser Data Protocol用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。这种协议并不能保证我们的网络程序的连接是可靠的,所以我们现在编写的程序一般是采用TCP协议的.

 

 

3.何为 socket套接字?

     socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open>读写write/read>关闭close”模式来操作。Socket就是该模式的一个实现,       socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/IO、打开、关闭).

     说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

     

 

 

 

4. 基本的SOCKET接口函数常用实例:

(1)建立Socket  

 

[cpp] view plain copy
  1. /* 网络编程 */  
  2.   
  3. /* 1.建立socket函数  */  
  4.   
  5. /* 常用实例: */  
  6.   
  7. int sockfd;  
  8.   
  9. //AF_INET:IPV4  SOCK_STREAM:TCP  
  10. sockfd = socket(AF_INET,SOCK_STREAM,0);  
  11. if(-1 == sockfd)  
  12. {  
  13.     perror("socket");  
  14.     exit(1);  
  15. }  
[cpp] view plain copy
  1. /* 网络编程 */  
  2.   
  3. /* 1.建立socket函数  */  
  4.   
  5. /* 常用实例: */  
  6.   
  7. int sockfd;  
  8.   
  9. //AF_INET:IPV4  SOCK_STREAM:TCP  
  10. sockfd = socket(AF_INET,SOCK_STREAM,0);  
  11. if(-1 == sockfd)  
  12. {  
  13.     perror("socket");  
  14.     exit(1);  
  15. }  

 

2)绑定地址

 

[cpp] view plain copy
  1. /* 绑定地址:bind()  
  2. bind()用来设置给sockfd的Socket的一个名称, 
  3. 此名称由参数my_addr指向一个sockaddr结构 
  4. */  
  5.   
  6. //常用实例:  
  7.   
  8. struct sockaddr_in my_addr;  //定义结构体变量  
  9.   
  10. memset(&my_addr,0,sizeof(struct sockaddr));  //将结构体清空  
  11.   
  12. my_addr.sin_family = AF_INET;  // 表示采用IPV4网络协议  
  13. my_addr.sin_port = htons(8888);  //表示端口号为8888  
  14. my_addr.sin_addr.s_addr = inet_addr("192.168.0.101");  //将IP地址字符串转换成二进制数字  
  15.   
  16. int ret;  
  17.   
  18. ret = bind(sockfd,(struct aockaddr*)&my_addr,sizeof(struct sockaddr));  
  19. if(-1 == ret)  
  20. {  
  21.     perror("bind");  
  22.     close(sockfd);  
  23.     exit(1);  
  24. }  
  25.   
  26.   
  27. /*注: 
  28.     1.通过将my_addr.sin_port置为0,函数会自动选择一个未被占用的端口来使用 
  29.     2.将my_addr.sin_addr.s_addr 设置为INADDR_ANY,系统会自动填入本机ip地址 */  
[cpp] view plain copy
  1. /* 绑定地址:bind()  
  2. bind()用来设置给sockfd的Socket的一个名称, 
  3. 此名称由参数my_addr指向一个sockaddr结构 
  4. */  
  5.   
  6. //常用实例:  
  7.   
  8. struct sockaddr_in my_addr;  //定义结构体变量  
  9.   
  10. memset(&my_addr,0,sizeof(struct sockaddr));  //将结构体清空  
  11.   
  12. my_addr.sin_family = AF_INET;  // 表示采用IPV4网络协议  
  13. my_addr.sin_port = htons(8888);  //表示端口号为8888  
  14. my_addr.sin_addr.s_addr = inet_addr("192.168.0.101");  //将IP地址字符串转换成二进制数字  
  15.   
  16. int ret;  
  17.   
  18. ret = bind(sockfd,(struct aockaddr*)&my_addr,sizeof(struct sockaddr));  
  19. if(-1 == ret)  
  20. {  
  21.     perror("bind");  
  22.     close(sockfd);  
  23.     exit(1);  
  24. }  
  25.   
  26.   
  27. /*注: 
  28.     1.通过将my_addr.sin_port置为0,函数会自动选择一个未被占用的端口来使用 
  29.     2.将my_addr.sin_addr.s_addr 设置为INADDR_ANY,系统会自动填入本机ip地址 */  

3)监听

 

[cpp] view plain copy
  1. /* 监听:listen() */  
  2.   
  3. //常用实例:  
  4.   
  5. int ret;  
  6.   
  7. ret = listen(sockfd,10);   //同时能处理10个连接要求  
  8. if(-1 == ret)  
  9. {  
  10.     perror("listen");  
  11.     close(sockfd);  
  12.     exit(1);  
  13. }  
[cpp] view plain copy
  1. /* 监听:listen() */  
  2.   
  3. //常用实例:  
  4.   
  5. int ret;  
  6.   
  7. ret = listen(sockfd,10);   //同时能处理10个连接要求  
  8. if(-1 == ret)  
  9. {  
  10.     perror("listen");  
  11.     close(sockfd);  
  12.     exit(1);  
  13. }  

   4)接收请求

 

[cpp] view plain copy
  1. /* 接收请求:accept() 
  2.     例如:客户请求来连接服务器,服务器会先做一些绑定和监听 
  3.     的操作.之后,如果允许连接,则会调用accept函数产生一个新 
  4.     的套接字,然后,用这个新的套接字跟客户进行收发数据. 
  5.     即,服务器和一个客户端连接成功,会产生2个套接字. 
  6. */  
  7.   
  8.   
  9. struct sockaddr_in client_addr;  
  10.   
  11. memset(&client_addr,0,sizeof(struct sockaddr));  
  12.   
  13. int addrlen = sizeof(struct sockaddr);  
  14. int new_fd = accept(sockfd,(struct sockaddr*)&client_addr,&addrlen);  
  15. if(-1 == new_fd)  
  16. {  
  17.     perror("accept");  
  18.     close(sockfd);  
  19.     exit(1);  
  20. }  
  21.   
  22. printf("%s %d success connect !\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));  
[cpp] view plain copy
  1. /* 接收请求:accept() 
  2.     例如:客户请求来连接服务器,服务器会先做一些绑定和监听 
  3.     的操作.之后,如果允许连接,则会调用accept函数产生一个新 
  4.     的套接字,然后,用这个新的套接字跟客户进行收发数据. 
  5.     即,服务器和一个客户端连接成功,会产生2个套接字. 
  6. */  
  7.   
  8.   
  9. struct sockaddr_in client_addr;  
  10.   
  11. memset(&client_addr,0,sizeof(struct sockaddr));  
  12.   
  13. int addrlen = sizeof(struct sockaddr);  
  14. int new_fd = accept(sockfd,(struct sockaddr*)&client_addr,&addrlen);  
  15. if(-1 == new_fd)  
  16. {  
  17.     perror("accept");  
  18.     close(sockfd);  
  19.     exit(1);  
  20. }  
  21.   
  22. printf("%s %d success connect !\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));  

(5)连接服务器

 

[cpp] view plain copy
  1. /* 连接服务器: 
  2.     int connect(int sockfd,struct sockaddr* serv_addr,int addrlen); 
  3.  
  4.     connect()用来将参数sockfd的Socket连接到参数ser_addr指定的网络地址 
  5. */  
  6.   
  7. struct sockaddr_in serv_addr;  //请求来连接服务器  
  8.   
  9. memset(&serv_addr,0,sizeof(struct sockaddr));  
  10.   
  11. serv_addr.sin_family = AF_INET;  
  12. serv_addr.sin_port = htons(8888);  //服务器的端口  
  13. serv_addr.sin_addr.s_addr = inet_addr("192.168.0.101"); //服务器的ip  
  14.   
  15. int ret;  
  16. ret = connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr));  
  17. if(-1 == ret)  
  18. {  
  19.     perror("connect");  
  20.     close(sockfd);  
  21.     exit(1);  
  22. }  
[cpp] view plain copy
  1. /* 连接服务器: 
  2.     int connect(int sockfd,struct sockaddr* serv_addr,int addrlen); 
  3.  
  4.     connect()用来将参数sockfd的Socket连接到参数ser_addr指定的网络地址 
  5. */  
  6.   
  7. struct sockaddr_in serv_addr;  //请求来连接服务器  
  8.   
  9. memset(&serv_addr,0,sizeof(struct sockaddr));  
  10.   
  11. serv_addr.sin_family = AF_INET;  
  12. serv_addr.sin_port = htons(8888);  //服务器的端口  
  13. serv_addr.sin_addr.s_addr = inet_addr("192.168.0.101"); //服务器的ip  
  14.   
  15. int ret;  
  16. ret = connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr));  
  17. if(-1 == ret)  
  18. {  
  19.     perror("connect");  
  20.     close(sockfd);  
  21.     exit(1);  
  22. }  

(6)发送数据

 

[cpp] view plain copy
  1. /* 发送数据  
  2.  
  3.     int send(int sockfd, void *msg,int len,unsigned int flags); 
  4.  
  5.     send()用来将数据由指定的Socket传给对方主机; 
  6.     sockfd为已经连接好的Socket; 
  7.     msg为欲连线的数据内容;len为数据长度 
  8. */  
  9.   
  10. int ret;  
  11.   
  12. ret = send(new_fd,"hello",6,0);  
  13. if(-1 == ret)  
  14. {  
  15.     perror("send");  
  16.     close(sockfd);  
  17.     close(new_fd);  
  18.     exit(1);  
  19. }  
[cpp] view plain copy
  1. /* 发送数据  
  2.  
  3.     int send(int sockfd, void *msg,int len,unsigned int flags); 
  4.  
  5.     send()用来将数据由指定的Socket传给对方主机; 
  6.     sockfd为已经连接好的Socket; 
  7.     msg为欲连线的数据内容;len为数据长度 
  8. */  
  9.   
  10. int ret;  
  11.   
  12. ret = send(new_fd,"hello",6,0);  
  13. if(-1 == ret)  
  14. {  
  15.     perror("send");  
  16.     close(sockfd);  
  17.     close(new_fd);  
  18.     exit(1);  
  19. }  

(7)接收数据

 

 

[cpp] view plain copy
  1. /* 接收数据 */  
  2.   
  3. //常用实例:  
  4.   
  5. char buf[1024] = {0};  
  6. int ret;  
  7.   
  8. ret = recv(new_fd,buf,sizefo(buf),0);  
  9. if(-1 == ret)  
  10. {  
  11.     perror("recv");  
  12.     close(sockfd);  
  13.     close(new_fd);  
  14.     exit(1);  
  15. }  
  16.   
  17. puts(buf);  
[cpp] view plain copy
  1. /* 接收数据 */  
  2.   
  3. //常用实例:  
  4.   
  5. char buf[1024] = {0};  
  6. int ret;  
  7.   
  8. ret = recv(new_fd,buf,sizefo(buf),0);  
  9. if(-1 == ret)  
  10. {  
  11.     perror("recv");  
  12.     close(sockfd);  
  13.     close(new_fd);  
  14.     exit(1);  
  15. }  
  16.   
  17. puts(buf);  

 5.为什么在SocketTCP建立连接协议是三次握手,而关闭连接却是四次握手呢?

这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACKSYNACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

 

6.在TCP连接的终止(四次握手释放)时为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

原创粉丝点击