C++Socket編程總結 [轉]


使用socket寫代碼主要是要看自己的需求是什么。 如果通信時,內容很重要就要使TCP方式。 如果用戶數太多,可能就要使用UDP方式了。 在TCP模式下,最簡單的方式就是這樣的,使阻塞方式: 服務端: 1.初始化socket環境,創建socket 2.梆定一個端口 3.開始監聽 4.接收客戶端 5.接收到客戶端之后,使用這個socket來與這個客戶通信

#include "stdAfx.h" #include <winsock2.h> #include <mswsock.h> #include <iostream>

using namespace std;

#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "mswsock.lib")

DWORD IniSOCKDLL() { WORD wVersionRequested; WSADATA wsaData; int err=0; wVersionRequested = MAKEWORD( 2, 2 ); err = WSAStartup( wVersionRequested, &wsaData ); return err; }

int main(int argc, char* argv[]) { cout<<"程序開始"<<endl; IniSOCKDLL(); SOCKET ss=WSASocket(AF_INET,   SOCK_STREAM,   0,   NULL,   0,   NULL); SOCKADDR_IN addr; int len; addr.sin_family=AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(1002); len=sizeof(addr); bind(ss , (PSOCKADDR)&addr , len); listen(ss,5); SOCKET sc=accept(ss,(PSOCKADDR)&addr,&len); char buff[1024]; ZeroMemory(buff,1024); recv(sc,buff,1024,0); cout<<buff<<endl; ZeroMemory(buff,1024); memcpy(buff,"123",3); send(sc,buff,3,0); closesocket(sc); closesocket(ss); return 0; }

客戶端: 1.初始化socket環境,創建socket 2.連接服務端 3.開啟一個線程來接收數據 4.使用send直接發數據包

#include "stdAfx.h" #include <winsock2.h> #include <mswsock.h> #include <iostream>

using namespace std;

#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "mswsock.lib")

DWORD IniSOCKDLL() { WORD wVersionRequested; WSADATA wsaData; int err=0; wVersionRequested = MAKEWORD( 2, 2 ); err = WSAStartup( wVersionRequested, &wsaData ); return err; }

int main(int argc, char* argv[]) { IniSOCKDLL(); SOCKET sc=WSASocket(AF_INET,   SOCK_STREAM,   0,   NULL,   0,   NULL); SOCKADDR_IN addr; int len; addr.sin_family=AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(1002); len=sizeof(addr); connect(sc, (struct sockaddr *)&addr, len); char buff[1024]; ZeroMemory(buff,1024); memcpy(buff,"123",3); send(sc,buff,3,0);

recv(sc,buff,1024,0); cout<<buff<<endl;

closesocket(sc); return 0; }

由這個我們可以做一個這樣的模型: 為每個阻塞函數開一個線程,讓它來處理。 這就要留意退出的時候把這一些線程給關閉。

當然TCP方式的模型還有事件選擇模型。 就是把所有的網絡事件和我們的一個程序里定義的事件梆定。 這個有它的好處,可能可以讓我們更好的寫一個線程來管理 接收與發送。 現在來講一下一個完成端口模型。

  完成端口   一個完成端口其實就是一個通知隊列,由操作系統把已經完成的重疊I/O請求的通知 放入其中。當某項I/O操作一旦完成,某個可以對該操作結果進行處理的工作者線程 就會收到一則通知。而套接字在被創建后,可以在任何時候與某個完成端口進行關 聯。 步驟: 1、創建一個空的完成端口; 2、得到本地機器的CPU個數; 3、開啟CPU*2個工作線程(又名線程池),全部都在等待完成端口的完成包; 4、創建TCP的監聽socket,使用事件邦定,創建監聽線程; 5、當有人連接進入的時候,將Client socket保存到一個我們自己定義的關鍵鍵,     並把它與我們創建的完成端口關聯; 6、使用WSARecv和WSASend函數投遞一些請求,這是使用重疊I/O的方式; 7、重復5~6;

注:1、重疊I/O的方式中,接收與發送數據包的時候,一定要進行投遞請求這是    它們這個體系結構的特點    當然,在完成端口方式中,不是直接使用的WSARecv和WSASend函數進行請求    的投遞的。而是使用的ReadFile,Write的方式   2、完成端口使用了系統內部的一些模型,所以我們只要按照一定的順序調用就    可以完成了。   3、完成端口是使用在這樣的情況下,有成千上萬的用戶連接的時候,它能夠    保證性能不會降低。

#include <winsock2.h> #include <windows.h> #include <stdio.h>

#define PORT 5150 #define DATA_BUFSIZE 8192

//關鍵項 typedef struct {    OVERLAPPED Overlapped;    WSABUF DataBuf;    CHAR Buffer[DATA_BUFSIZE];    DWORD BytesSEND;    DWORD BytesRECV; } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;

typedef struct {    SOCKET Socket; } PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);

