iOS中基於 Socket 的 C/S 結構網路通訊(中),iossocket
結合上一篇的知識,接下來將介紹基於 TCP 協議的 Socket 編程;由於 Socket 需要有用戶端和服務端,那麼現在實現的是關於服務端的簡單程式。服務端採用的是CFStream 類來實現的。
這個服務端是把Xcode中的 Command Line Tool 來作為服務端的;當然,你也可以把 iPhone 作為服務端,但是要利用其他的架構,比如 AsyncSocket (https://github.com/roustem/AsyncSocket) ,裡面有分為 UDP 和 TCP 實現的 Socket,來源程式裡也有許多關於資料流的操作,可以作為深入理解來用,當然也是比較複雜的。
下面來看一下簡單實現的 TCP 服務端程式:(TCPServer.m)
<pre name="code" class="objc">#import <CoreFoundation/CoreFoundation.h>#include <sys/socket.h>#include <netinet/in.h>#define PORT 8734void AcceptCallBack(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void *);void ReadStreamClientCallBack (CFReadStreamRef stream, CFStreamEventType eventType,void *);int main(int argc, const char * argv[]){ /* 定義一個Server Socket引用 */ CFSocketRef sserver; /* 建立socket context */ CFSocketContext CTX = { 0, NULL, NULL, NULL, NULL }; /* 建立server socket TCP IPv4 設定回呼函數 */ sserver = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)AcceptCallBack, &CTX); if (sserver == NULL) return -1; /* 設定是否重新綁定標誌 */ int yes = 1; /* 設定socket屬性 SOL_SOCKET是設定tcp SO_REUSEADDR是重新綁定,yes 是否重新綁定*/ setsockopt(CFSocketGetNative(sserver), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); /* 設定連接埠和地址 */ struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); //memset函數對指定的地址進行記憶體拷貝 addr.sin_len = sizeof(addr); addr.sin_family = AF_INET; //AF_INET是設定 IPv4 addr.sin_port = htons(PORT); //htons函數 無符號短整型數轉換成“網路位元組序” addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY有核心分配,htonl函數 無符號長整型數轉換成“網路位元組序” /* 從指定位元組緩衝區複製,一個不可變的CFData對象*/ CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8*)&addr, sizeof(addr)); /* 設定Socket*/ if (CFSocketSetAddress(sserver, (CFDataRef)address) != kCFSocketSuccess) { fprintf(stderr, "Socket綁定失敗\n"); CFRelease(sserver); return -1; } /* 建立一個Run Loop Socket源 */ CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sserver, 0); /* Socket源添加到Run Loop中 */ CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes); CFRelease(sourceRef); printf("Socket listening on port %d\n", PORT); /* 運行Loop */ CFRunLoopRun(); }/* 接收用戶端請求後,回呼函數 */void AcceptCallBack( CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info){ CFReadStreamRef readStream = NULL; CFWriteStreamRef writeStream = NULL; /* data 參數涵義是,如果是kCFSocketAcceptCallBack類型,data是CFSocketNativeHandle類型的指標 */ CFSocketNativeHandle sock = *(CFSocketNativeHandle *) data; /* 建立讀寫Socket流 */ CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock, &readStream, &writeStream); if (!readStream || !writeStream) { close(sock); fprintf(stderr, "CFStreamCreatePairWithSocket() 失敗\n"); return; } CFStreamClientContext streamCtxt = {0, NULL, NULL, NULL, NULL}; // 註冊兩種回呼函數 CFReadStreamSetClient(readStream, kCFStreamEventHasBytesAvailable, ReadStreamClientCallBack, &streamCtxt); CFWriteStreamSetClient(writeStream, kCFStreamEventCanAcceptBytes, WriteStreamClientCallBack, &streamCtxt); //加入到迴圈當中 CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes); CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes); CFReadStreamOpen(readStream); CFWriteStreamOpen(writeStream); }/* 讀取流操作 用戶端有資料過來時候調用 */void ReadStreamClientCallBack(CFReadStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo){ UInt8 buff[255]; CFReadStreamRef inputStream = stream; if(NULL != inputStream) { CFReadStreamRead(stream, buff, 255); printf("接受到資料:%s\n",buff); CFReadStreamClose(inputStream); CFReadStreamUnscheduleFromRunLoop(inputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes); inputStream = NULL; }}/* 寫入流操作 用戶端在讀取資料時候調用 */void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo){ CFWriteStreamRef outputStream = stream; //輸出 UInt8 buff[] = "你好,用戶端!"; //向用戶端發送的資訊 if(NULL != outputStream) { CFWriteStreamWrite(outputStream, buff, strlen((const char*)buff)+1); //關閉輸出資料流 CFWriteStreamClose(outputStream); CFWriteStreamUnscheduleFromRunLoop(outputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes); outputStream = NULL; }}
此段程式只涉及比較簡單的資料流操作,詳細的資料流操作請參考 AsyncSocket 的源碼;至此,那麼一個服務端就已經實現了。
未完待續......
什是socket網路?什是C-S結構?
通訊端Socket是指向基於網路的另一個應用程式的通訊連的引用。應用程式通常通過通訊端向網路發出請求或者答應網路請求。socket大致位於會話層。c/s結構就是客戶機伺服器模式。其中一台或者幾台較大的電腦集中進行共用資料庫的管理和存取,稱為伺服器。其他的應用處理工作分散到網路上其他電腦上進行。B/s就是中庸伺服器結構,由若干個微機工作站與一台或者多台檔案伺服器通過通訊鏈路串連。比如超市的收銀機。
使用Socket來編寫C/S結構的的程式,實現客戶器端發送“你好伺服器端接收,同時,伺服器端返回“你好客
下面這個是伺服器端
import java.net.*;
import java.io.*;
public class SocketTest {
public static void main(String[] args) throws Exception{
//boolean isquit=true;
InputStream is;
OutputStream os;
byte [] buf = new byte[20];
String str;
ServerSocket ss = new ServerSocket(8000);
//while(isquit) 這裡不注釋可以一直監聽
{
Socket s = ss.accept();
is=s.getInputStream();
os=s.getOutputStream();
is.read(buf);
str = new String(buf);
System.out.println(str);
os.write("你好,用戶端!".getBytes());
}
}
}
下面這個是用戶端
import java.net.*;
import java.io.*;
public class Client {
public Client() {
}
public static void main(String [] args) throws Exception
{
OutputStream os;
InputStream is;
byte [] buf = new byte[20];
String str;
Socket s = new Socket("127.0.0.1",8000);
os = s.getOutputStream();
is = s.getInputStream();
os.write("你好".getBytes());
is.read(buf);
str = new String(buf);
System.out.println(str);
}
}
有什麼問題可以繼續問我,QQ:517044170