win32下的socket编程

被刻印的时光 ゝ 提交于 2020-02-12 07:21:51
// socket.cpp : 定义控制台应用程序的入口点。
//
//服务器端


//SOCKET连接过程
  //根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。   
//服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。  
//客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。
           //为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。   
//连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。
              //而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
  //如何开发一个Server-Client模型的程序


//开发原理:   
  //服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。  
  // 客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。  
  // Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。
    //要学Internet上的TCP/IP网络编程,必须理解Socket接口。Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。
  //网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。
  //Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

//常用的Socket类型
  //有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。
    //流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;
    //数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。

//Socket 阻塞与非阻塞模式

//Windows套接字在阻塞和非阻塞两种模式下执行I/O操作。
//在阻塞模式下,在I/O操作完成前,执行的操作函数一直等候而不会立即返回,该函数所在的线程会阻塞在这里。
//相反,在非阻塞模式下,套接字函数会立即返回,而不管I/O是否完成,该函数所在的线程会继续运行。
#pragma comment(lib, "Ws2_32.lib")
#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <WS2tcpip.h>
#include <process.h>

using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    WORD wVersionRequested;//typedef unsigned short WORD
    WSADATA wsaData;//这个结构被用来存储 被WSAStartup函数调用后返回的 Windows Sockets数据
    int err;

    wVersionRequested = MAKEWORD(1,1);
//     WORD MAKEWORD(
//         BYTE bLow,
//         BYTE bHigh
//         );
     err = WSAStartup(wVersionRequested,&wsaData);//WSAStartup,即WSA(Windows SocKNDs Asynchronous,Windows异步套接字)的启动命令。
     //是Windows下的网络编程接口软件Winsock1 或 Winsock2 里面的一个命令
     //这个函数是用来加载Winsocket DLL,wVersionRequested是用来存储你所要申请的Winsocket DLL版本,
     //可以通过MAKEWORD函数获取,wVersionRequested的高位代表副版本号,低位代表高版本号
     //     int WSAStartup(
     //         __in          WORD wVersionRequested,
     //         __out         LPWSADATA lpWSAData
     //         );

     if(err != 0)//返回0表示成功
     {
         return 0;
     }
     if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)//高位字节指出副版本(修正)号,低位字节指明主版本号。/* Confirm that the Windows Sockets DLL supports 1.1.*/ 
     {
         /* Tell the user that we couldn't find a useable  winsock.dll. */ 
         WSACleanup();//终止Winsock 2 DLL (Ws2_32.dll) 的使用.
         return 0;
     }
     SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0);
     //int socket(int domain, int type, int protocol); 该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET
     SOCKADDR_IN addrSrv;//sockaddr_in和sockaddr是并列的结构
//      struct sockaddr_in 
//      {
//            short int sin_family; /* Address family */  
//             unsigned short int sin_port; /* Port number */   
//             struct in_addr sin_addr; /* Internet address */   
//             unsigned char sin_zero[8]; /* Same size as struct sockaddr */  
//       }; 
     //该结构体用于指定一个socket的一端【ip+port】
     addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
     //所以local.sin_addr.s_addr是ip地址。作为服务器,你要绑定【bind】到本地的IP地址上进行监听【listen】,
     //但是你的机器上可能有多块网卡,也就有多个IP地址,这时候你要选择绑定在哪个IP上面,如果指定为INADDR_ANY,那么系统将绑定默认的网卡【即IP地址】。
     //作为客户端,你要连接【connect】到远端的服务器,也是要指定远端服务器的(ip, port)对。
     //当然,在这种情况下,不可能将IP地址指定为INADDR_ANY,系统会疯掉的。
     addrSrv.sin_family = AF_INET;
     //Winsock2.h中   #define AF_INET 0   #define PF_INET AF_INET 
     //AF 表示ADDRESS FAMILY 地址族   
     //PF 表示PROTOCOL FAMILY 协议族   
        // 但这两个宏定义是一样的   
         //所以使用哪个都没有关系 
     addrSrv.sin_port = htons(6000);
     //htons 是把你机器上的整数转换成“网络字节序”, 网络字节序是 big-endian,也就是整数的高位字节存放在内存的低地址处
     bind(sockSrv,(SOCKADDR*) &addrSrv,sizeof(SOCKADDR));
     //将套接字绑定于特定地址的特定端口,其中第二个参数可以使用SOCKADDR_IN来代替。