void main(void) {    SOCKADDR_IN InternetAddr;    SOCKET Listen;    SOCKET Accept;    HANDLE CompletionPort;    SYSTEM_INFO SystemInfo;    LPPER_HANDLE_DATA PerHandleData;    LPPER_IO_OPERATION_DATA PerIoData;    int i;    DWORD RecvBytes;    DWORD Flags;    DWORD ThreadID;    WSADATA wsaData;    DWORD Ret;

   if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)    {       printf("WSAStartup failed with error %d\n", Ret);       return;    }

   //打開一個空的完成端口

   if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)    {       printf( "CreateIoCompletionPort failed with error: %d\n", GetLastError());       return;    }

   // Determine how many processors are on the system.

   GetSystemInfo(&SystemInfo);

   // 開啟cpu個數的2倍個的線程

   for(i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)    {       HANDLE ThreadHandle;

      // Create a server worker thread and pass the completion port to the thread.

      if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,          0, &ThreadID)) == NULL)       {          printf("CreateThread() failed with error %d\n", GetLastError());          return;       }

      // Close the thread handle       CloseHandle(ThreadHandle);    }

   //打開一個服務器socket

   if ((Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,       WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)    {       printf("WSASocket() failed with error %d\n", WSAGetLastError());       return;    }

   InternetAddr.sin_family = AF_INET;    InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);    InternetAddr.sin_port = htons(PORT);

   if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)    {       printf("bind() failed with error %d\n", WSAGetLastError());       return;    }

   if (listen(Listen, 5) == SOCKET_ERROR)    {       printf("listen() failed with error %d\n", WSAGetLastError());       return;    }

   //開始接收從客戶端來的連接

   while(TRUE)    {       if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)       {          printf("WSAAccept() failed with error %d\n", WSAGetLastError());          return;       }

      // 創建一個關鍵項用於保存這個客戶端的信息,用戶接收發送的重疊結構,       // 還有使用到的緩沖區       if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR,          sizeof(PER_HANDLE_DATA))) == NULL)       {          printf("GlobalAlloc() failed with error %d\n", GetLastError());          return;       }

      // Associate the accepted socket with the original completion port.

      printf("Socket number %d connected\n", Accept);       PerHandleData->Socket = Accept;

      //與我們的創建的那個完成端口關聯起來,將關鍵項也與指定的一個完成端口關聯       if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,          0) == NULL)       {          printf("CreateIoCompletionPort failed with error %d\n", GetLastError());          return;       }

      // 投遞一次接收,由於接收都需要使用這個函數來投遞一個接收的准備

      if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR,          sizeof(PER_IO_OPERATION_DATA))) == NULL)       {          printf("GlobalAlloc() failed with error %d\n", GetLastError());          return;       }

      ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));       PerIoData->BytesSEND = 0;       PerIoData->BytesRECV = 0;       PerIoData->DataBuf.len = DATA_BUFSIZE;       PerIoData->DataBuf.buf = PerIoData->Buffer;

      Flags = 0;       if (WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,          &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)       {          if (WSAGetLastError() != ERROR_IO_PENDING)          {             printf("WSARecv() failed with error %d\n", WSAGetLastError());             return;          }       }    } } //工作線程 DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID) {    HANDLE CompletionPort = (HANDLE) CompletionPortID;    DWORD BytesTransferred;    LPOVERLAPPED Overlapped;    LPPER_HANDLE_DATA PerHandleData;    LPPER_IO_OPERATION_DATA PerIoData;    DWORD SendBytes, RecvBytes;    DWORD Flags;       while(TRUE)    {       //完成端口有消息來了       if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,          (LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)       {          printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());          return 0;       }

      //是不是有人退出了

      if (BytesTransferred == 0)       {          printf("Closing socket %d\n", PerHandleData->Socket);

         if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)          {             printf("closesocket() failed with error %d\n", WSAGetLastError());             return 0;          }

         GlobalFree(PerHandleData);          GlobalFree(PerIoData);          continue;       }

      //

      if (PerIoData->BytesRECV == 0)       {          PerIoData->BytesRECV = BytesTransferred;          PerIoData->BytesSEND = 0;       }       else       {          PerIoData->BytesSEND += BytesTransferred;       }

      if (PerIoData->BytesRECV > PerIoData->BytesSEND)       {

         // Post another WSASend() request.          // Since WSASend() is not gauranteed to send all of the bytes requested,          // continue posting WSASend() calls until all received bytes are sent.

         ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

         PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;          PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;

         if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,             &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)          {             if (WSAGetLastError() != ERROR_IO_PENDING)             {                printf("WSASend() failed with error %d\n", WSAGetLastError());                return 0;             }          }       }       else       {          PerIoData->BytesRECV = 0;

         // Now that there are no more bytes to send post another WSARecv() request.

         Flags = 0;          ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

         PerIoData->DataBuf.len = DATA_BUFSIZE;          PerIoData->DataBuf.buf = PerIoData->Buffer;

         if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,             &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)          {             if (WSAGetLastError() != ERROR_IO_PENDING)             {                printf("WSARecv() failed with error %d\n", WSAGetLastError());                return 0;             }          }       }    } }

 

原文:http://www.cnblogs.com/cnLiou/articles/202150.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM