// 服務器 # pragma once using namespace std; # include <iostream> # include <string> # include <stdio.h> # include <winsock2.h> # pragma comment(lib,”ws2_32.lib”) # include “Tool.h” void main() { WSAData wsadata; SOCKET ListeningSocket; SOCKET newConnection; sockaddr_in serverAddr; sockaddr_in clientAddr; // Struct sockaddr_in // { // short sin_family; Sin_family:代表協議族,一般為AF_INET.代表使用TCP/IP協議族 // u_short sin_port; Sin_port: 代表端口號16位。注意字節序 // struct in_addr sin_addr; Sin_addr:代表32位IPV4地址。 // char sin_zero[8]; Sin_zero:8個字節的0補充。 // } int clientAddrLen = sizeof(sockaddr_in); int port = 5150; int ret; char dataBuffer[1024]; // int WSAStartup(WORD version, LPWSADATA lpWSAData); /* 此函數在應用程序中初始化Windows Sockets DLL ,只有此函數調用成功后,應用程序才可以再調用其他Windows Sockets DLL中的API函數。如果成功返回0。 Version 代表程序需要的Winsock的最高版本。其中主版本號在低 字節,次版本號在高字節。 比如:使用1.2的版本, version的值0x0201。 也可以使用MAKEWORD(2, 2)。 WORD MAKEWORD ( BYTE bLow, BYTE bHigh ); lpWSAData 代表WSADATA的指針,里面包括本機系統的信息。它是返回值 struct WSAData { WORD wVersion; 代表建議使用的版本號 WORD wHighVersion; 代表系統最高支持的版本號 char szDescription[WSADESCRIPTION_LEN + 1]; char szSystemStatus[WSASYSSTATUS_LEN + 1]; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; }; */ if ((ret = WSAStartup(MAKEWORD(2, 2), &wsadata)) != 0) { cout << “WSAStart up is failed with error\t” << ret << endl; return; } /* SOCKET socket(int af, int type, int protocol) af:代表協議族,一般為AF_INET. Type : 代表套接口類型,SOCK_STREAM流套接字, SOCK_DGRAM數據報套接字 流套接字上數據進出是無邊界的,可靠的。 數據報套接字是有邊界的,不可靠的(相對於流套接字而言) 流套接字是速度慢,數據報套字速度快。 流套接字是有序的,數據報套接字是無序的(后發的數據有可能先被接收到。) 所以一般tcp 用劉套接字,udp用數據報 關於udp協議,后章會給出(好吧,可能會給出,看有沒有時間) 估計qq的消息用得是udp,具體沒去研究—- Protocol : 指定所用的協議.如:IPPROTO_TCP、PPROTO_UDP 返回值:如果沒有錯誤發生,返回一個新的套接字, 否則返回INVALID_SOCKET。 */ // 創建一個監聽套接字,所謂監聽,就是看看有沒有客戶端連接上來 if (INVALID_SOCKET == (ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) { cout << “socket is falied with error\t” << WSAGetLastError() << endl; WSACleanup(); return; } // 本機系統所用的字節序稱為:本機字節順序 // 網絡標准使用的字節序稱為:網絡字節順序 /* htons()——本機到網絡(short) htonl()——本機到網絡(Long) ntohs()——網絡到本機(short) ntohl()——網絡到本機(long) */ serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(port); serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY就是指定地址為0.0.0.0的地址,這個地址事實上表示不確定地址,或“所有地址”、“任意地址”。 一般來說,在各個系統中均定義成為0值。 // 綁定端口 // bind(SOCKET s, const struct sockaddr FAR *name, int namelen); // 接下來要為服務器端定義的這個監聽的socket指定一個地 // 及端口(Port),這樣客戶端才知道待會要連接哪一個地址的哪個 // 端口,為此我們要調用bind()函數,該函數調用成功返回0,否則 // 返回SOCKET_ERROR if (SOCKET_ERROR == bind(ListeningSocket, (sockaddr*)&serverAddr, sizeof(serverAddr))) { cout << “bind is falied with error\t” << WSAGetLastError() << endl; closesocket(ListeningSocket); WSACleanup(); return; } // 監聽 // int listen( SOCKET s, int backlog ); // 服務器端的socket對象綁定完成之后,服務器端必須建立一個監聽的隊列來接收客戶端的連接請求。 // listen()函數使服務器端的socket 進入監聽狀態,並設定可以建立的最大連接數(目前最大值限制為 5, 最小值為1)。 // 該函數調用成功返回0,否則返回SOCKET_ERROR。 if (SOCKET_ERROR == listen(ListeningSocket, 5)) { cout << “listen is falied with error\t” << WSAGetLastError() << endl; closesocket(ListeningSocket); WSACleanup(); return; } cout << “now we are waiting a connection from client” << endl; cout <<“sizeof sockaddr_in:\t”<< sizeof(sockaddr_in) << “\tsizeof sockaddr\t” << sizeof(sockaddr) << endl; // 服務器端接受客戶端的連接請求 // SOCKET accept( SCOKET s, struct sockaddr FAR *addr,int FAR *addrlen ); // 為了使服務器端接受客戶端的連接請求,就要使用accept() 函數, // 該函數新建一個socket與客戶端的socket相通,原先監聽之socket繼續進入監聽狀態, // 等待他人的連接要求。該函數調用成功返回一個新產生的socket對象,否則返回INVALID_SOCKET。 if (INVALID_SOCKET == (newConnection = accept(ListeningSocket, (sockaddr*)&clientAddr, &clientAddrLen))) { cout << “accept is falied with error\t” << WSAGetLastError() << endl; closesocket(ListeningSocket); WSACleanup(); return; } cout << “get a connect from client addr\t” << inet_ntoa(clientAddr.sin_addr) << “\t” << ntohs(clientAddr.sin_port) << endl; // 數據的接收 // int recv( SOCKET s, char FAR *buf, int len, int flags ); // 參數: // s為socket 的識別碼 // buf:存放接收到的信息的暫存區 // len:buf的長度 // flags:此函數被調用的方式 // 返回值就是接受的字符串的長度(在沒出錯的情況下) if (SOCKET_ERROR == (ret = recv(newConnection, dataBuffer, sizeof(dataBuffer), 0))) { cout << “recv is errow with\t” << WSAGetLastError() << endl; closesocket(newConnection); WSACleanup(); return; } dataBuffer[ret] = ‘\0’; //wchar_t *temp = Tool::getSingleTon()->charToWchar(dataBuffer); //cout << “ret:\t” << ret << “\tdata\t” << temp << endl; // 暫時不知道怎么解析unity客戶端發過來的中文,,,憂傷的不行,試了好幾種方法,有知道的可以告訴我下 cout << dataBuffer << endl; // 結束 socket 連接 // 結束服務器和客戶端的通信連接是很簡單的,這一過程可以由服務器或客戶機的任一端啟動, // 只要調用closesocket()就可以了,而要關閉Server端監聽狀態的socket,同樣也是利用此函數。 // 另外,與程序啟動時調用WSAStartup()憨數相對應,程式結束前,需要調用 WSACleanup() // 來通知Winsock Stack釋放socket所占用的資源。這兩個函數都是調用成功返回0,否則返回SOCKET_ERROR。 // int PASCAL FAR closesocket(SOCKET s); // 參 數:s為socket 的識別碼; // int PASCAL FAR WSACleanup(void); closesocket(newConnection); WSACleanup(); }
unity客戶端 using UnityEngine; using System.Collections; using System.Text; using System.Net; using System.Net.Sockets; using System.IO; public class TestScoket : MonoBehaviour { void Start() { ConncetServer(); } void ConncetServer() { IPAddress ipAdr = IPAddress.Parse(“10.0.0.22”); IPEndPoint ipEp = new IPEndPoint(ipAdr, 5150); Socket clientScoket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); clientScoket.Connect(ipEp); string output = “zhangsan”;// 中文暫時不知道怎么解析 “中文” byte[] concent = Encoding.UTF8.GetBytes(output); //byte[] concent = Encoding.Unicode.GetBytes(output); int count = clientScoket.Send(concent); Debug.LogError(count); //byte[] response = new byte[1024]; //int bytesRead = clientScoket.Receive(response); //string input = Encoding.UTF8.GetString(response, 0, bytesRead); //print(“Client request:” + input); clientScoket.Shutdown(SocketShutdown.Both); clientScoket.Close(); } private void ConnectCallBack(System.IAsyncResult ar) { Debug.LogError(“連接成功”); } }
