C++Socket编程总结 [转]

时间:2022-03-13 09:39:11

使用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

C++Socket编程总结 [转]的更多相关文章

  1. Linux下的C Socket编程 -- server端的继续研究

    Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...

  2. java socket编程(li)

    一.网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输.在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以 ...

  3. Python Socket 编程——聊天室示例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...

  4. Linux下的C Socket编程 -- server端的简单示例

    Linux下的C Socket编程(三) server端的简单示例 经过前面的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的一个端口上面去. 绑定socket ...

  5. Linux下的C Socket编程 -- 获取对方IP地址

    Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...

  6. Linux下的C Socket编程 -- 简介与client端的处理

    Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...

  7. python网络编程-socket编程

     一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...

  8. Socket编程实践&lpar;2&rpar; Socket API 与 简单例程

    在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...

  9. Socket编程实践&lpar;1&rpar; 基本概念

    1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...

  10. &lbrack;转&rsqb;C语言SOCKET编程指南

    1.介绍 Socket编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等 ...

随机推荐

  1. linux shell 中的sleep命令

    开始还以为是这样的语法: sleep(1), 后面发现是: linux shell 中的sleep命令 分类: LINUX 在有的shell(比如linux中的bash)中sleep还支持睡眠(分,小 ...

  2. 【USACO 2&period;3】Cow Pedigrees(DP)

    问n个结点深度为k且只有度为2或0的二叉树有多少种. dp[i][j]=dp[lk][ln]*dp[rk][j-1-ln],max(lk,rk)=i-1. http://train.usaco.org ...

  3. Android入门(十):界面的布局方式及其实际应用

    关于Android界面布局,网上已经有了很多非常不错的学习资料,在这里我也不班门弄斧了,推荐两篇我认为写的不错的教程,然后再重点讲一下几种布局方式的实际应用. 教程链接:①http://www.cnb ...

  4. 单例模式-C&plus;&plus;

    单例模式(Singleton) --本文内容部分引自<大话设计模式 Chapter21> 一.概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 通常我们可以让一个全局变量使一个 ...

  5. 前台js分页,自己手写逻辑2

    //设置分页 var pageSize = 10; //设置一次显示多少页 var pageLimit = 5; $(function(){ $.post("rest/rtdbfix/lis ...

  6. Direct3D11学习:(三)Direct3D11初始化

    转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 做完一系列的准备工作之后,我们就正式进入Direct3D11的学习了.我们就从Direct3D11的初始化工作开 ...

  7. const char&ast; &lt&semi;----- &gt&semi; string

    (1) const char*      <-----     string const char* const_txt_path=txt_path.c_str(); (2)  string  ...

  8. &lbrack;HDU 4585&rsqb; Shaolin &lpar;map应用&rpar;

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4585 题目大意:不停的插入数字,问你跟他相距近的ID号.如果有两个距离相近的话选择小的那个. 用map ...

  9. 【WEB小工具】jQuery函数

    jQuery-API帮助文档:Click here jQuery简介 jQuery是JavaScript框架,jQuery也是JavaScript代码.使用jQuery要比直接使用JavaScript ...

  10. idea2018注册

    1.在网上随便找一个注册码,lanyu的即可,注册时会被提示此注册码已被取消. 2.修改hosts文件,目录:C:\Windows\System32\drivers\etc\hosts,在文件中添加  ...