精华内容
下载资源
问答
  • windows socket编程

    2013-04-12 18:28:34
    windows socket编程,适用于初学者,内附代码
  • Windows Socket编程

    2009-03-19 13:19:07
    有关于windows socket编程的详细说明和解示。是初学者的好资料。
  • Windows SOCKET编程

    千次阅读 2012-03-13 21:57:09
    Windows SOCKET编程   第一章 序言   我写这个专题的目的,一方面是为了通过对网络编程再一次系统的总结,提高自己的网络编程水平,特别是Windows下的网络编程水平。同时,我也希望,能为众多初学网络编程的人...

    Windows SOCKET编程

     

    第一章 序言

     

    我写这个专题的目的,一方面是为了通过对网络编程再一次系统的总结,提高自己的网络编程水平,特别是Windows下的网络编程水平。同时,我也希望,能为众多初学网络编程的人提供一点帮助,因为我开始学习网络编程的时候,能找到的资料就很少。当然,花钱可以买到翻译版本的书:)

    首先向大家推荐一本很好的参考书,Network Programming for Microsoft Windows 2nd,

    初学网络编程的时候我还不知道有这样一本好书,只是上各大论坛把能找到的网络编程方面的文章和代码下载下来,然后自己研究。后来看到别人推荐这一本书,下载了一个,看了感觉非常好,里面的内容写得很规范,条理也很清楚,英文好的朋友可以直接阅读,不然就只好去弄一本翻译好的来研究了。

    我试着从Windows编程的基础开始,一直到探索建立高性能的网络应用程序。我说过,我并不是以高手的身份写这本书,而是以和大家一起学习的心态学习网络编程,写书只是让自己的思路更清晰,以后还可以翻阅。所以,我不保证书中所有的内容都是绝对正确和标准的,有不妥的地方,还希望高手批评指正。

    这本书是完全免费的,读者可以任意使用书中的代码。但是如果需要转载,请注明原作者和出处。如果有商业运作的需求,请直接和我联系。

     

    第二章 Windows网络编程基础

     

    这本书主要探索Windows网络编程,开发平台是Windows 2000 和Visual C++.NET,从一个合格的C++程序员到网络编程高手,还是需要花不少功夫,至少我认为写一个聊天程序很简单,而要写一个能同时响应成千上万用户的高性能网络程序,的确不容易。这篇文章所介绍的方法也并不是能直接应用于每一个具体的应用程序,只能作为学习的参考资料。

    开发高性能网络游戏恐怕是促使很多程序员研究网络编程的原因(包括我),现在的大型网络游戏对同时在线人数的要求比较高,真正的项目往往采取多个服务器(组)负荷分担的方式工作,我将首先把注意力放到单个服务器的情况。

    大家都知道,我们用得最多的协议是UDP和TCP,UDP是不可靠传输服务,TCP是可靠传输服务。UDP就像点对点的数据传输一样,发送者把数据打包,包上有收信者的地址和其他必要信息,至于收信者能不能收到,UDP协议并不保证。而TCP协议就像(实际他们是一个层次的网络协议)是建立在UDP的基础上,加入了校验和重传等复杂的机制来保证数据可靠的传达到收信者。关于网络协议的具体内容,读者可以参考专门介绍网络协议的书籍,或者查看RFC中的有关内容。本书直接探讨编程实现网络程序的问题。

     

    2.1 Window Socket介绍

    Windows Socket是从UNIX Socket继承发展而来,最新的版本是2.2。进行Windows网络编程,你需要在你的程序中包含WINSOCK2.H或MSWSOCK.H,同时你需要添加引入库WS2_32. LIB或WSOCK32.LIB。准备好后,你就可以着手建立你的第一个网络程序了。

    Socket编程有阻塞和非阻塞两种,在操作系统I/O实现时又有几种模型,包括Select,WSAAsyncSelect,WSAEventSelect ,IO重叠模型,完成端口等。要学习基本的网络编程概念,可以选择从阻塞模式开始,而要开发真正实用的程序,就要进行非阻塞模式的编程(很难想象一个大型服务器采用阻塞模式进行网络通信)。在选择I/O模型时,我建议初学者可以从WSAAsyncSelect模型开始,因为它比较简单,而且有一定的实用性。但是,几乎所有人都认识到,要开发同时响应成千上万用户的网络程序,完成端口模型是最好的选择。

    既然完成端口模型是最好的选择,那为什么我们不直接写出一个使用完成端口的程序,然后大家稍加修改就OK了。我认为这确实是一个好的想法,但是真正做项目的时候,不同的情况对程序有不同的要求,如果不深入学习网络编程的各方面知识,是不可能写出符合要求的程序,在学习网络编程以前,我建议读者先学习一下网络协议。

     

    2.2 第一个网络程序

    由于服务器/客户端模式的网络应用比较多,而且服务器端的设计是重点和难点。所以我想首先探讨服务器的设计方法,在完成服务器的设计后再探讨其他模式的网络程序。

    设计一个基本的网络服务器有以下几个步骤:

    1、初始化Windows Socket

    2、创建一个监听的Socket

    3、设置服务器地址信息,并将监听端口绑定到这个地址上

    4、开始监听

    5、接受客户端连接

    6、和客户端通信

    7、结束服务并清理Windows Socket和相关数据,或者返回第4步

     

    我们可以看出设计一个最简单的服务器并不需要太多的代码,它完全可以做一个小型的聊天程序,或进行数据的传输。但是这只是我们的开始,我们的最终目的是建立一个有大规模响应能力的网络服务器。如果读者对操作系统部分的线程使用还有疑问,我建议你现在就开始复习,因为我们经常使用线程来提高程序性能,其实线程就是让CPU不停的工作,而不是总在等待I/O,或者是一个CPI,累死了还是一个CPU。千万不要以为线程越多的服务器,它的性能就越好,线程的切换也是需要消耗时间的,对于I/O等待少的程序,线程越多性能反而越低。

    下面是简单的服务器和客户端源代码。(阻塞模式下的,供初学者理解)

    TCPServer

     

    #include <winsock2.h>

    void main(void)

    {

    WSADATA wsaData;

    SOCKET ListeningSocket;

    SOCKET NewConnection;

    SOCKADDR_IN ServerAddr;

    SOCKADDR_IN ClientAddr;

    int Port = 5150;

     

    // 初始化Windows Socket 2.2

     

    WSAStartup(MAKEWORD(2,2), &wsaData);

     

    // 创建一个新的Socket来响应客户端的连接请求

     

    ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

     

    // 填写服务器地址信息

    // 端口为5150

    // IP地址为INADDR_ANY,注意使用htonl将IP地址转换为网络格式

     

    ServerAddr.sin_family = AF_INET;

    ServerAddr.sin_port = htons(Port);

    ServerAddr.sin_addr.s_addr =htonl(INADDR_ANY);

     

    // 绑定监听端口

     

    bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr));

     

    // 开始监听,指定最大同时连接数为5

     

    listen(ListeningSocket, 5);

     

    // 接受新的连接

     

    NewConnection = accept(ListeningSocket, (SOCKADDR *) &ClientAddr,&ClientAddrLen));

     

    // 新的连接建立后,就可以互相通信了,在这个简单的例子中,我们直接关闭连接,

    // 并关闭监听Socket,然后退出应用程序

    //

     

    closesocket(NewConnection);

    closesocket(ListeningSocket);

     

    // 释放Windows Socket DLL的相关资源

     

    WSACleanup();

    }

     

     

    TCPClient

    # include <winsock2.h>

    void main(void)

    {

    WSADATA wsaData;

    SOCKET s;

    SOCKADDR_IN ServerAddr;

    int Port = 5150;

     

    //初始化Windows Socket 2.2

     

    WSAStartup(MAKEWORD(2,2), &wsaData);

     

    // 创建一个新的Socket来连接服务器

     

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

     

    // 填写客户端地址信息

    // 端口为5150

    // 服务器IP地址为"136.149.3.29",注意使用inet_addr将IP地址转换为网络格式

     

    ServerAddr.sin_family = AF_INET;

    ServerAddr.sin_port = htons(Port);

    ServerAddr.sin_addr.s_addr = inet_addr("136.149.3.29");

     

    // 向服务器发出连接请求

     

    connect(s, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr));

     

    // 新的连接建立后,就可以互相通信了,在这个简单的例子中,我们直接关闭连接,

    // 并关闭监听Socket,然后退出应用程序

     

    closesocket(s);

     

    // 释放Windows Socket DLL的相关资源

     

    WSACleanup();

    }

     

     

    2.3 WSAAsyncSelect模式

    前面说过,Windows网络编程模式有好几种,他们各有特点,实现起来复杂程度各不相同,适用范围也不一样。下图是Network Programming for Microsoft Windows 2nd 一书中对不同模式的一个性能测试结果。服务器采用Pentium 4 1.7GHz Xeon的CPU,768M内存;客户端有3台PC,配置分别是Pentium 2 233MHz ,128 MB 内存,Pentium 2 350 MHz ,128 MB内存,Itanium 733 MHz ,1 GB内存。

    具体的结果分析大家可以看看原书中作者的叙述,我关心的是哪种模式是我需要的。首先是服务器,勿庸置疑,肯定是完成端口模式。那么客户端呢,当然也可以采用完成端口,但是不同模式是在不同的操作系统下支持的,看下图:

    完成端口在Windows 98下是不支持的,虽然我们可以假定所有的用户都已经装上了Windows 2000和Windows XP,。但是,如果是商业程序,这种想法在现阶段不应该有,我们不能让用户为了使用我们的客户端而去升级他的操作系统。Overlapped I/O可以在Windows 98下实现,性能也不错,但是实现和理解起来快赶上完成端口了。而且,最关键的一点,客户端程序不是用来进行大规模网络响应的,客户端的主要工作应该是进行诸如图形运算等非网络方面的任务。原书作者,包括我强烈推荐大家使用WSAAsyncSelect模式实现客户端,因为它实现起来比较直接和容易,而且他完全可以满足客户端编程的需求。

    下面是一段源代码,虽然我们是用它来写客户端,我还是把它的服务端代码放上来,一方面是有兴趣的朋友可以用他做测试和了解如何用它实现服务器;另一方面是客户端的代码可以很容易的从它修改而成,不同的地方只要参考一下2.1节里的代码就知道了。

     

    #define WM_SOCKET WM_USER + 1

    #include <winsock2.h>

    #include <windows.h>

     

    int WINAPI WinMain(HINSTANCE hInstance,

    HINSTANCE hPrevInstance, LPSTR lpCmdLine,

    int nCmdShow)

    {

    WSADATA wsd;

    SOCKET Listen;

    SOCKADDR_IN InternetAddr;

    HWND Window;

     

    // 创建主窗口

     

    Window = CreateWindow();

    // 初始化Windows Socket 2.2

     

    WSAStartup(MAKEWORD(2,2), &wsd);

     

    // 创建监听Socket

    Listen = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);

     

    // 设置服务器地址

     

    InternetAddr.sin_family = AF_INET;

    InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    InternetAddr.sin_port = htons(5150);

     

    // 绑定Socket

    bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr));

     

    // 设置Windows消息,这样当有Socket事件发生时,窗口就能收到对应的消息通知

    // 服务器一般设置 FD_ACCEPT │ FD_READ | FD_CLOSE

    // 客户端一般设置 FD_CONNECT │ FD_READ | FD_CLOSE

    WSAAsyncSelect(Listen, Window, WM_SOCKET, FD_ACCEPT │ FD_READ | FD_CLOSE);

     

    // 开始监听

    listen(Listen, 5);

     

    // Translate and dispatch window messages

    // until the application terminates

    while (1) {

    // ...

    }

    }

     

    BOOL CALLBACK ServerWinProc(HWND hDlg,UINT wMsg,

    WPARAM wParam, LPARAM lParam)

    {

    SOCKET Accept;

     

    switch(wMsg)

    {

    case WM_PAINT:

    // Process window paint messages

    break;

     

    case WM_SOCKET:

     

    // Determine whether an error occurred onthe

    // socket by using the WSAGETSELECTERROR()macro

     

    if (WSAGETSELECTERROR(lParam))

    {

    // Display the error and close the socket

    closesocket( (SOCKET) wParam);

    break;

    }

     

    // Determine what event occurred on the

    // socket

     

    switch(WSAGETSELECTEVENT(lParam))

    {

    case FD_ACCEPT:

     

    // Accept an incoming connection

    Accept = accept(wParam, NULL, NULL);

     

    // Prepare accepted socket for read,

    // write, and close notification

     

    WSAAsyncSelect(Accept, hDlg, WM_SOCKET,

    FD_READ │ FD_WRITE │ FD_CLOSE);

    break;

     

    case FD_READ:

     

    // Receive data from the socket in

    // wParam

    break;

     

    case FD_WRITE:

     

    // The socket in wParam is ready

    // for sending data

    break;

     

    case FD_CLOSE:

     

    // The connection is now closed

    closesocket( (SOCKET)wParam);

    break;

    }

    break;

    }

    return TRUE;

    }

     

     

    2.4 小节

    目前为止,我非常简要的介绍了Windows网络编程的一些东西,附上了一些源代码。可以说,读者特别是初学者,看了后不一定就能马上写出程序来,而那些代码也不是可以直接应用于实际的项目。别急,万里长征才开始第一步呢,很多书里都是按照基础到应用的顺序来写的,但是我喜欢更直接一点,更实用一些的方式。而且,我写的这个专题,毕竟不是商业化的,时间上不能投入过多,只是作为给初学者的一个小小帮助。更多的还是希望读者自己刻苦研究,有问题的时候可以到我的论坛上给我留言,以后有机会我也会公布一些实际的代码。希望结交更多热爱编程和中国游戏事业的朋友。下一章里我将主要讲解完成端口编程,这也是我写这篇文章的初衷,希望对大家能有所帮助。

     

    第三章 完成端口模式下的高性能网络服务器

     

    3.1开始

    完成端口听起来好像很神秘和复杂,其实并没有想象的那么难。这方面的文章在论坛上能找到的我差不多都看过,写得好点的就是CSDN.NET上看到的一组系列文章,不过我认为它只是简单的翻译了一下Network Programming for Microsoft Windows 2nd 中的相关内容,附上的代码好像不是原书中的,可能是另一本外文书里的。我看了以后,觉得还不如看原版的更容易理解。所以在我的开始部分,我主要带领初学者理解一下完成端口的有关内容,是我开发的经验,其他的请参考原书的相关内容。

    采用完成端口的好处是,操作系统的内部重叠机制可以保证大量的网络请求都被服务器处理,而不是像WSAAsyncSelect 和WSAEventSelect的那样对并发的网络请求有限制,这一点从上一章的测试表格中可以清楚的看出。

    完成端口就像一种消息通知的机制,我们创建一个线程来不断读取完成端口状态,接收到相应的完成通知后,就进行相应的处理。其实感觉就像WSAAsyncSelect一样,不过还是有一些的不同。比如我们想接收消息,WSAAsyncSelect会在消息到来的时候直接通知Windows消息循环,然后就可以调用WSARecv来接收消息了;而完成端口则首先调用一个WSARecv表示程序需要接收消息(这时可能还没有任何消息到来),但是只有当消息来的时候WSARecv才算完成,用户就可以处理消息了,然后再调用一个WSARecv表示等待下一个消息,如此不停循环,我想这就是完成端口的最大特点吧。

    Per-handle Data 和 Per-I/O Operation Data 是两个比较重要的概念,Per-handle Data用来把客户端数据和对应的完成通知关联起来,这样每次我们处理完成通知的时候,就能知道它是哪个客户端的消息,并且可以根据客户端的信息作出相应的反应,我想也可以理解为Per-Client handle Data吧。Per-I/O Operation Data则不同,它记录了每次I/O通知的信息,比如接收消息时我们就可以从中读出消息的内容,也就是和I/O操作有关的信息都记录在里面了。当你亲手实现完成端口的时候就可以理解他们的不同和用途了。

    CreateIoCompletionPort函数中有个参数NumberOfConcurrentThreads,完成端口编程里有个概念Worker Threads。这里比较容易引起混乱,NumberOfConcurrentThreads需要设置多少,又需要创建多少个Worker Threads才算合适?NumberOfConcurrentThreads的数目和CPU数量一样最好,因为少了就没法利用多CPU的优势,而多了则会因为线程切换造成性能下降。Worker Threads的数量是不是也要一样多呢,当然不是,它的数量取决于应用程序的需要。举例来说,我们在Worker Threads里进行消息处理,如果这个过程中有可能会造成线程阻塞,那如果我们只有一个Worker Thread,我们就不能很快响应其他客户端的请求了,而只有当这个阻塞操作完成了后才能继续处理下一个完成消息。但是如果我们还有其他的Worker Thread,我们就能继续处理其他客户端的请求,所以到底需要多少的Worker Thread,需要根据应用程序来定,而不是可以事先估算出来的。如果工作者线程里没有阻塞操作,对于某些情况来说,一个工作者线程就可以满足需要了。

    其他问题,Network Programming for Microsoft Windows 2nd中,作者还提出了如何安全的退出应用程序等等实现中的细节问题,这里我就不一一讲述了,请读者参考原书的相关内容,如果仍有疑问,可以联系我。

     

    3.2实现

    下面是一般的实现步骤

    1. 获得计算机信息,得到CPU的数量。创建一个完成端口,第四个参数置0,指定NumberOfConcurrentThreads为CPU个数。

    2. Determine how many processors exist onthe system.

    3. Create worker threads to servicecompleted I/O requests on the completion port using processor information instep 2. In the case of this simple example, we create one worker thread per processor because we do not expectour threads to ever get in a suspended condition in which there would not beenough threads to execute for each processor. When the CreateThread function iscalled, you mustsupply a worker routine that the thread executes upon creation. We will discussthe worker thread's responsibilities later in this section.

    4. Prepare a listening socket to listen forconnections on port 5150.

    5. Accept inbound connections using theaccept function.

    6. Create a data structure to representper-handle data and save the accepted socket handle in the structure.

    7. Associate the new socket handle returnedfrom accept with the completion port by calling CreateIoCompletionPort. Passthe per-handle data structure to CreateIoCompletionPort via the completion keyparameter.

    8. Start processing I/O on the acceptedconnection. Essentially, you want to post one or more asynchronous WSARecv or WSASendrequests on the new socket using the overlapped I/O mechanism. When these I/Orequests complete, a workerthread services the I/O requests and continues processing future I/O requests, as we will see later in the workerroutine specified in step 3.

    9. Repeat steps 5-8 until serverterminates.

     

    那么学习完成端口编程从哪里开始比较好,对于初学者而言,直接进入编程并不是一个好主意,我建议初学者首先学习用异步Socket模式,即WSAEventSelect模式构建一个简单的聊天服务器。当把Windows网络编程的概念有一个清晰的认识之后,再深入研究完成端口编程。

    接着就是深入研究具体的编程实现了,从Network Programming for Microsoft Windows 2nd中摘录的这段经典代码可以说是非常合适的,这里我只简单解释一下其中比较关键的地方,还有不明白的可以参看原书,或者联系我。

     

    主程序段:

    1. HANDLE CompletionPort;

    2. WSADATA wsd;

    3. SYSTEM_INFO SystemInfo;

    4. SOCKADDR_IN InternetAddr;

    5. SOCKET Listen;

    6. int i;

    7.

    8. typedef struct _PER_HANDLE_DATA

    9. {

    10. SOCKET Socket;

    11. SOCKADDR_STORAGE ClientAddr;

    12. // 在这里还可以加入其他和客户端关联的数据

    13. } PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

    14.

    15. // 初始化Windows Socket 2.2

    16. StartWinsock(MAKEWORD(2,2), &wsd);

    17.

    18. // Step 1:

    19. // 创建完成端口

    20.

    21. CompletionPort = CreateIoCompletionPort(

    22. INVALID_HANDLE_VALUE, NULL, 0, 0);

    23.

    24. // Step 2:

    25. // 检测系统信息

    26.

    27. GetSystemInfo(&SystemInfo);

    28.

    29. // Step 3: 创建工作者线程,数量和CPU的数量一样多

    30. // Create worker threads based on thenumber of

    31. // processors available on the system. Forthis

    32. // simple case, we create one worker thread foreach

    33. // processor.

    34.

    35. for(i = 0; i <SystemInfo.dwNumberOfProcessors; i++)

    36. {

    37. HANDLE ThreadHandle;

    38.

    39. // Create a server worker thread, and pass the

    40. // completion port to the thread. NOTE:the

    41. // ServerWorkerThread procedure is notdefined

    42. // in this listing.

    43.

    44. ThreadHandle = CreateThread(NULL, 0,

    45. ServerWorkerThread, CompletionPort,

    46. 0, NULL;

    47.

    48. // Close the thread handle

    49. CloseHandle(ThreadHandle);

    50. }

    51.

    52. // Step 4:

    53. // 创建监听Socket

    54.

    55. Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,

    56. WSA_FLAG_OVERLAPPED);

    57.

    58. InternetAddr.sin_family = AF_INET;

    59. InternetAddr.sin_addr.s_addr =htonl(INADDR_ANY);

    60. InternetAddr.sin_port = htons(5150);

    61. bind(Listen, (PSOCKADDR) &InternetAddr,

    62. sizeof(InternetAddr));

    63.

    64. // 开始监听

    65.

    66. listen(Listen, 5);

    67.

    68. while(TRUE)

    69. {

    70. PER_HANDLE_DATA *PerHandleData=NULL;

    71. SOCKADDR_IN saRemote;

    72. SOCKET Accept;

    73. int RemoteLen;

    74. // Step 5: 等待客户端连接,然后将客户端Socket加入完成端口

    75. // Accept connections and assign to thecompletion

    76. // port

    77.

    78. RemoteLen = sizeof(saRemote);

    79. Accept = WSAAccept(Listen, (SOCKADDR *)&saRemote,

    80. &RemoteLen);

    81.

    82. // Step 6: 初始化客户端数据

    83. // Create per-handle data informationstructure to

    84. // associate with the socket

    85. PerHandleData = (LPPER_HANDLE_DATA)

    86. GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));

    87.

    88. printf("Socket number %d connected

    ", Accept);

    89. PerHandleData->Socket = Accept;

    90.memcpy(&PerHandleData->ClientAddr, &saRemote, RemoteLen);

    91.

    92. // Step 7:

    93. // Associate the accepted socket withthe

    94. // completion port

    95.

    96. CreateIoCompletionPort((HANDLE) Accept,

    97. CompletionPort, (DWORD) PerHandleData, 0);

    98.

    99. // Step 8: 发出对客户端的I/O请求,等待完成消息

    100. // Start processing I/O on theaccepted socket.

    101. // Post one or more WSASend() orWSARecv() calls

    102. // on the socket using overlapped I/O.

    103. WSARecv(...);

    104. }

    105.

    106.

     

     

    工作者线程

     

    DWORD WINAPI ServerWorkerThread(LPVOIDCompletionPortID)

    {

    HANDLE CompletionPort = (HANDLE)CompletionPortID;

    DWORD BytesTransferred;

    LPOVERLAPPED Overlapped;

    LPPER_HANDLE_DATA PerHandleData;

    LPPER_IO_DATA PerIoData;

    DWORD SendBytes, RecvBytes;

    DWORD Flags;

     

    while(TRUE)

    {

    // 等待完成端口消息,未收到消息德时候则阻塞线程

     

    ret =GetQueuedCompletionStatus(CompletionPort,

    &BytesTransferred,(LPDWORD)&PerHandleData,

    (LPOVERLAPPED *) &PerIoData, INFINITE);

     

    // First check to see if an error has occurred

    // on the socket; if so, close the

    // socket and clean up the per-handle data

    // and per-I/O operation data associatedwith

    // the socket

     

    if (BytesTransferred == 0&&

    (PerIoData->OperationType ==RECV_POSTED ││

    PerIoData->OperationType ==SEND_POSTED))

    {

    // A zero BytesTransferred indicates thatthe

    // socket has been closed by the peer, so

    // you should close the socket. Note:

    // Per-handle data was used to referencethe

    // socket associated with the I/Ooperation.

     

    closesocket(PerHandleData->Socket);

     

    GlobalFree(PerHandleData);

    GlobalFree(PerIoData);

    continue;

    }

     

    // Service the completed I/O request. Youcan

    // determine which I/O request has just

    // completed by looking at theOperationType

    // field contained in the per-I/O operationdata.

    if (PerIoData->OperationType ==RECV_POSTED)

    {

    // Do something with the received data

    // in PerIoData->Buffer

    }

     

    // Post another WSASend or WSARecvoperation.

    // As an example, we will post another WSARecv()

    // I/O operation.

     

    Flags = 0;

     

    // Set up the per-I/O operation data forthe next

    // overlapped call

    ZeroMemory(&(PerIoData->Overlapped),

    sizeof(OVERLAPPED));

     

    PerIoData->DataBuf.len =DATA_BUFSIZE;

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

    PerIoData->OperationType = RECV_POSTED;

     

    WSARecv(PerHandleData->Socket,

    &(PerIoData->DataBuf), 1, &RecvBytes,

    &Flags,&(PerIoData->Overlapped), NULL);

    }

    }

     

    3.3 小节

    讲这么点就完了?你一定认为我介绍的东西并没有超过原书中的内容,实事上完成端口编程的精髓就是上面的代码和原书中的有关叙述。如果我再把他们完整的重复一遍,那又有什么意思呢?根据我的经验,设计网络服务器的真正难点,不在于完成端口技术,所以我想利用小节把自己编程中的一些经验告诉大家。

    首先是服务器的管理,一个服务器首先要分析它的设计目标是应对很多的连接还是很大的数据传送量。这样在设计工作者线程时就可以最大限度的提高性能。管理客户端方面,我们可以将客户端的数据捆绑到Perhand-Data数据结构上,如果还有需要,可以建一个表来记录客户端的宏观情况。

    在Ares引擎中,我将文件传送和大容量数据传送功能也封装进了服务器和客户端。我建议服务器和客户端都应该封装这些功能,尽管我们并不是做FTP服务器,但是当客户端需要和服务器交换文件和大块数据时,你会发现这样做,灵活性和性能都能做得比用单纯的FTP协议来更好,所以在你的服务器和客户端可以传送数据包以后,把他们都做进去吧。

    为了服务器不被黑客攻击,或被BUG弄崩溃,我们还需要认真设计服务器的认证机制,以及密切注意程序中的溢出,一定要在每一个使用缓冲区的地方加上检查代码。可以说并没有现成的办法来解决这个问题,不然就没有人研究网络安全了,所以我们要做的是尽量减少错误,即使出现错误也不会造成太大损失,在发现错误的时候能够很快纠正同类错误。

    还有就是对客户端情况的检测,比如客户端的正常和非正常断开连接。如果不注意这一点,就会造成服务器资源持续消耗而最终崩溃,因为我们的服务器不可能总是重启,而是要持续的运行,越久越好。还有比如客户端断开连接后又尝试连接,但是在服务器看来这个客户"仍然在线",这个时候我们不能单纯的拒绝客户端的连接,也不能单纯的接收。

    讲了几点服务器设计中的问题,他们只是众多问题中的一小部分,限于时间原因,在这个版本的文章中就说这么多。你一定会发现,其实网络编程最困难和有成就的地方,并不是服务器用了什么模式等等,而是真正深入设计的时候碰到的众多问题。正是那些没有标准答案的问题,值得我们去研究和解决。

     

    第四章 作者的话

    写这篇文章的目的,一方面是简要的谈谈游戏编程中的网络部分。另一方面是结交众多开发的朋友。毕竟我们做东西不可能不和他人交流,也不可能只做非商业化的项目。我开发的Ares引擎就是同时为了这两个目的,到我写这篇文章的时候,引擎的版本仍然是3.2,并不是我不想继续开发,也不是没有新的改变了。恰恰相反,我有很多新的想法,急切想把他们加入新的版本中,只是现在手上还有短期的项目没有完成。

    有希望交流的朋友,希望合作开发的朋友,有项目委托的朋友。。。联系我。

    展开全文
  • windows Socket编程

    2016-04-08 22:10:04
    windowssocket编程有两种方式,tcp和udp 通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0)...

    windows下socket编程有两种方式,tcp和udp
    通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),这表示建立一个socket用于流式网络通讯。

    SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制保证它是可靠的、有序的,即每个包按照发送的顺序到达接收方。

    而SOCK_DGRAM这种是User Datagram Protocol协议的网络通讯,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据。任何一方建立一个socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存在,是否发送了数据。它的特点是通讯速度比较快。大家都知道TCP是要经过三次握手的,而UDP没有。

    基于上述不同,UDP和TCP编程步骤也有些不同,如下:
    TCP编程的服务器端一般步骤是:
    1、创建一个socket,用函数socket();
    2、设置socket属性,用函数setsockopt(); * 可选
    3、绑定IP地址、端口等信息到socket上,用函数bind();
    4、开启监听,用函数listen();
    5、接收客户端上来的连接,用函数accept();
    6、收发数据,用函数send()和recv(),或者read()和write();
    7、关闭网络连接;
    8、关闭监听;

    TCP编程的客户端一般步骤是:
    1、创建一个socket,用函数socket();
    2、设置socket属性,用函数setsockopt();* 可选
    3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
    4、设置要连接的对方的IP地址和端口等属性;
    5、连接服务器,用函数connect();
    6、收发数据,用函数send()和recv(),或者read()和write();
    7、关闭网络连接;

    与之对应的UDP编程步骤要简单许多,分别如下:
    UDP编程的服务器端一般步骤是:
    1、创建一个socket,用函数socket();
    2、设置socket属性,用函数setsockopt();* 可选
    3、绑定IP地址、端口等信息到socket上,用函数bind();
    4、循环接收数据,用函数recvfrom();
    5、关闭网络连接;

    UDP编程的客户端一般步骤是:
    1、创建一个socket,用函数socket();
    2、设置socket属性,用函数setsockopt();* 可选
    3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
    4、设置对方的IP地址和端口等属性;
    5、发送数据,用函数sendto();
    6、关闭网络连接;

    Tcp服务器

    #pragma comment(lib, "ws2_32.lib")
    #include <winsock2.h>
    #include <stdio.h>
    int main()
    {
         SOCKET mysock,tsock;              // 定义套接字
         struct sockaddr_in my_addr;       // 本地地址信息
         struct sockaddr_in their_addr;     // 连接者地址信息
         int sin_size;
         WSADATA wsa;
         WSAStartup(MAKEWORD(2,2),&wsa);       //初始化Windows Socket 
         //建立socket
         mysock = socket(AF_INET, SOCK_STREAM, 0);
         //bind本机的端口
         my_addr.sin_family = AF_INET;               // 协议类型是INET
         my_addr.sin_port = htons(1234);           // 绑定端口1234
         my_addr.sin_addr.s_addr = INADDR_ANY;   // 本机IP
         bind(mysock, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
          //listen,监听端口
         listen(mysock, 10); // 等待连接数目
         printf("listen......");    
         //等待客户端连接
         sin_size = sizeof(struct sockaddr_in);
         tsock = accept(mysock, (struct sockaddr *)&their_addr, &sin_size);
         //有连接就发送Hello!字符串过去
         send(tsock, "Hello!\n", sizeof("Hello!\n"), 0);
         printf("send ok!\n");
          //成功,关闭套接字
         closesocket(mysock);
         closesocket(tsock);
         return 0;
    }

    程序运行后,服务器端显示listen,通过telnet目标主机ip:端口号,连接后显示“hello”

    Udp socket服务器

    #include <winsock2.h>
    #include <stdio.h>
    BOOL InitWinsock();
    void main()
    {
        SOCKET socket1;
    
        InitWinsock();
        struct sockaddr_in server;
        int len =sizeof(server);
        server.sin_family=AF_INET;
        server.sin_port=htons(1000);                      ///server的监听端口
        server.sin_addr.s_addr=inet_addr("12.1.34.217"); ///server的地址 
    
        socket1=socket(AF_INET,SOCK_DGRAM,0);
        while (1)
        {
    
            char buffer[1024]="\0";
            printf("input message\n");
            scanf("%s",buffer);
            if (strcmp(buffer,"bye")==0)
                break;
            if (sendto(socket1,buffer,sizeof buffer,0,(struct sockaddr*)&server,len)!=SOCKET_ERROR)
            {   
                if (recvfrom(socket1,buffer,sizeof buffer,0,(struct sockaddr*)&server,&len)!=SOCKET_ERROR)
                    printf("rece from server:%s\n",buffer);
            }       
        }
        closesocket(socket1);
    }
    
    BOOL InitWinsock()
    {
        int Error;
        WORD VersionRequested;
        WSADATA WsaData;
        VersionRequested=MAKEWORD(2,2);
        Error=WSAStartup(VersionRequested,&WsaData); //启动WinSock2
        if(Error!=0)
        {
            return FALSE;
        }
        else
        {
            if(LOBYTE(WsaData.wVersion)!=2||HIBYTE(WsaData.wHighVersion)!=2)
            {
                WSACleanup();
                return FALSE;
            }
    
        }
        return TRUE;
    }

    udp socket 客户端

    #include <winsock2.h>
    #include <stdio.h>
    BOOL InitWinsock();
    void main()
    {
        SOCKET socket1;
    
        InitWinsock();
        struct sockaddr_in local;
        struct sockaddr_in from;
        int fromlen =sizeof(from);
        local.sin_family=AF_INET;
        local.sin_port=htons(1000);             ///监听端口
        local.sin_addr.s_addr=INADDR_ANY;       ///本机
    
        socket1=socket(AF_INET,SOCK_DGRAM,0);
    
        bind(socket1,(struct sockaddr*)&local,sizeof local);
    
        while (1)
        {
            char buffer[1024]="\0";
            printf("waiting for message from others-------------\n");
            if (recvfrom(socket1,buffer,sizeof buffer,0,(struct sockaddr*)&from,&fromlen)!=SOCKET_ERROR)
            {
                printf("Received datagram from %s--%s\n",inet_ntoa(from.sin_addr),buffer);
                ////给cilent发信息
                sendto(socket1,buffer,sizeof buffer,0,(struct sockaddr*)&from,fromlen);
    
            }
            Sleep(500);
        }
    
        closesocket(socket1);
    
    
    }
    
    BOOL InitWinsock()
    {
        int Error;
        WORD VersionRequested;
        WSADATA WsaData;
        VersionRequested=MAKEWORD(2,2);
        Error=WSAStartup(VersionRequested,&WsaData); //启动WinSock2
        if(Error!=0)
        {
            return FALSE;
        }
        else
        {
            if(LOBYTE(WsaData.wVersion)!=2||HIBYTE(WsaData.wHighVersion)!=2)
            {
                WSACleanup();
                return FALSE;
            }
    
        }
        return TRUE;
    }

    wsasocket

    socket()创建一个通讯端点并返回一个套接口。但是在socket库中例程在应用于阻塞套接口时会阻塞。
    WSASocket()的发送操作和接收操作都可以被重叠使用。接收函数可以被多次调用,发出接收缓冲区,准备接收到来的数据。发送函数也可以被多次调用,组成一个发送缓冲区队列。 可是socket()却只能发过之后等待回消息才可做下一步操作!

    Header: Declared in Winsock2.h.
    Library: Use Ws2_32.lib.
    
    SOCKET WSASocket(
        int af,
        int type,
        int protocol,
        LPWSAPROTOCOL_INFO lpProtocolInfo,
        GROUP g,
        DWORD dwFlags
    );
    af:地址族描述。目前仅支持PF_INET格式,亦即ARPA Internet地址格式。
    type:新套接口的类型描述。
    protocol:套接口使用的特定协议,如果调用者不愿指定协议则定为0。
    lpProtocolInfo:一个指向PROTOCOL_INFO结构的指针,该结构定义所创建套接口的特性。如果本参数非零,则前三个参数(af, type, protocol)被忽略。  
    g:套接口组的描述字。
    iFlags:套接口属性描述。
    返回值:
    若无错误发生,WSASocket()返回新套接口的描述字。否则的话,返回 INVALID_SOCKET,应用程序可以调用WSAGetLastError()来获取相应的错误代码。
    错误代码:
    WSANOTINITIALISED            在调用本API之前应成功调用WSAStartup()。
    WSAENETDOWN                  网络子系统失效。
    WSAEAFNOSUPPORT              不支持指定的地址族。
    WSAEINPROGRESS               一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数WSAEMFILE                    无可用的套接口描述字。
    WSAENOBUFS                   无可用的缓冲区空间。套接口无法创建。
    WSAEPROTONOSUPPORT           不支持指定的协议。
    WSAEPROTOTYPE                指定的协议对于本套接口类型错误。
    WSAESOCKTNOSUPPORT           本地址族不支持指定的套接口类型。
    WSAEINVAL                    g参数非法。
    展开全文
  • windowssocket编程

    2012-12-05 16:42:35
    关于socket的基础简介,适合于初级入门学习。
  • Windows Socket编程

    2009-09-09 15:02:26
    Windows Socket编程 一、基于TCP(面向连接)的socket编程 服务器端程序: 1、创建套接字(socket)。 2、将套接字绑定到一个本地地址和端口上(bind)。 3、将套接字设为监听模式,准备接受客户请求(listen...
    Windows Socket编程 
    一、基于TCP(面向连接)的socket编程
    服务器端程序:
    1、创建套接字(socket)。
    2、将套接字绑定到一个本地地址和端口上(bind)。
    3、将套接字设为监听模式,准备接受客户请求(listen)。
    4、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。
    5、用返回的套接字和客户端进行通信(send/recv)。
    6、返回,等待另一客户请求。
    7、关闭套接字。
    客户端程序:
    1、创建套接字(socket)。
    2、向服务器发出连接请求(connect)。
    3、和服务器端进行通信(send/recv)。
    4、关闭套接字。

    二、基于UDP(面向无连接)的socket编程
    服务器端(接受端)程序:
    1、创建套接字(socket)。
    2、将套接字绑定到一个本地地址和端口上(bind)。
    3、等待接收数据(recvfrom)。
    4、关闭套接字。
    客户端(发送端)程序:
    1、创建套接字(socket)。
    2、向服务器发送数据(sendto)。
    3、关闭套接字。
    其实对于建立连接后,可以相互发送信息,无所谓谁是服务器,谁是客户端。在开始的时候,一定是bind的一段,接收数据,这是死的。对于服务器和客户是在开始的时候划分的。


    三、建立MFC的控制台应用程序,编写基于TCP/IP的服务器端应用。

    1、int WSAStartup(
    WORD wVersionRequested,
    LPWSADATA lpWSAData
    );
    wVersionRequested的高位字节说明了socket的副版本号,低位字节说明了socket的主版本号。
    LPWSADATA lpWSAData 指向WSADATA数据结构的指针,其中包含多个字段。
    WSAStartup是socket应用程序调用的第一个函数,用于加载WindowsSockets版本号和WindowsSockets执行的细节参数。
    具体代码如下:
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    wVersionRequested = MAKEWORD( 1, 1 );
    err = WSAStartup( wVersionRequested, &wsaData );
    if ( err != 0 )return;

    2、创建socket套接字
    SOCKET socket(
    int af,
    int type,
    int protocol
    );
    socket函数产生一个socket描述符和相关资源,和特定的协议联系。
    af表示协议族,如果是TCP/IP or UDP协议,af等于AF_INET
    type表示使用什么类型的socket,有三种SOCK_STREAM,SOCK_DGRAM,SOCK_RAW
    如果是编写基于tcp的应用,使用SOCK_STREAM
    如果编写基于udp的应用,使用SOCK_DGRAM.
    protocol默认设为0。具体代码:
    SOCKET SockSrv=socket(AF_INET,SOCK_STREAM,0);
    ………127.0.0.1本地回路地址,在本机器上发消息……………………

    3、在服务器端将socket和地址、端口绑定。
    int bind(
    SOCKET s,
    const struct sockaddr FAR *name,
    int namelen
    );
    bind函数把产生的socket和指定的地址,端口联系起来,在服务器端使用。其中sockaddr(or SOCKADDR)用于存储ip地址结构(服务器的地址和端口),具体定义为:
    struct sockaddr {
    unsigned short sa_family;
    char sa_data[14];
    };
    sa_family表示使用的socket地址族,sa_data[14]定义了socket地址结构的最大存储单元。为了更详细的指定socket地址,可以使用SOCKADDR_IN 数据结构,它细分了TCP/IP Sockets 的地址字段,在程序中可以分别给每位字段赋预定的值,最后在bind时,进行SOCKADDR的强制转换,因为SOCKADDR和SOCKADDR_IN结构大小相等。
    struct sockaddr_in{
    short sin_family;
    unsigned short sin_port;
    struct in_addr sin_addr;
    char sin_zero[8];
    };
    struct in_addr {
    union {
    struct{
    unsigned char s_b1,
    s_b2,
    s_b3,
    s_b4;
    } S_un_b;
    struct {
    unsigned short s_w1,
    s_w2;
    } S_un_w;
    unsigned long S_addr;
    } S_un;
    };
    在sockaddr中除sin_family外,其他都是网络字节顺序表示,因此需要显示转换数据的字节顺序,把主机上的字节顺序变换为网络的字节顺序。其中u_short htons(
    u_short hostshort
    );
    是转换无符号短整型数。用于端口号的转换。
    u_long htonl(
    u_long hostlong
    );
    是转换无符号长整型数。用于S_addr的转换。
    namelen表示地址结构的长度。sizeof(sockaddr)即可得到。
    具体代码如下:
    SOCKADDR_IN addrSRV;
    addrSRV.sin_family=AF_INET;
    addrSRV.sin_addr.S_un.S_addr=ADDR_ANY;
    addrSRV.sin_port=htons(6000);
    bind(SockSrv,(SOCKADDR*)&addrSRV,sizeof(SOCKADDR));
    ADDR_ANY表示不详细指定具体的网卡ip地址,它也不需要字节顺序转换,因为值为0,对于非零值则一定需要转换。

    4、设置socket为监听模式
    int listen(
    SOCKET s,
    int backlog
    );
    s表示待设定的socket,backlog表示队列的最大等待数。
    具体代码如下:
    listen(SockSrv,10);

    5、开始接受来自客户端的请求
    SOCKET accept(
    SOCKET s,
    struct sockaddr FAR *addr,
    int FAR *addrlen
    );
    s是处于监听状态的socket,*addr是请求连接的客户端的地址信息,*addrlen 是客户端地址结构的长度。返回一个socket,然后可以使用返回的socket和客户端通信。
    具体代码如下:
    SOCKADDR_IN addrClient;
    int len=sizeof(SOCKADDR);
    SOCKET SockConn=accept(SockSrv,(SOCKADDR*)&addrClient,&len);
    这里的len必须进行初始化,否则会产生错误。

    6、发送信息给客户端
    int send(
    SOCKET s,
    const char FAR *buf,
    int len,
    int flags
    );
    s是应答上客户端的socket,即使用accept返回的socket。
    *buf保存要传送给客户端的信息,是一个字符指针。
    len保存字符数组的长度。
    flags指定函数调用的方式。默认使用0。
    具体代码如下:
    send(SockConn,"Welcome to www.sun.com!",sizeof("Welcome to www.sun.com!")+1,0);

    7、接收来自客户端的信息
    int recv(
    SOCKET s,
    char FAR *buf,
    int len,
    int flags
    );
    s是和客户端通信的socket
    *buf存放接收的信息
    len存放buf的大小
    flags指定函数的调用方式,默认为0。
    具体代码如下:
    char recvBuf[100];
    recv(SockConn,recvBuf,100,0);
    printf("%s\n",recvBuf);

    8、通信完毕后要关闭socket
    int closesocket(SOCKET s );
    具体代码如下:
    closesocket(SockConn);

    还要在程序的最后关闭WSA
    int WSACleanup (void);
    所以int WSAStartup(
    WORD wVersionRequested,
    LPWSADATA lpWSAData
    );
    和int WSACleanup (void);成对出现。
    对于以上的函数,要求包含Winsock2.h头文件,同时连接时使用Ws2_32.lib库(在工程->设置--->连接 中加入库文件);否则,编译运行会产生错误。


    9、总结:
    首先要了解socket编程的原理,在原理的指导下,每一个通信的步骤都对应Windows Socket的一个函数,调用函数完成预定的通信。

    四、编写基于TCP/IP的客户端应用程序。
    1、加载Windows Socket版本
    int WSAStartup(
    WORD wVersionRequested,
    LPWSADATA lpWSAData
    );
    typedef struct WSAData {
    WORD wVersion;
    WORD wHighVersion;
    char szDescription[WSADESCRIPTION_LEN+1];
    char szSystemStatus[WSASYS_STATUS_LEN+1];
    unsigned short iMaxSockets;
    unsigned short iMaxUdpDg;
    char FAR * lpVendorInfo;
    } WSADATA, *LPWSADATA;

    成功加载返回0。wVersionRequested的高位字节说明了socket的副版本号,低位字节说明了socket的主版本号。
    2、创建socket套接字
    SOCKET socket(
    int af,
    int type,
    int protocol
    );
    SOCKET SockClient=socket(AF_INET,SOCK_STREAM,0);

    3、使用socket套接字发送建立请求
    int connect(
    SOCKET s,
    const struct sockaddr FAR *name,
    int namelen
    );
    其中s是客户端的套接字,*name存储要连接到的服务器端的地址信息,在此函数之前要详细说明各字段的值。
    具体代码如下:
    SOCKADDR_IN SockSrv;
    SockSrv.sin_family=AF_INET;
    SockSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
    SockSrv.sin_port=htons(6000); connect(SockClient,(SOCKADDR*)&SockSrv,sizeof(SOCKADDR));
    inet_addr()是将ip地址字符串转换成适合IN_ADDR 结构的ip地址。

    4、接收来自服务器端的信息。
    具体代码如下:
    char recvBuf[100];
    recv(SockClient,recvBuf,100,0);
    printf("%s\n",recvBuf);

    5、发送信息给服务器。
    send(SockClient,"I am chenlei!",sizeof("I am chenlei!")+1,0);
    6、可以继续发送和接收。
    7、通信完毕关闭socket
    int closesocket(SOCKET s );
    具体代码如下:
    closesocket(SockClient);
    还有相关操作和服务器端的一样。
    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/chenlei5662/archive/2008/08/20/2801228.aspx
    展开全文
  • Windows socket编程

    2016-09-12 14:28:17
    其实就是一个无符号的整数,是一个socket描述符,我的理解就是 ,指向系统内核的”指针”,但这个”指针”要靠系统维护,我们只能获取,还要负责释放.   2 SOCKADDR_IN struct sockaddr_in{ short sin_family; ...

    一常用数据结构

    1 SOCKET

    typedef u_int           SOCKET

    其实就是一个无符号的整数,是一个socket描述符,我的理解就是 ,指向系统内核的”指针”,但这个”指针”要靠系统维护,我们只能获取,还要负责释放.

     

    2 SOCKADDR_IN

    struct sockaddr_in{ short sin_family; unsigned short sin_port; IN_ADDR sin_addr;

     char sin_zero[8];};

    用来指定本地或远程的地址,以此来进行socket连接

    sin_family:指定为AF_INET

    sin_port: 指定所连接的端口号,用htons将short的端口号进行转换,如htons(6000)

    sin_addr:指定所连接的ip,用inet_addr装换,如inet_addr(“127.0.0.1”)

    sin_zero:为了和SOCKADDR的大小相同,而进行的填充,应该将其初始化为0

     

     

    二 常用函数

    1 WSAStartup

    原型:

     int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData)

    作用:

    用来初始化ws2_32.lib

    返回值

    成功返回0

     

    2 WSACleanup

    终止ws2_32.lib

     

    3 socket

    原型:

    SOCKET socket (int af, int type, int protocol );

    参数说明

    af:指定地址家族,AF_INET

    type: SOCK_STREAMSOCK_DGRAM,前者用于TCP,后者用于UDP

    protocol:设置为0即可

    作用:

    创建一个SOCKET

    返回值

    创建失败返回INVALID_SOCKET

     

    4 bind

    原型:

    int bind( SOCKET s, const struct sockaddr FAR *name,int namelen );

    参数说明

    s:server端已经创建好的SOCKET

    name:server的SOCKADDR_IN结构,需要做强制类型转换,注意,他和SOCKADDR的大小一样

    namelen:顾名思义,name的size大小,注意他和SOCKADDR的大小一样

    作用

    SOCKET绑定到机器上

    返回值

    成功返回0,错误返回SOCKET_ERROR

     

    5 listen

    原型

    int listen(  SOCKET s,      int backlog  );

    参数说明

    s: 服务端未连接的SOCKET

    backlog:最大可连接数

    作用

    设置tcp服务端

    返回值

    成功返回0,失败返回SOCKET_ERROR

     

    6 accept

    原型

    SOCKET accept(  SOCKET s,  struct sockaddr FAR *addr,  int FAR *addrlen);

    参数说明

    s:tcp服务端的SOCKET

    addr:远程客户端的地址信息

    addlen:addrsize

    作用

    从已经获得许可的连接中关联一个新的SOCKET

    返回值

    成功返回新的SOCKET的值,错误返回INVALID_SOCKET

     

    7 send

    原型

    int send(SOCKET s, const char FAR *buf,  int len, int flags );

    参数说明

    s:对方的SOCKET

    buf:要发送的缓冲区的地址

    flags:通常置为0

    作用

    向对方发送数据

    返回值

    成功返回已发送的数据的字节数,错误返回SOCKET_ERROR

     

    8 recv

    原型

    int recv(  SOCKET s, char FAR *buf, int len, int flags       );

    参数说明

    s:对方的SOCKET

    buf:要发送的缓冲区的地址

    flags:通常置为0

    作用

    接受对方的数据

    返回值

    成功返回已接受的数据的字节数,错误返回SOCKET_ERROR

     

    9 closesocket

    原型

    int closesocket(  SOCKET s  );

    参数说明

    s:要关闭的SOCKET

    作用

    关闭指定的SOCKET

    返回值

    成功返回0,错误返回SOCKET_ERROR

     

    10 connect

    原型

    Int connect(SOCKET s,const struct sockaddr FAR *name,int namelen)                        );

    参数说明

    S:本地SOCKET

    name:远程服务端地址信息

    namelen:namesize

    作用

    远程socket连接

    返回值

    成功返回0,错误返回SOCKET_ERROR

     

    11 sendto

    原型

    int sendto(  SOCKET s, const char FAR *buf,  int len, int flags, const struct sockaddr FAR *to,    int tolen);                        );

    参数说明

    S:对方的SOCKET,可能是连接状态

    buf:要发送的信息地址

    len:信息的大小

    flags:通常置为0

    to:远程地址信息

    tolen:tosize

    作用

    用于UDP,数据的发送

    返回值

    成功返回已发送的数据的字节数,错误返回SOCKET_ERROR

     

    12 recvfrom

    原型

    int recvfrom(  SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR *from, int FAR *fromlen );

    参数说明

    S:对方的SOCKET,可能是连接状态

    buf:用于接收的缓冲区地址

    len:信息的大小

    flags:通常置为0

    from:接收远程地址信息

    fromlen:tosize

    作用

    用于UDP,数据的接收

    返回值

    成功返回已接收的数据的字节数,错误返回SOCKET_ERROR

     

    15 WSAGetLastError

    原型: int  WSAGetLastError (void);

    作用:返回最后一次的错误代码

    错误代码在WINSOCK2.H中定义

     

    14 一些转换函数

    ntohs:”network to host short”

    ntohl:”network to host long”

    htons:”host to network short”

    htonl:”host to network long”

    inet_addr:将字符串的IP转换为网络IP

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,239
精华内容 1,695
关键字:

windowssocket编程