你好,歡迎來到IOS教程網

 Ios教程網 >> IOS訊息 >> 關於IOS >> ios學習socket編程——HTTP1.0服務器端

ios學習socket編程——HTTP1.0服務器端

編輯:關於IOS

介紹一下如何使用OC搭建socket的服務器端。雖然這並不是一個好的解決方案,通常我們使用Java或者PHP抑或NodeJS來搭建服務器後台。但是還是有必要了解一下OC的做法,順便加深對Socket編程的理解。
廢話不多說,還是導入AsyncSocket.h和AsyncSocket.m文件。這裡我們創建的是一個桌面端的軟件(iOS端也可。),創建了一個AppController類。
下面直接上代碼,有點長,會慢慢解釋。原來的程序是有圖形界面的,不過為了更好地解釋socket的工作原理,這裡就把不必要的

<code class="hljs objectivec has-numbering"><span class="hljs-comment">//AppController.h</span>
<span class="hljs-preprocessor">#import <span class="hljs-title">&lt;Cocoa/Cocoa.h&gt;</span></span>
<span class="hljs-preprocessor">#import <span class="hljs-title">"AsyncSocket.h"</span></span>
<span class="hljs-class"><span class="hljs-keyword">@interface</span> <span class="hljs-title">AppController</span> : <span class="hljs-title">NSObject</span>&lt;<span class="hljs-title">AsyncSocketDelegate</span>&gt;</span>
{
    AsyncSocket *listenSocket;
    <span class="hljs-built_in">NSMutableArray</span> *connectedSockets;

    <span class="hljs-built_in">BOOL</span> isRunning;
    <span class="hljs-keyword">IBOutlet</span> <span class="hljs-keyword">id</span> logView;
    <span class="hljs-keyword">IBOutlet</span> <span class="hljs-keyword">id</span> portField;
    <span class="hljs-keyword">IBOutlet</span> <span class="hljs-keyword">id</span> startStopButton;
}

<span class="hljs-keyword">@property</span> (atomic,<span class="hljs-keyword">strong</span>) <span class="hljs-built_in">NSString</span> *fileName;
<span class="hljs-keyword">@property</span> (atomic,<span class="hljs-keyword">strong</span>) NSMutableData *receiveData;
- (<span class="hljs-keyword">IBAction</span>)startStop:(<span class="hljs-keyword">id</span>)sender;
<span class="hljs-keyword">@end</span></code>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

AppController.h文件非常簡單,有一個AsyncSocket類的socket對象,還有一個用來存放已連接的socket的數組。isRunning表示是否在運行,三個IBOutlet是界面需要的。fileName用於讀取http請求的文件名,還有一個動作事件,在啟動/結束鍵被按下時觸發。

<code class="hljs objectivec has-numbering"><span class="hljs-preprocessor">#import <span class="hljs-title">"AppController.h"</span></span>
<span class="hljs-preprocessor">#import <span class="hljs-title">"AsyncSocket.h"</span></span>
<span class="hljs-preprocessor">#import <span class="hljs-title">&lt;AppKit/AppKit.h&gt;</span></span>

<span class="hljs-preprocessor">#define WELCOME_MSG  0</span>
<span class="hljs-preprocessor">#define ECHO_MSG     1</span>
<span class="hljs-preprocessor">#define WARNING_MSG  2</span>

<span class="hljs-preprocessor">#define READ_TIMEOUT 15.0</span>
<span class="hljs-preprocessor">#define READ_TIMEOUT_EXTENSION 10.0</span>

<span class="hljs-preprocessor">#define FORMAT(format, ...) [NSString stringWithFormat:(format), ##__VA_ARGS__]</span>

<span class="hljs-class"><span class="hljs-keyword">@interface</span> <span class="hljs-title">AppController</span> (<span class="hljs-title">PrivateAPI</span>)</span>
<span class="hljs-keyword">@end</span>


