socket网络编程
1.进程间如何通信
本地进程间通信我们知道有以下几种方式:
1)消息传递(管道,FIFO,消息队列)
管道是一个进程的数据流到另一个进程的通道,即一个进程的数据输出作为另一个进程的数据输入,管道起到了桥梁的作用。
2)共享内存
不同进程访问同一个逻辑内存
3)同步
互斥量,条件变量,读写锁,信号量
4)远程过程调用
但是网络间的进程是如何通信的呢?如浏览器进程如何与web服务器通信,QQ聊天时,QQ进程如何与服务器或你好友所在的QQ进程通信?
答案是socket,网络间的进程通信几乎都是用的socket.
2.如何识别网络中唯一进程
本地进程间可以通过PID来唯一标识一个进程
网络中我们根据TCP/IP协议族来标识唯一进程,网络层的“IP地址”可唯一标识网络中的主机,而传输层的“协议+端口”可唯一标识主机中的进程。“IP地址+协议+端口”就可以确定唯一进程了。
3.进程间如何通过socket进行通讯
Linux的哲学是一切皆文件,socket也是一种文件。可以使用“打开-读写-关闭”来操作。socket是应用层和运输层之间的一个抽象层。
socket通信流程:
1)服务端创建socket
socket()
2)服务端绑定端口号
bind()
3)服务端监听端口号
listen()
4)客户端创建socket
socket()
5)客户端主动打开socket,连接服务器端socket
connect()
6)服务端接收客户端请求,socket被动打开
同步:accept() 异步:beginaccept()
7)客户端socket向服务端socket写信息
send()
8)服务端socket读取信息
9)客户端socket关闭
10)服务端socket关闭
4.socket建立连接的三次握手
第一步,客户端调用connect,向服务器发送SYN J包,connect阻塞
第二步,服务端收到SYN J包,调用accept函数接收请求,向客户端发送 SYN K,ACK J+1
accept函数阻塞
第三步,客户端收到服务器的SYN K,ACK J+1后,connect返回,进行SYN K确认,服务端收到ACK K+1时,accept返回,三次握手完成,连接建立。
5.socket释放连接的四次握手
第一步,客户端关闭连接,发送一个FIN M到服务端
第二步,服务端接收到FIN M后,执行被动关闭,对这个FIN M进行确认ACK M+1。
第三步,当服务端发送完毕后,再发送FIN N到客户端
第四步,客户端接收到FIN N调用close关闭它的socket,发送一个ACK N+1到服务端
服务端接收到这个ACK N+1关闭它的socket
6.为什么建立连接是三次握手,释放连接需要四次
这是因为进行释放连接的第二步时,有可能服务端仍有数据发送给客户端,因此,需要分开,先对客户端FIN报文应答,当服务端数据发送完毕后,再发送FIN报文到客户端。
7.socket连接示例
服务器端
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #define MAXLINE 4096 int main(int argc, char** argv) { int listenfd, connfd; struct sockaddr_in servaddr; char buff[4096]; int n; if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){ printf("create socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(6666); if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){ printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } if( listen(listenfd, 10) == -1){ printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } printf("======waiting for client‘s request======\n"); while(1){ if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){ printf("accept socket error: %s(errno: %d)",strerror(errno),errno); continue; } n = recv(connfd, buff, MAXLINE, 0); buff[n] = ‘\0‘; printf("recv msg from client: %s\n", buff); close(connfd); } close(listenfd); }
客户端
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #define MAXLINE 4096 int main(int argc, char** argv) { int sockfd, n; char recvline[4096], sendline[4096]; struct sockaddr_in servaddr; if( argc != 2){ printf("usage: ./client <ipaddress>\n"); exit(0); } if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ printf("create socket error: %s(errno: %d)\n", strerror(errno),errno); exit(0); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(6666); if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){ printf("inet_pton error for %s\n",argv[1]); exit(0); } if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){ printf("connect error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } printf("send msg to server: \n"); fgets(sendline, 4096, stdin); if( send(sockfd, sendline, strlen(sendline), 0) < 0) { printf("send msg error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } close(sockfd); exit(0); }