//      int bind(
//          SOCKET s,
//          const struct sockaddr FAR* name,
//          int namelen
//          );
     //参数说明:   socket:是一个套接字。   
     //address:是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号。  
     // address_len:确定address缓冲区的长度。
     

     listen(sockSrv,5);//这个函数一般用于服务器端,这里的第二个参数为请求队列的最大程度,注意,不是最大连接数目
//      int listen(
//          __in          SOCKET s,
//          __in          int backlog
//          );

     
     SOCKADDR_IN addrClient;//与sockaddr等价的数据结构
     int len = sizeof(SOCKADDR);
//      struct sockaddr
//      {
//          unsignedshort sa_family;
//          char sa_data[14];
//      };
         
    while(1)
    {
        SOCKET sockConn = accept(sockSrv,(SOCKADDR *) &addrClient,&len);//从连接请求队列中获得连接信息,创建新的套接字,并返回该套接字的文件描述符。新创建的套接字用于服务器与客户机的通信,而原来的套接字仍然处于监听状态。 

        //accept一样主要用于服务器端,第二个参数同样可以使用SOCKADDR_IN来替代,但是注意,这里,该参数是用来存储建立连接时候客户端的相关信息。

//         SOCKET accept(
//             SOCKET s,
//         struct sockaddr FAR* addr,
//             int FAR* addrlen
//             );
        //服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,
        //并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,
        //以后与客户套接字交换数据的是新创建的套接字;如果失败就返回 INVALID_SOCKET。
        //该函数的第一个参数指定处于监听状态的流套接字;
        //操作系统利用第二个参数来返回所连接的客户进程的协议地址(由cliaddr指针所指);
        //操作系统利用第三个参数来返回该地址(参数二)的大小。
        //如果我们对客户协议地址不感兴趣,那么可以把cliaddr和addrlen均置为空指针NULL。 
        char sendbuffer[100];
        sprintf(sendbuffer,"welcome %s here",inet_ntoa(addrClient.sin_addr));//将一个IP转换成一个互联网标准点分格式的字符串。 原型:char FAR * inet_ntoa( struct in_addr in); 
        send(sockConn,sendbuffer,strlen(sendbuffer)+1,0);//该函数用来相互发送数据,但是需要注意的是,服务器端使用该函数时候,第一个参数为accept函数所返回的socket结构值。
//         int send(
//             SOCKET s,
//             const char FAR* buf,
//             int len,
//             int flags
//             );
        char recvchar[100];
        recv(sockConn,recvchar,100,0);//本函数用于已连接的数据报或流式套接口s进行数据的接收
        printf("%s\n",recvchar);
        closesocket(sockConn);//本函数关闭一个套接口。更确切地说,它释放套接口描述字s,以后对s的访问均以WSAENOTSOCK错误返回。
    }
    return 0;
}
// socket_client.cpp : 定义控制台应用程序的入口点。
//
#pragma comment(lib, "Ws2_32.lib")
#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
#include <WS2tcpip.h>
#include <process.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(1,1);
    err = WSAStartup(wVersionRequested,&wsaData);//加载Winsocket DLL
    if(err != 0)
    {
        return 0;
    }

    if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
    {
        WSACleanup();
        return 0;
    }

    SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);//创建套接字
    SOCKADDR_IN addrSrv;//socketAddress socket端口
    //服务器端口配置
    addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    ////作为客户端,你要连接【connect】到远端的服务器,也是要指定远端服务器的(ip, port)对。
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(6000);

    connect(sockClient,(SOCKADDR *) &addrSrv,sizeof(SOCKADDR));

    char recvBuffer[100];
    recv(sockClient,recvBuffer,100,0);//接收服务器数据,存入recvBuffer
    printf("%s\n",recvBuffer);//打印服务器数据
    send(sockClient,"This is Kary",strlen("This is Kary")+1,0);//向服务器发送数据"This is Kary"
    closesocket(sockClient);
    WSACleanup();
    scanf("%d",&err);
    return 0;
}

出现问题:不加#pragma comment(lib, "Ws2_32.lib")出现错误

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!