<span class="hljs-class"><span class="hljs-keyword">@implementation</span> <span class="hljs-title">AppController</span></span>
<span class="hljs-keyword">@synthesize</span> fileName;
- (<span class="hljs-keyword">id</span>)init
{
    <span class="hljs-keyword">if</span>((<span class="hljs-keyword">self</span> = [<span class="hljs-keyword">super</span> init]))
    {
        listenSocket = [[AsyncSocket alloc] initWithDelegate:<span class="hljs-keyword">self</span>];
        connectedSockets = [[<span class="hljs-built_in">NSMutableArray</span> alloc] initWithCapacity:<span class="hljs-number">1</span>];

        isRunning = <span class="hljs-literal">NO</span>;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>;
}

- (<span class="hljs-keyword">void</span>)awakeFromNib
{
    [logView setString:@<span class="hljs-string">""</span>];
}

- (<span class="hljs-keyword">void</span>)applicationDidFinishLaunching:(<span class="hljs-built_in">NSNotification</span> *)aNotification
{
    [listenSocket setRunLoopModes:[<span class="hljs-built_in">NSArray</span> arrayWithObject:NSRunLoopCommonModes]];
}

- (<span class="hljs-keyword">IBAction</span>)startStop:(<span class="hljs-keyword">id</span>)sender
{
    <span class="hljs-keyword">if</span>(!isRunning)
    {
        <span class="hljs-keyword">int</span> port = [portField intValue];
        <span class="hljs-keyword">if</span>(port &lt; <span class="hljs-number">0</span> || port &gt; <span class="hljs-number">65535</span>)
        {
            port = <span class="hljs-number">0</span>;
        }

        <span class="hljs-built_in">NSError</span> *error = <span class="hljs-literal">nil</span>;
        <span class="hljs-keyword">if</span>(![listenSocket acceptOnPort:port error:&amp;error])
        {
            <span class="hljs-keyword">return</span>;
        }
        isRunning = <span class="hljs-literal">YES</span>;

        [portField setEnabled:<span class="hljs-literal">NO</span>];
        [startStopButton setTitle:@<span class="hljs-string">"Stop"</span>];
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-comment">// Stop accepting connections</span>
        [listenSocket disconnect];

        <span class="hljs-comment">// Stop any client connections</span>
        NSUInteger i;
        <span class="hljs-keyword">for</span>(i = <span class="hljs-number">0</span>; i &lt; [connectedSockets count]; i++)
        {
            [[connectedSockets objectAtIndex:i] disconnect];
        }
        isRunning = <span class="hljs-literal">false</span>;
        [portField setEnabled:<span class="hljs-literal">YES</span>];
        [startStopButton setTitle:@<span class="hljs-string">"Start"</span>];
    }
}

- (<span class="hljs-keyword">void</span>)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
    [connectedSockets addObject:newSocket];
}

- (<span class="hljs-keyword">void</span>)onSocket:(AsyncSocket *)sock didConnectToHost:(<span class="hljs-built_in">NSString</span> *)host port:(UInt16)port
{
    [sock readDataWithTimeout:-<span class="hljs-number">1</span> tag:<span class="hljs-number">0</span>];<span class="hljs-comment">//very important</span>
    <span class="hljs-built_in">NSString</span> *welcomeMsg = @<span class="hljs-string">"Welcome to the AsyncSocket Echo Serverrn"</span>;
    NSData *welcomeData = [welcomeMsg dataUsingEncoding:NSUTF8StringEncoding];
    [sock readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:<span class="hljs-number">0</span>];
}

- (<span class="hljs-keyword">void</span>)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(<span class="hljs-keyword">long</span>)tag
{
    <span class="hljs-keyword">if</span>(tag == ECHO_MSG)
    {
        [sock readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:<span class="hljs-number">0</span>];
    }
}

