最近很多小伙伴都在问ios网络编程(http、socket)和ios网络编程面试这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展HTTP、TCP、UDP、Socket、Androi
最近很多小伙伴都在问ios网络编程(http、socket)和ios网络编程面试这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展 HTTP、TCP、UDP、Socket、Android 网络编程之 TCP/IP 的 Socket、ServerSocket 模式、cocos2d-x网络编程三(SocketIO)、HTTP、HTTPS、WebSocket等相关知识,下面开始了哦!
本文目录一览:- ios网络编程(http、socket)(ios网络编程面试)
- HTTP、TCP、UDP、Socket
- Android 网络编程之 TCP/IP 的 Socket、ServerSocket 模式
- cocos2d-x网络编程三(SocketIO)
- HTTP、HTTPS、WebSocket
ios网络编程(http、socket)(ios网络编程面试)
http编程综述:亦可称为soap编程。通常情况下,http编程要比socket编程相对要简单易用得多。所以用的最广广泛。
==》post方法相对要复杂一些。首先post方法要设置key和value ,所有的key和value都会拼接成 key1=value1&key2=value2的样式的字符串,然后这个字符串转化为二进制放到 http请求的body中。当请求发送的时候,也就跟随body一起传给服务器。http请求Content-Type设置为:application/x-www-form-urlencoded。这里讲的只是简单的post请求,一般发送文件不会选择这种方式(从技术方面考虑也可以发送文件,就是把文件以 key 和 value的方式放入)。下面我们再讨论一下post发送二进制文件更加普遍的方法。
简单来说,就是一个基于应用层的通信规范:双方要进行通信,大家都要遵守一个规范,这个规范就是HTTP协议。
HTTP协议能做什么?
很多人首先一定会想到:浏览网页。没错,浏览网页是HTTP的主要应用,但是这并不代表HTTP就只能应用于网页的浏览。HTTP是一种协议,只要通信的双方都遵守这个协议,HTTP就能有用武之地。比如咱们常用的QQ,迅雷这些软件,都会使用HTTP协议(还包括其他的协议)。
HTTP协议如何工作?
大家都知道一般的通信流程:首先客户端发送一个请求(request)给服务器,服务器在接收到这个请求后将生成一个响应(response)返回给客户端。
在这个通信的过程中HTTP协议在以下4个方面做了规定:
2. 建立连接的方式(1、非持久连接 2、持久连接)
3. 缓存的机制
4. 响应授权激发机制
5. 基于HTTP的应用(1、 HTTP代理 2、多线程下载 3、 HTTPS传输协议原理 4、开发web程序时常用的Request Methods 5、用户与服务器的交互)
2:发起NSConnection请求
3:处理返回xml数据包(NSXMLParser解析xml文件),或者返回文件png、pdf之类
4:若返回数据包中含有待下载的图片下载地址。则重复2、3步骤下载下来图片。只不过3中返回的是文件。
socket编程综述:
http网络编程实例
一:确认网络环境3G/WIFI开发Web等网络应用程序的时候,需要确认网络环境,连接情况等信息。如果没有处理它们,是不会通过Apple的审查的。
Apple 的 例程 Reachability 中介绍了取得/检测网络状态的方法。要在应用程序程序中使用Reachability,首先要完成如下两部:
1.1. 添加源文件:
在你的程序中使用 Reachability 只须将该例程中的 Reachability.h 和 Reachability.m 拷贝到你的工程中。如下图:
1.2.添加framework:
将SystemConfiguration.framework 添加进工程。如下图:
2. 网络状态
Reachability.h中定义了三种网络状态:
typedef enum {
NotReachable = 0, //无连接
ReachableViaWiFi, //使用3G/GPRS网络
ReachableViaWWAN //使用WiFi网络
} NetworkStatus;
Reachability *r = [Reachability reachabilityWithHostName:@“www.apple.com”];
switch ([r currentReachabilityStatus]) {
case NotReachable:
// 没有网络连接
break;
case ReachableViaWWAN:
// 使用3G网络
break;
case ReachableViaWiFi:
// 使用WiFi网络
break;
}
3.检查当前网络环境
程序启动时,如果想检测可用的网络环境,可以像这样
// 是否wifi
+ (BOOL) IsEnableWIFI {
return ([[Reachability reachabilityForLocalWiFi] currentReachabilityStatus] != NotReachable);
}
// 是否3G
+ (BOOL) IsEnable3G {
return ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] != NotReachable);
}
例子:
- (void)viewWillAppear:(BOOL)animated {
if (([Reachability reachabilityForInternetConnection].currentReachabilityStatus == NotReachable) &&
([Reachability reachabilityForLocalWiFi].currentReachabilityStatus == NotReachable)) {
self.navigationItem.hidesBackButton = YES;
[self.navigationItem setLeftBarButtonItem:nil animated:NO];
}
}
4. 链接状态的实时通知
网络连接状态的实时检查,通知在网络应用中也是十分必要的。接续状态发生变化时,需要及时地通知用户:
Reachability 1.5版本
// My.AppDelegate.h
#import "Reachability.h"
@interface MyAppDelegate : NSObject <UIApplicationDelegate> {
NetworkStatus remoteHostStatus;
}
@end
// My.AppDelegate.m
#import "MyAppDelegate.h"
@implementation MyAppDelegate
@synthesize remoteHostStatus;
// 更新网络状态
- (void)updateStatus {
self.remoteHostStatus = [[Reachability sharedReachability] remoteHostStatus];
}
// 通知网络状态
- (void)reachabilityChanged:(NSNotification *)note {
[self updateStatus];
if (self.remoteHostStatus == NotReachable) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"AppName",nil)
message:NSLocalizedString (@"NotReachable",nil)
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// 设置网络检测的站点
[[Reachability sharedReachability] setHostName:@"www.apple.com"];
[[Reachability sharedReachability] setNetworkStatusNotificati*****Enabled:YES];
// 设置网络状态变化时的通知函数
[[NSNotificationCenter defaultCenter] addobserver:self selector:@selector(reachabilityChanged:)
name:@"kNetworkReachabilityChangednotification" object:nil];
[self updateStatus];
}
- (void)dealloc {
// 删除通知对象
[[NSNotificationCenter defaultCenter] removeObserver:self];
[window release];
[super dealloc];
}
Reachability 2.0版本
// MyAppDelegate.h
@class Reachability;
Reachability *hostReach;
}
@end
// MyAppDelegate.m
- (void)reachabilityChanged:(NSNotification *)note {
Reachability* curReach = [note object];
NSParameterassert([curReach isKindOfClass: [Reachability class]]);
NetworkStatus status = [curReach currentReachabilityStatus];
if (status == NotReachable) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"AppName""
message:@"NotReachable"
delegate:nil
cancelButtonTitle:@"YES" otherButtonTitles:nil];
[alert show];
[alert release];
}
}
// ...
// 监测网络情况
[[NSNotificationCenter defaultCenter] addobserver:self
selector:@selector(reachabilityChanged:)
name: kReachabilityChangednotification
object: nil];
hostReach = [[Reachability reachabilityWithHostName:@"www.google.com"] retain];
hostReach startNotifer];
// ...
}
二:使用NSConnection下载数据
1.创建NSConnection对象,设置委托对象
NSMutableuRLRequest *request = [NSMutableuRLRequest requestWithURL:[NSURL URLWithString:[self urlString]]];
[NSURLConnection connectionWithRequest:request delegate:self];
2. NSURLConnection delegate委托方法
- (void)connection:(NSURLConnection *)connection didReceiveResP*****e:(NSURLResP*****e *)resP*****e;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
3. 实现委托方法
- (void)connection:(NSURLConnection *)connection didReceiveResP*****e:(NSURLResP*****e *)resP*****e {
// store data
[self.receivedData setLength:0]; //通常在这里先清空接受数据的缓存
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
/* appends the new data to the received data */
[self.receivedData appendData:data]; //可能多次收到数据,把新的数据添加在现有数据最后
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// 错误处理
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// disconnect
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
Nsstring *returnString = [[Nsstring alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
NSLog(returnString);
[self urlLoaded:[self urlString] data:self.receivedData];
firstTimeDownloaded = YES;
}
三:使用NSXMLParser解析xml文件
1. 设置委托对象,开始解析
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; //或者也可以使用initWithContentsOfURL直接下载文件,但是有一个原因不这么做:
// It's also possible to have NSXMLParser download the data,by passing it a URL,but this is not desirable
// because it gives less control over the network,particularly in responding to connection errors.
[parser setDelegate:self];
[parser parse];
2. 常用的委托方法
- (void)parser:(NSXMLParser *)parser didStartElement:(Nsstring *)elementName
namespaceURI:(Nsstring *)namespaceURI
qualifiedname:(Nsstring *)qName
attributes:(NSDictionary *)attributeDict;
- (void)parser:(NSXMLParser *)parser didEndElement:(Nsstring *)elementName
namespaceURI:(Nsstring *)namespaceURI
qualifiedname:(Nsstring *)qName;
- (void)parser:(NSXMLParser *)parser foundCharacters:(Nsstring *)string;
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError;
static Nsstring *FeedURLString = @"http://www.yifeiyang.net/test/test.xml";
3. 应用举例
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
NSError *parseError = [parser parserError];
if (parseError && error) {
*error = parseError;
}
[parser release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(Nsstring *)elementName namespaceURI:(Nsstring *)namespaceURI
qualifiedname:(Nsstring*)qName attributes:(NSDictionary *)attributeDict{
// 元素开始句柄
if (qName) {
elementName = qName;
}
// 输出属性值
NSLog(@"Name is %@,Age is %@",[attributeDict objectForKey:@"name"],[attributeDict objectForKey:@"age"]);
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(Nsstring *)elementName namespaceURI:(Nsstring *)namespaceURI
qualifiedname:(Nsstring *)qName
{
// 元素终了句柄
if (qName) {
elementName = qName;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(Nsstring *)string
{
// 取得元素的text
}
NSError *parseError = nil;
[self parseXMLFileAtURL:[NSURL URLWithString:FeedURLString] parseError:&parseError];
HTTP、TCP、UDP、Socket
先来一个讲TCP、UDP和HTTP关系的
1、TCP/IP是个协议组,可分层次:网络层、传输层和应用层。 在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。 在传输层中有TCP协议与UDP协议。 在应用层有FTP、HTTP、TELNET、SMTP、DNS等协议。 因此,HTTP本身就是一个协议,是从Web服务器传输超文本到本地浏览器的传送协议。
2、HTTP协议是建立在请求/响应模型上的。首先由客户建立一条与服务器的TCP链接,并发送一个请求到服务器,请求中包含请求方法、URI、协议版本以及相关的MIME样式的消息。服务器响应一个状态行,包含消息的协议版本、一个成功和失败码以及相关的MIME式样的消息。 HTTP/1.0为每一次HTTP的请求/响应建立一条新的TCP链接,因此一个包含HTML内容和图片的页面将需要建立多次的短期的TCP链接。一次TCP链接的建立将需要3次握手。 另外,为了获得适当的传输速度,则需要TCP花费额外的回路链接时间(RTT)。每一次链接的建立需要这种经常性的开销,而其并不带有实际有用的数据,只是保证链接的可靠性,因此HTTP/1.1提出了可持续链接的实现方法。HTTP/1.1将只建立一次TCP的链接而重复地使用它传输一系列的请求/响应消息,因此减少了链接建立的次数和经常性的链接开销。
3、结论:虽然HTTP本身是一个协议,但其最终还是基于TCP的。不过,目前,有人正在研究基于TCP+UDP混合的HTTP协议。 Socket是什么呢? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Android 网络编程之 TCP/IP 的 Socket、ServerSocket 模式
TCP/IP 是一种协议,一种面向连接的、可靠的协议。Socket 仅仅是针对 TCP、UDP 网络接口来封装,不涉及上层协议。TCP 与 UDP 的传输特性不一样,适用于不同类型的应用层协议,TCP 有连接,延时较长,能保证服务质量。UDP 无连接,应用程序需要进行数据分包、延时短,效率高,数据包可能丢失或达到对端发生数据混乱。在 Socket 之上可以实现 RFC 标准的应用层协议,也可以自定义实现私有的应用协议。
在 java 编程中经常使用 java.net 和 javax.net 包来开发 socket 程序,下面列出所有常用的类。
Java.net
Java.net.Socket 客户端连接使用的 TCP Socket
Java.net.DatagramSocket 客户端和服务器共同使用的 UDP Socket
Java.net.ServerSocket 服务端 TCP Socket 监听接口
Java.net.InetAddress IP 地址封装类
Java.net.DataGramPacket 通过 DatagramSocket 首发数据包的封装类,包括数据和对端的 IP 地址、UDP 端口
Javax.net
Javax.net.SocketFactory 客户端连接使用的 TCP Socket
Javax.net.ServerSocketFactory 服务端 TCP Socket 监听端口
Javax.net.ssl.SSLSocketFactory SSL 客户端 Socket 构造器
Javax.net.ssl.SSLServerFactory SSL 服务监听 Socket 构造器
cocos2d-x网络编程三(SocketIO)
记得提前设置 好服务器环境
TestSocketIoScene.h文件:
#ifndef __TestSocketIo_SCENE_H__ #define __TestSocketIo_SCENE_H__ #include "cocos2d.h" #include "network\SocketIO.h" USING_NS_CC; using namespace cocos2d::network; //继承SocketIO::SIODelegate和实现四个虚函数 class TestSocketIo : public cocos2d::Layer,SocketIO::SIODelegate { public: // there's no 'id' in cpp,so we recommend returning the class instance pointer static cocos2d::Scene* createScene(); // Here's a difference. Method 'init' in cocos2d-x returns bool,instead of returning 'id' in cocos2d-iphone virtual bool init(); //socket连接时调用 void onConnect(SIOClient* client); //收到数据时调用 void onMessage(SIOClient* client,const std::string& data); //连接错误或接收到错误信号时调用 void onError(SIOClient* client,const std::string& data); //socket关闭时调用 void onClose(SIOClient* client); // implement the "static create()" method manually CREATE_FUNC(TestSocketIo); SIOClient *client; }; #endif // __TestSocketIo_SCENE_H__
TestSocketIoScene.cpp文件:
#include "TestSocketIoScene.h" Scene* TestSocketIo::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = TestSocketIo::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool TestSocketIo::init() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } Size size = Director::getInstance()->getWinSize(); client = nullptr;//初始化为空指针 auto menu = Menu::create(); menu->setPosition(Vec2::ZERO); addChild(menu); auto lblInit = Label::create("init socket","Arial",22); auto menuInit = MenuItemLabel::create(lblInit,[=](Ref *sender){ //1.connect方法创建实例 client = SocketIO::connect("ws://192.168.1.102:3000",*this); client->setTag("init socket"); //4.初始化的时候设置一个监听器:使用on监听事件和获取接收到的数据 client->on("loginresult",[=](SIOClient *client,const std::string &data){//使用C++匿名函数实现 log("login result is :%s",data.c_str()); }); }); menuInit->setPosition(size/2); menu->addChild(menuInit); auto lblSend = Label::create("send message",22); auto menuSend = MenuItemLabel::create(lblSend,[=](Ref *sender){ //2.send方法发送数据 client->send("hello socket.io"); }); menuSend->setPosition(size.width/2,size.height/2-50); menu->addChild(menuSend); auto lblSendEvent = Label::create("emit event",22); auto menuSendEvent = MenuItemLabel::create(lblSendEvent,[=](Ref *sender){ //3.向服务器发送login事件,并把名字和密码传给服务器 client->emit("login","[{\"name\":\"myname\",\"pwd\":\"mypwd\"}]"); }); menuSendEvent->setPosition(size.width/2,size.height/2-100); menu->addChild(menuSendEvent); return true; } void TestSocketIo::onConnect(SIOClient* client){ log("onConnect"); log("%s connect",client->getTag()); } void TestSocketIo::onMessage(SIOClient* client,const std::string& data){ log("onMessage"); log("%s received content is:%s",client->getTag(),data.c_str()); } void TestSocketIo::onClose(SIOClient * client){ log("onClose"); log("%s is closed",client->getTag()); } void TestSocketIo::onError(SIOClient* client,const std::string& data){ log("onError"); log("%s error is:%s",data.c_str()); }
HTTP、HTTPS、WebSocket
一 、HTTP
1.1 HTTP发展史
1.1.1 什么是HTTP
超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法
1.1.2 发展历史
1.2 HTTP特点
- 无状态:协议对客户端没有状态存储,对事物处理没有“记忆”能力,比如访问一个网站需要反复进行登录操作
- 无连接:HTTP/1.1之前,由于无状态特点,每次请求需要通过TCP三次握手四次挥手,和服务器重新建立连接。比如某个客户机在短时间多次请求同一个资源,服务器并不能区别是否已经响应过用户的请求,所以每次需要重新响应请求,需要耗费不必要的时间和流量。
- 基于请求和响应:基本的特性,由客户端发起请求,服务端响应简单快速、灵活
- 通信使用明文、请求和响应不会对通信方进行确认、无法保护数据的完整性
1.3 HTTP消息结构
一个HTTP请求报文由请求行(request line)、请求头(header)、空行和请求数据4个部分组成,下图给出了请求报文的一般格式
1.3.1 请求行
请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET/index.htmlHTTP/1.1
1.3.2 请求头
请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有常见的请求头属性
- **Accept :**指定客户端能够接收的内容类型 | Accept: text/plain, text/html
- Cache-Control: 指定请求和响应遵循的缓存机制 | Cache-Control: no-cache
- **Cookie :**HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器 | Cookie:$Version=1; Skin=new;
- **Content-Type:**请求的与实体对应的MIME信息 | Content-Type: application/x-www-form-urlencoded
- Host: 指定请求的服务器的域名和端口号
- Referer: 先前网页的地址,当前请求网页紧随其后,即来路
- User-Agent : 发起请求的用户的身份标识
1.3.3 空行
最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。
1.4 HTTP响应
HTTP状态码的英文为HTTP Status Code。状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。
状态响应头
- 1xx:指示信息–表示请求已接收,继续处理。
- 2xx:成功–表示请求已被成功接收、理解、接受。
- 3xx:重定向–要完成请求必须进行更进一步的操作。
- 4xx:客户端错误–请求有语法错误或请求无法实现。
- 5xx:服务器端错误–服务器未能实现合法的请求。
常见的状态码
- 200 OK**:**客户端请求成功,一般用于GET和POST请求
- 400 Bad Request**:**客户端请求有语法错误,不能被服务器所理解。
- **301 Moved Permanently:**永久移动,请求的资源已被永久移动到新url,返回信息会包含新的url,浏览器会自动定向到新url
- 401 Unauthorized**:**请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用。
- 403 Forbidden**:**服务器收到请求,但是拒绝提供服务。
- 404 Not Found**:**请求资源不存在,举个例子:输入了错误的URL。
- 500 Internal Server Error**:**服务器发生不可预期的错误。
- 502 Bad Gateway: 充当网关或代理的服务器,从远端接收到一个无效的请求
- 503 Server Unavailable**:**服务器当前不能处理客户端的请求,一段时间后可能恢复正常,举个例子 : HTTP/1.1 200 OK(CRLF)
1.5 关于HTTP请求GET和POST的区别
1.5.1 两种提交方式
- GET提交 : 请求的数据会附在URL之后(就是把数据放置在HTTP协议头<request-line>中),以?分割URL和传 输数据,多个参数用&连接;例如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0 %E5%A5%BD。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接 把字符串用BASE64加密,得出如: %E4%BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的 ASCII。
- POST提交:把提交的数据放置在是HTTP包的包体<request-body>中。
1.5.2 传输数据的大小
首先声明,HTTP协议没有对传输的数据大小进行限制,HTTP协议规范也没有对URL长度进行限制。 而在实际开发中存在的限制主要有:
- **GET : **特定浏览器和服务器对URL长度有限制,例如IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器,如Netscape、FireFox等,理论上没有长度限制,其限制取决于操作系统的支持。因此对于GET提交时,传输数据就会受到URL长度的限制
- **POST : ** 由于不是通过URL传值,理论上数据不受限。但实际各个WEB服务器会规定对post提交数据大小进行限制,Apache、IIS6都有各自的配置。
1.5.3 安全性
POST的安全性要比GET的安全性高。
注意:这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改,而这里安全的含义是真正的Security的含义,比如:通过GET提交数据,用户名和密码将明文出现在URL上,因为(1)登录页面有可能被浏览器缓存, (2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了
二 、实现原理
HTTPS是一种通过计算机网络进行安全通信的传输协议,经由HTTP进行通信,利用SSL/TLS建立全信道,加密数据包。
HTTPS使用的主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。
PS:TLS是传输层加密协议,前身是SSL协议,由网景公司1995年发布,有时候两者不区分
2.1 对称秘钥加密
客户端自己封装一种加密算法,将给服务端发送的数据进行加密,并且将数据加密的方式即秘钥发送给密文,服务端收到秘钥和数据,用秘钥进行解密
2.2 非对称秘钥加密
服务器端为客户端统一创建一个加密方式,由服务器端指定创建,称为公钥,服务器端所创建的加密方式统一的分发给即将要进行服务器连接的客户端,客户端只需要将密文发送给服务端,服务端通过公钥加密,但是的建立两次连接,先传送公钥,也存在隐患,模拟某些服务器的公钥,发送的数据被第三方截取。
2.3 证书秘钥加密
用到三方机构,数字证书,服务器端和客户端足以信任的机构,服务器端将公钥发送给三方机构,在三方机构 做一个防伪标识,数字签名,公钥携带三方机构的证书发送给客户端,客户端使用公钥对数据进行加密。
三 、http通信原理
客户端输入URL回车,DNS解析域名得到服务器的IP地址,服务器在80端口监听客户端请求,端口通过TCP/IP协议(可以通过Socket实现)建立连接。HTTP属于TCP/IP模型中的运用层协议,所以通信的过程其实是对应数据的入栈和出栈
报文从运用层传送到运输层,运输层通过TCP三次握手和服务器建立连接,四次挥手释放连接。
四 、WebSocket
4.1 WebSocket与HTTP
- WebSocket 的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是全双工通信。
- TTP 有 1.1 和 1.0 之说,也就是所谓的 keep-alive,把多个 HTTP 请求合并为一个,但是 Websocket 其实是一个新协议,跟 HTTP 协议基本没有关系,只是为了兼容现有浏览器,所以在握手阶段使用了HTTP
4.2 WebSocket - 握手
WebSocket是基于HTTP协议的,或者说借用了HTTP协议来完成一部分握手
GET /chatsocket HTTP/1.1
Host: 127.0.0.1:8002
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13 # 版本
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
-
Sec-WebSocket-Key是一个Base64 encode的值,这个是浏览器随机生成的,发送给服务器
-
服务端从请求(HTTP的请求头)信息中提取Sec-WebSocket-Key,利用magic_string和Sec-WebSocket-Key进行
hmac1
加密,再进行base64
加密 -
将加密结果响应给客户端,服务器会返回下列东西,表示已经接受到请求, 成功建立
WebSocket
headers = get_headers(data) # 提取请求头信息
# 对请求头中的sec-websocket-key进行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n"
value = headers[''Sec-WebSocket-Key''] + magic_string
print(value)
ac = base64.b64encode(hashlib.sha1(value.encode(''utf-8'')).digest())
response_str = response_tpl % (ac.decode(''utf-8''))
# 响应【握手】信息
conn.send(response_str.encode("utf8"))
-
依然是固定的,通过Upgrade告诉客户端即将升级的是WebSocket协议,而不是mozillasocket,urnarsocket** 或者shitsocket。
-
然后,Sec-WebSocket-Accept这个则是经过服务器确认,并且加密过后的Sec-WebSocket-Key。服务器:好啦好啦,知道啦,给你看我的ID CARD来证明行了吧。
-
Sec-WebSocket-Protocol则是表示最终使用的协议。
4.3 WebSocket - 解密
hashstr = b''\x81\x83\xceH\xb6\x85\xffz\x85''
# b''\x81 \x83 \xceH\xb6\x85\xffz\x85''
# 将第二个字节也就是 \x83 第9-16位 进行与127进行位运算
payload = hashstr[1] & 127
print(payload)
if payload == 127:
extend_payload_len = hashstr[2:10]
mask = hashstr[10:14]
decoded = hashstr[14:]
# 当位运算结果等于127时,则第3-10个字节为数据长度
# 第11-14字节为mask 解密所需字符串
# 则数据为第15字节至结尾
if payload == 126:
extend_payload_len = hashstr[2:4]
mask = hashstr[4:8]
decoded = hashstr[8:]
# 当位运算结果等于126时,则第3-4个字节为数据长度
# 第5-8字节为mask 解密所需字符串
# 则数据为第9字节至结尾
if payload <= 125:
extend_payload_len = None
mask = hashstr[2:6]
decoded = hashstr[6:]
# 当位运算结果小于等于125时,则这个数字就是数据的长度
# 第3-6字节为mask 解密所需字符串
# 则数据为第7字节至结尾
str_byte = bytearray()
for i in range(len(decoded)):
byte = decoded[i] ^ mask[i % 4]
str_byte.append(byte)
print(str_byte.decode("utf8"))
4.4 WebSocket - 加密
import struct
msg_bytes = "hello".encode("utf8")
token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length == 126:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
print(msg)
五 、WebSocket - 作用
5.1 ajax轮询和long poll的原理。
-
ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息
-
long poll 其实原理跟 ajax轮询差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起请求后,如果没消息,就一直不返回 Response 给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
5.2 WebSocket
-
这两种方式都不是最好的方式,需要很多资源,一种需要更快的速度,一种需要更多的’电话。这两种都会导致’电话’的需求越来越高。
-
忘记说了 HTTP 还是一个无状态协议。通俗的说就是,服务器因为每天要接待太多客户了,是个健忘鬼,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。
-
只需要经过一次 HTTP 请求,就可以做到源源不断的信息传送了,当服务器完成协议升级后(HTTP --—> Websocket),服务端就可以主动推送信息给客户端啦。
原文出处:https://www.cnblogs.com/Dr-wei/p/11752698.html
今天关于ios网络编程(http、socket)和ios网络编程面试的介绍到此结束,谢谢您的阅读,有关 HTTP、TCP、UDP、Socket、Android 网络编程之 TCP/IP 的 Socket、ServerSocket 模式、cocos2d-x网络编程三(SocketIO)、HTTP、HTTPS、WebSocket等更多相关知识的信息可以在本站进行查询。
本文标签: