【Server】Socket编程原理详解
2014-11-10 15:01
309 查看
目录(?)[-]
代码
原理
实验
这篇文章将对一个简单的socket例程进行剖析。
参考代码如下:
server端:
[cpp] view
plaincopy
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
int main(void)
{
int listenfd = 0,connfd = 0;
struct sockaddr_in serv_addr;
char sendBuff[1025];
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
memset(sendBuff, '0', sizeof(sendBuff));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(55555);
bind(listenfd, (struct sockaddr*)&serv_addr,sizeof(serv_addr));
printf("server start successfully!\n");
if(listen(listenfd, 10) == -1){
printf("Failed to listen\n");
return -1;
}
while(1) {
connfd = accept(listenfd, (struct sockaddr*)NULL ,NULL); // accept awaiting request
strcpy(sendBuff, "[server] welcome client!");
write(connfd, sendBuff, strlen(sendBuff));
close(connfd);
sleep(1);
}
return 0;
}
客户端代码:
[cpp] view
plaincopy
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main(void)
{
int sockfd = 0,n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0) {
printf("\n Error : Could not create socket \n");
return 1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(55555);
serv_addr.sin_addr.s_addr = inet_addr("192.168.1.101");
if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0)
{
printf("\n Error : Connect Failed \n");
return 1;
}
memset(recvBuff, '0' ,sizeof(recvBuff));
while((n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0) {
recvBuff
= 0;
if(fputs(recvBuff, stdout) == EOF) {
printf("\n Error : Fputs error");
}
printf("\n");
}
if( n < 0) {
printf("\n Read Error \n");
}
return 0;
}
[cpp] view
plaincopy
int listen(int sockfd, int backlog);
其中backlog参数表明服务器内核能够同时承受的最大的连接数(这里的例子是10)。继续深入backlog。其实内核维护了两个队列:1)连接未完成队列(incomplete connection queue); 2) 连接完成队列(completed connection queue)。 "连接未完成队列"可以理解为空闲队列,当客户端到来,将占用一个 "连接未完成队列",与服务器进行3次握手成功后,将从“连接未完成队列”移动到“连接完成队列”中。如下图所示:
接下来讨论客户端和服务器端的三次握手连接。
上图描述的情况简述如下:在三次握手之前,服务器端进行了socket,bind和listen函数的调用,在内核中设立一个socket资源,并准备了相关的连接队列,等待客户端连接的到来;之后服务器就阻塞在accept函数上面。当客户端准备好要连接的服务信息(即为struct sockaddr_in结构体指定特定的值),并传给connect来实现和服务器的三次握手,并确立tcp连接(server端accept函数将返回,客户端connect将返回)。
建立tcp连接之后,就是数据的通讯了,在通讯结束后,要关闭连接,由于tcp是全双工的,要关闭读和写,每次操作要确认,所以关闭tcp需要四次数据包的传递。如下图:
1. 工具的准备。Wireshark可以在官网上下载到。
2. 在mac运行,还需要设置一下权限,否则Wireshark无法访问网卡(参考《No Interfaces Available In Wireshark Mac OS
X》)。在控制台运行如下命令:
[cpp] view
plaincopy
sudo chmod 644 /dev/bpf*
3. 配置Wireshark;在Wireshark的工具栏点击
,选择相应的网卡(这里走本地网卡,所以选择lo0),如下图:
4. 由于使用xcode进行调试代码,调试信息也是走本地网络的,所以,我要做一些过滤,以便更好地观察试验结果。具体在工具栏下面的Filter下,填写过滤条件:
[cpp] view
plaincopy
ip.addr==192.168.1.101
这里表明将只显示目标或者源地址中包含192.168.1.101的数据包(调试信息,地址为127.0.0.1,所以可以被过滤掉)。
点击开始按钮
,就可以对数据包进行监听了。
在这个试验中,我在xcode中设置了几个断点,具体如下:
服务器端:
客户端:
这里约定,服务器代码第36行记作s36, 客户端代码第25行记作c25。
确保WireShark启动的状态下,启动服务器端(这时会阻塞在s34,accept函数处),然后启动客户端。
1)当代码运行到c25行时,wireShark并没有纪录到任何信息。
2)当客户端运行到c33时(执行完connect函数后,客户端将阻塞在read函数上),服务器运行到s36。这样服务器和客户端建立了tcp连接,wireShark纪录到了三次握手的信息,如下:
3)当服务器端运行到s39,服务器端就向客户端发送了“[server] welcome client!”字符串,这时候客户端从c33唤醒,运行到c38(经过read的调用,程序从内核中取出数据,并打印,运行过c38后,客户端又被阻塞在c33行,等待服务器的消息)。这个时候,wireShark纪录到数据的传输,如下图:
4)当服务器端运行过s39,执行完close之后,客户端将从c33唤醒,这个时候的返回值为0,跳出循环到c41;wireShark纪录到结束的四次数据包交换,如下图:
ok,tcp的剖析暂时到这里,接下来我将写点select,poll等相关的nonblock的文章。
参考:
《Unix Networking Programming Volume 1, Third Edition》
https://langui.sh/2010/01/31/no-interfaces-available-in-wireshark-mac-os-x/
代码
原理
实验
这篇文章将对一个简单的socket例程进行剖析。
代码
这里提供的例子就是一个简单的TCP client/server程序,client主动连接server,并从服务器中得到一条欢迎消息:“[server] welcome client!”。程序的流程参考如下:参考代码如下:
server端:
[cpp] view
plaincopy
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
int main(void)
{
int listenfd = 0,connfd = 0;
struct sockaddr_in serv_addr;
char sendBuff[1025];
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
memset(sendBuff, '0', sizeof(sendBuff));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(55555);
bind(listenfd, (struct sockaddr*)&serv_addr,sizeof(serv_addr));
printf("server start successfully!\n");
if(listen(listenfd, 10) == -1){
printf("Failed to listen\n");
return -1;
}
while(1) {
connfd = accept(listenfd, (struct sockaddr*)NULL ,NULL); // accept awaiting request
strcpy(sendBuff, "[server] welcome client!");
write(connfd, sendBuff, strlen(sendBuff));
close(connfd);
sleep(1);
}
return 0;
}
客户端代码:
[cpp] view
plaincopy
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main(void)
{
int sockfd = 0,n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0) {
printf("\n Error : Could not create socket \n");
return 1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(55555);
serv_addr.sin_addr.s_addr = inet_addr("192.168.1.101");
if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0)
{
printf("\n Error : Connect Failed \n");
return 1;
}
memset(recvBuff, '0' ,sizeof(recvBuff));
while((n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0) {
recvBuff
= 0;
if(fputs(recvBuff, stdout) == EOF) {
printf("\n Error : Fputs error");
}
printf("\n");
}
if( n < 0) {
printf("\n Read Error \n");
}
return 0;
}
原理
先谈谈服务器端的listen的原理,listen函数的声明如下:[cpp] view
plaincopy
int listen(int sockfd, int backlog);
其中backlog参数表明服务器内核能够同时承受的最大的连接数(这里的例子是10)。继续深入backlog。其实内核维护了两个队列:1)连接未完成队列(incomplete connection queue); 2) 连接完成队列(completed connection queue)。 "连接未完成队列"可以理解为空闲队列,当客户端到来,将占用一个 "连接未完成队列",与服务器进行3次握手成功后,将从“连接未完成队列”移动到“连接完成队列”中。如下图所示:
接下来讨论客户端和服务器端的三次握手连接。
上图描述的情况简述如下:在三次握手之前,服务器端进行了socket,bind和listen函数的调用,在内核中设立一个socket资源,并准备了相关的连接队列,等待客户端连接的到来;之后服务器就阻塞在accept函数上面。当客户端准备好要连接的服务信息(即为struct sockaddr_in结构体指定特定的值),并传给connect来实现和服务器的三次握手,并确立tcp连接(server端accept函数将返回,客户端connect将返回)。
建立tcp连接之后,就是数据的通讯了,在通讯结束后,要关闭连接,由于tcp是全双工的,要关闭读和写,每次操作要确认,所以关闭tcp需要四次数据包的传递。如下图:
实验
原理介绍清楚了,到了进行试验的时候了。这里,我将使用Wireshark工具在mac平台上进行试验,具体如下:1. 工具的准备。Wireshark可以在官网上下载到。
2. 在mac运行,还需要设置一下权限,否则Wireshark无法访问网卡(参考《No Interfaces Available In Wireshark Mac OS
X》)。在控制台运行如下命令:
[cpp] view
plaincopy
sudo chmod 644 /dev/bpf*
3. 配置Wireshark;在Wireshark的工具栏点击
,选择相应的网卡(这里走本地网卡,所以选择lo0),如下图:
4. 由于使用xcode进行调试代码,调试信息也是走本地网络的,所以,我要做一些过滤,以便更好地观察试验结果。具体在工具栏下面的Filter下,填写过滤条件:
[cpp] view
plaincopy
ip.addr==192.168.1.101
这里表明将只显示目标或者源地址中包含192.168.1.101的数据包(调试信息,地址为127.0.0.1,所以可以被过滤掉)。
点击开始按钮
,就可以对数据包进行监听了。
在这个试验中,我在xcode中设置了几个断点,具体如下:
服务器端:
客户端:
这里约定,服务器代码第36行记作s36, 客户端代码第25行记作c25。
确保WireShark启动的状态下,启动服务器端(这时会阻塞在s34,accept函数处),然后启动客户端。
1)当代码运行到c25行时,wireShark并没有纪录到任何信息。
2)当客户端运行到c33时(执行完connect函数后,客户端将阻塞在read函数上),服务器运行到s36。这样服务器和客户端建立了tcp连接,wireShark纪录到了三次握手的信息,如下:
3)当服务器端运行到s39,服务器端就向客户端发送了“[server] welcome client!”字符串,这时候客户端从c33唤醒,运行到c38(经过read的调用,程序从内核中取出数据,并打印,运行过c38后,客户端又被阻塞在c33行,等待服务器的消息)。这个时候,wireShark纪录到数据的传输,如下图:
4)当服务器端运行过s39,执行完close之后,客户端将从c33唤醒,这个时候的返回值为0,跳出循环到c41;wireShark纪录到结束的四次数据包交换,如下图:
ok,tcp的剖析暂时到这里,接下来我将写点select,poll等相关的nonblock的文章。
参考:
《Unix Networking Programming Volume 1, Third Edition》
https://langui.sh/2010/01/31/no-interfaces-available-in-wireshark-mac-os-x/
相关文章推荐
- 【Server】Socket编程原理详解
- Java网络编程精解之ServerSocket用法详解一
- Java网络编程学习笔记(3)ServerSocket用法详解
- Java网络编程精解之ServerSocket用法详解二
- Java网络编程精解笔记3:ServerSocket详解
- Java网络编程精解之ServerSocket用法详解一
- 黑马程序员--10.网络编程--06.【Clinet_Server通信原理】【TCP_客户端和服务端会话】【阻塞式方法的详解】
- Java网络编程精解之ServerSocket用法详解二1
- [转] Java网络编程精解之ServerSocket用法详解一
- Java网络编程精解之ServerSocket用法详解一
- Java网络编程精解之ServerSocket用法详解三
- Java网络编程从入门到精通(28):获取ServerSocket信息的方法及FTP原理 推荐
- Linux Socket 编程原理详解
- NIO编程之ServerSocketChannel用法详解
- java网络编程学习笔记(三):ServerSocket详解
- Java网络编程精解之ServerSocket用法详解一2
- java socket/Serversocket编程详解(中/英文)
- java 网络编程之ServerSocket详解
- Java网络编程精解之ServerSocket用法详解二2
- socket编程原理