- (<span class="hljs-keyword">void</span>)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(<span class="hljs-keyword">long</span>)tag
{
    <span class="hljs-built_in">NSString</span> *msg = [[<span class="hljs-built_in">NSString</span> alloc] initWithData:strData encoding:NSUTF8StringEncoding];
    <span class="hljs-built_in">NSRange</span> endRange = [msg rangeOfString:@<span class="hljs-string">"HTTP"</span>];
    <span class="hljs-built_in">NSRange</span> beginRange = [msg rangeOfString:@<span class="hljs-string">"/"</span>];

    <span class="hljs-keyword">if</span> (beginRange<span class="hljs-variable">.location</span> != <span class="hljs-built_in">NSNotFound</span> &amp;&amp; endRange<span class="hljs-variable">.location</span> != <span class="hljs-built_in">NSNotFound</span>) {
        <span class="hljs-built_in">NSRange</span> fileRange = NSMakeRange(beginRange<span class="hljs-variable">.location</span>, endRange<span class="hljs-variable">.location</span> - beginRange<span class="hljs-variable">.location</span>-<span class="hljs-number">1</span>);
        fileName = [msg substringWithRange:fileRange];
        <span class="hljs-keyword">if</span>(fileName &amp;&amp; ![fileName isEqualToString:@<span class="hljs-string">""</span>]){
            NSData *returnData = [<span class="hljs-keyword">self</span> getResponseHeader:fileName];
            [sock writeData:returnData withTimeout:-<span class="hljs-number">1</span> tag:ECHO_MSG];
            fileName = <span class="hljs-literal">nil</span>;
            [sock disconnectAfterWriting];
        }
        <span class="hljs-keyword">else</span>{ 
            [sock writeData:data withTimeout:-<span class="hljs-number">1</span> tag:ECHO_MSG];
            fileName = <span class="hljs-literal">nil</span>;
            [sock disconnectAfterWriting];
        }
    }
    <span class="hljs-keyword">else</span>{
        <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"Header not found"</span>);
        <span class="hljs-built_in">dispatch_async</span>(dispatch_get_main_queue(), ^{
            [sock writeData:data withTimeout:-<span class="hljs-number">1</span> tag:ECHO_MSG];
            fileName = <span class="hljs-literal">nil</span>;
            [sock disconnectAfterWriting];
        });
    }
}

<span class="hljs-comment">/**
 * This method is called if a read has timed out.
 * It allows us to optionally extend the timeout.
 * We use this method to issue a warning to the user prior to disconnecting them.
**/</span>
- (<span class="hljs-built_in">NSTimeInterval</span>)onSocket:(AsyncSocket *)sock
  shouldTimeoutReadWithTag:(<span class="hljs-keyword">long</span>)tag
                   elapsed:(<span class="hljs-built_in">NSTimeInterval</span>)elapsed
                 bytesDone:(NSUInteger)length
{
    <span class="hljs-keyword">if</span>(elapsed &lt;= READ_TIMEOUT)
    {
        <span class="hljs-built_in">NSString</span> *warningMsg = @<span class="hljs-string">"Are you still there?rn"</span>;
        NSData *warningData = [warningMsg dataUsingEncoding:NSUTF8StringEncoding];

        [sock writeData:warningData withTimeout:-<span class="hljs-number">1</span> tag:WARNING_MSG];

        <span class="hljs-keyword">return</span> READ_TIMEOUT_EXTENSION;
    }

    <span class="hljs-keyword">return</span> <span class="hljs-number">0.0</span>;
}

- (<span class="hljs-keyword">void</span>)onSocket:(AsyncSocket *)sock willDisconnectWithError:(<span class="hljs-built_in">NSError</span> *)err
{
    [<span class="hljs-keyword">self</span> logInfo:FORMAT(@<span class="hljs-string">"Client Disconnected: %@:%hu"</span>, [sock connectedHost], [sock connectedPort])];
}

- (<span class="hljs-keyword">void</span>)onSocketDidDisconnect:(AsyncSocket *)sock
{
    [connectedSockets removeObject:sock];
}

- (NSData *)getResponseHeader:(<span class="hljs-built_in">NSString</span> *)localFileName{
    <span class="hljs-comment">//獲取Response Header</span>
    <span class="hljs-built_in">NSMutableString</span> *header = [[<span class="hljs-built_in">NSMutableString</span> alloc]init];
    NSMutableData *returnData = [[NSMutableData alloc]init];

    NSData *fileContent = [NSData dataWithContentsOfFile:localFileName];
    <span class="hljs-keyword">int</span> contentLength = (<span class="hljs-keyword">int</span>)[fileContent length];

    [header appendString:@<span class="hljs-string">"HTTP/1.0 200 OKrn"</span>];
    [header appendString:@<span class="hljs-string">"Connection: closern"</span>];
    [header appendString:@<span class="hljs-string">"Server: Apache/1.3.0(Unix)rn"</span>];
    <span class="hljs-built_in">NSString</span> *contentLengthString = [<span class="hljs-built_in">NSString</span> stringWithFormat:@<span class="hljs-string">"Content-Length:%drn"</span>,contentLength];
    [header appendString:contentLengthString];

    <span class="hljs-built_in">NSString</span> *mimeString = [<span class="hljs-built_in">NSString</span> stringWithFormat:@<span class="hljs-string">"Content-Type: %@rn"</span>,[<span class="hljs-keyword">self</span> getMimeByFileName:localFileName]];
    [header appendString:mimeString];
    [header appendString:@<span class="hljs-string">"rn"</span>];
    NSData *headerData = [header dataUsingEncoding:NSUTF8StringEncoding];

    <span class="hljs-built_in">NSLog</span>(@<span class="hljs-string">"header = %@"</span>,header);
    [returnData appendData:headerData];
    [returnData appendData:fileContent];
    <span class="hljs-comment">//NSLog(@"return data = %@",returnData);</span>
    <span class="hljs-keyword">return</span> (NSData *)returnData;
}

- (<span class="hljs-built_in">NSString</span> *)getMimeByFileName:(<span class="hljs-built_in">NSString</span> *)localFileName{
    <span class="hljs-comment">//通過文件名判斷Response Header的MIME類型</span>
    <span class="hljs-built_in">NSRange</span> htmlRange = [localFileName rangeOfString:@<span class="hljs-string">"html"</span>];
    <span class="hljs-built_in">NSRange</span> htmRange = [localFileName rangeOfString:@<span class="hljs-string">"htm"</span>];
    <span class="hljs-built_in">NSRange</span> pngRange = [localFileName rangeOfString:@<span class="hljs-string">"png"</span>];
    <span class="hljs-keyword">if</span> (htmlRange<span class="hljs-variable">.location</span> != <span class="hljs-built_in">NSNotFound</span> || htmRange<span class="hljs-variable">.location</span> != <span class="hljs-built_in">NSNotFound</span>) {
        <span class="hljs-built_in">NSString</span> *mime = [[<span class="hljs-built_in">NSString</span> alloc]initWithFormat:@<span class="hljs-string">"text/html"</span>];
        <span class="hljs-keyword">return</span> mime;
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(pngRange<span class="hljs-variable">.location</span> != <span class="hljs-built_in">NSNotFound</span>){
        <span class="hljs-built_in">NSString</span> *mime = [[<span class="hljs-built_in">NSString</span> alloc]initWithFormat:@<span class="hljs-string">"image/png"</span>];
        <span class="hljs-keyword">return</span> mime;
    }
    <span class="hljs-built_in">NSString</span> *mime = [[<span class="hljs-built_in">NSString</span> alloc]initWithFormat:@<span class="hljs-string">"unknown"</span>];
    <span class="hljs-keyword">return</span> mime;
}

<span class="hljs-keyword">@end</span>
</code>
  • 因為Storyboard文件無法上傳,所以以上代碼只能顯示Socket服務器的工作流程而不能直接復制運行。需要自己寫出圖形界面或者綁定端口號。
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved