DoubleLi

qq: 517712484 wx: ldbgliet

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

基于 TCP 的 socket 编程

/*
服务器端程序流程:
1.加载套接字库       WSAStartup
2.创建套接字           socket
3.将我们创建的套接字,绑定到本机地址的某一端口上     bind
4.为套接字设置监听模式,准备客户请求                          listen
5.等待客户请求到来。当请求到来,将接受连接请求,并返回一个新的对应于此次连接的套接字     accept
6.用新返回的套接字和客户端进行通信                             send / recv
7.在通信结束后,关闭套接字                                            closesocket


客户端程序流程:
1.加载套接字库                           WSAStartup
2.创建套接字                              socket
3.向服务器发出请求连接            connect
4.和服务器进行通信                   send / recv
5.在通信结束后,关闭套接字    closesocket
*/

 

 

服务器端代码:

  1. #include <Winsock2.h>   
  2. #include <stdio.h>   
  3.   
  4. #pragma comment(lib, "Ws2_32.lib")   
  5.   
  6. void main()  
  7. {  
  8.     // 加载套接字库,并进行套接字的版本协商   
  9.     WORD        wVersionRequested;  // 指定将要加载的 winsock 库版本   
  10.     WSADATA     wsaData;            // 用于存储加载的 winsock 库版本信息   
  11.     int         result;             // 用于检测 WSAStartup 函数运行结果   
  12.   
  13.     wVersionRequested   = MAKEWORD(1, 1);   // 设定版本   
  14.   
  15.     result = WSAStartup(wVersionRequested, &wsaData);  
  16.   
  17.     // 函数 WSAStartup 调用成功返回 0   
  18.     // 出错处理   
  19.     if (result != 0)                  
  20.     {  
  21.         return;  
  22.     }  
  23.   
  24.     if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)  
  25.     {  
  26.         WSACleanup();  
  27.         return;  
  28.     }  
  29.   
  30.     // 创建套接字   
  31.     SOCKET      sock    = socket(AF_INET, SOCK_STREAM, 0);  
  32.   
  33.     // 绑定套接字   
  34.     SOCKADDR_IN addrInfo;           // 存储本地主机地址信息   
  35.   
  36.     addrInfo.sin_addr.S_un.S_addr   = htonl(INADDR_ANY);    // 本地主机地址   
  37.     addrInfo.sin_port               = htons(6000);          // 端口号   
  38.     addrInfo.sin_family             = AF_INET;              // 地址族   
  39.   
  40.     bind(sock, (SOCKADDR *)&addrInfo, sizeof(SOCKADDR));  
  41.   
  42.     // 设置套接字监听模式   
  43.     listen(sock, 5);  
  44.   
  45.     SOCKADDR_IN     addrInfoClient; // 存储客户端地址信息   
  46.     int             len = sizeof(SOCKADDR);  
  47.   
  48.     while (true)  
  49.     {  
  50.         // 等待客户请求到来,并返回用于通信的套接字   
  51.         SOCKET  sockConnect = accept(sock, (SOCKADDR *)&addrInfoClient, &len);  
  52.   
  53.         // 下面通过刚建立的套接字,来进行通信   
  54.   
  55.         // 发送数据   
  56.         char    sendBuf[100];  
  57.         sprintf(sendBuf, "这是服务器端,主机地址:%s", inet_ntoa(addrInfo.sin_addr));  
  58.         send(sockConnect, sendBuf, strlen(sendBuf), 0);  
  59.   
  60.         // 接收数据   
  61.         char    recvBuf[100];  
  62.         recv(sockConnect, recvBuf, strlen(recvBuf), 0);  
  63.   
  64.         // 打印接收的数据   
  65.         printf("%s\n", recvBuf);  
  66.   
  67.         closesocket(sockConnect);  
  68.     }  
  69.   
  70. }  


 

客户端代码:

  1. #include <Winsock2.h>   
  2. #include <stdio.h>   
  3.   
  4. #pragma comment(lib,"Ws2_32.lib")   
  5.   
  6. void main()  
  7. {  
  8.     // 加载套接字库,并进行套接字的版本协商   
  9.     WORD        wVersionRequested;  // 指定将要加载的 winsock 库版本   
  10.     WSADATA     wsaData;            // 用于存储加载的 winsock 库版本信息   
  11.     int         result;             // 用于检测 WSAStartup 函数运行结果   
  12.   
  13.     wVersionRequested   = MAKEWORD(1, 1);   // 设定版本   
  14.   
  15.     result = WSAStartup(wVersionRequested, &wsaData);  
  16.   
  17.     // 函数 WSAStartup 调用成功返回 0   
  18.     // 出错处理   
  19.     if (result != 0)                  
  20.     {  
  21.         return;  
  22.     }  
  23.   
  24.     if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)  
  25.     {  
  26.         WSACleanup();  
  27.         return;  
  28.     }  
  29.   
  30.     // 创建套接字   
  31.     SOCKET      sockConnect = socket(AF_INET, SOCK_STREAM, 0);  
  32.   
  33.     // 向服务器发出连接请求   
  34.     SOCKADDR_IN     addrInfoServer;     // 存储服务器端地址信息   
  35.     addrInfoServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  
  36.     addrInfoServer.sin_port             = htons(6000);  
  37.     addrInfoServer.sin_family           = AF_INET;  
  38.   
  39.     // 向服务器发出连接请求   
  40.     connect(sockConnect, (SOCKADDR *)&addrInfoServer, sizeof(SOCKADDR));  
  41.   
  42.     // 接收数据   
  43.     char    recvBuf[100];  
  44.     recv(sockConnect, recvBuf, sizeof(recvBuf), 0);  
  45.     printf("%s\n", recvBuf);  
  46.   
  47.     // 发送数据   
  48.     char    sendBuf[100] = "这是客户端\n";  
  49.     send(sockConnect, sendBuf, sizeof(sendBuf) + 1, 0);  
  50.   
  51.     //关闭套接字   
  52.     closesocket(sockConnect);  
  53.   
  54.     WSACleanup();  
  55.   
  56.     system("pause");  
  57.     return;  
  58. }  


 

 ================================================

 

 

 基于 UDP 无连接的 socket 编程

/*

服务端程序流程:
1.加载套接字库       WSAStartup
2.创建套接字           socket
3.将创建的套接字绑定到一个本地地址和端口上     bind
4.等待接收数据。后与客户端实现实时交流            recvfrom / sendto
5.关闭套接字          closesocket

客户端程序流程:
1.加载套接字库     WSAStartup
2.创建套接字         socket
3.向服务器发送数据.后与服务端实现实时交流     recvfrom / sendto
4.关闭套接字        closesocket

*/

 

服务器端代码:

  1. #include <Winsock2.h>   
  2. #include <stdio.h>   
  3. #pragma comment(lib, "Ws2_32.lib")   
  4.   
  5. void main()  
  6. {  
  7.     // 加载套接字库,并进行套接字的版本协商   
  8.     WORD        wVersionRequested;  // 指定将要加载的 winsock 库版本   
  9.     WSADATA     wsaData;            // 用于存储加载的 wdnsock 库版本信息   
  10.     int         result;             // 用于检测 WSAStartup 函数运行结果   
  11.   
  12.     wVersionRequested = MAKEWORD(1, 1);     // 设定版本   
  13.   
  14.     result  = WSAStartup(wVersionRequested, &wsaData);  
  15.   
  16.     // 函数 WSAStartup 调用成功返回 0   
  17.     // 出错处理   
  18.     if (result != 0)  
  19.     {  
  20.         return;  
  21.     }  
  22.   
  23.     if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)  
  24.     {  
  25.         WSACleanup();  
  26.         return;  
  27.     }  
  28.   
  29.     // 创建用于套接字   
  30.     SOCKET  sockConnect = socket(AF_INET, SOCK_DGRAM, 0);  
  31.   
  32.     // 绑定套接字   
  33.     SOCKADDR_IN     addrInfo;       // 存储本地主机地址信息   
  34.   
  35.     addrInfo.sin_addr.S_un.S_addr   = htonl(INADDR_ANY);    // 本地主机地址     
  36.     addrInfo.sin_port               = htons(6000);          // 端口号     
  37.     addrInfo.sin_family             = AF_INET;              // 地址族     
  38.     
  39.     bind(sockConnect, (SOCKADDR *)&addrInfo, sizeof(SOCKADDR));    
  40.   
  41.     // 等待接收数据   
  42.     char    recvBuf[100];   // 接收数据缓冲   
  43.     char    sendBuf[100];   // 发送数据缓冲   
  44.     char    tempBuf[200];     
  45.   
  46.     SOCKADDR_IN     addrInfoClient; // 存储客户端地址信息   
  47.     int             len = sizeof(SOCKADDR);  
  48.   
  49.     while (true)  
  50.     {  
  51.         recvfrom(sockConnect, recvBuf, strlen(recvBuf), 0, (SOCKADDR *)&addrInfoClient, &len);  
  52.         if ('q' == recvBuf[0])  
  53.         {  
  54.             sendto(sockConnect, "q", strlen("q") + 1, 0, (SOCKADDR *)&addrInfoClient, len);  
  55.             printf("聊天结束");  
  56.             break;  
  57.         }  
  58.   
  59.         sprintf(tempBuf, "%s 说:%s", inet_ntoa(addrInfoClient.sin_addr), recvBuf);  
  60.         printf("%s\n", tempBuf);  
  61.   
  62.         // 发送数据   
  63.         printf("我说:");  
  64.         gets(sendBuf);  
  65.         sendto(sockConnect, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrInfoClient, len);  
  66.     }  
  67.   
  68.     // 关闭套接字   
  69.     closesocket(sockConnect);  
  70.     WSACleanup();  
  71.   
  72. }  

 

客户端代码:

  1. #include <Winsock2.h>   
  2. #include <stdio.h>   
  3. #pragma comment(lib, "Ws2_32.lib")   
  4.   
  5. void main()  
  6. {  
  7.     // 加载套接字库,并进行套接字的版本协商   
  8.     WORD        wVersionRequested;  // 指定将要加载的 winsock 库版本   
  9.     WSADATA     wsaData;            // 用于存储加载的 wdnsock 库版本信息   
  10.     int         result;             // 用于检测 WSAStartup 函数运行结果   
  11.   
  12.     wVersionRequested = MAKEWORD(1, 1);     // 设定版本   
  13.   
  14.     result  = WSAStartup(wVersionRequested, &wsaData);  
  15.   
  16.     // 函数 WSAStartup 调用成功返回 0   
  17.     // 出错处理   
  18.     if (result != 0)  
  19.     {  
  20.         return;  
  21.     }  
  22.   
  23.     if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)  
  24.     {  
  25.         WSACleanup();  
  26.         return;  
  27.     }  
  28.   
  29.     // 创建套接字   
  30.     SOCKET  sockConnect = socket(AF_INET, SOCK_DGRAM, 0);  
  31.   
  32.     // 向服务器发送数据   
  33.     SOCKADDR_IN     addrInfoServer;     // 存储服务器地址信息   
  34.   
  35.     addrInfoServer.sin_addr.S_un.S_addr   = inet_addr("127.0.0.1");     // 指定服务器地址   
  36.     addrInfoServer.sin_port               = htons(6000);                // 端口号     
  37.     addrInfoServer.sin_family             = AF_INET;                    // 地址族    
  38.   
  39.     int     len = sizeof(SOCKADDR);  
  40.   
  41.     char    recvBuf[100];   // 接收数据缓冲   
  42.     char    sendBuf[100];   // 发送数据缓冲   
  43.     char    tempBuf[200];     
  44.   
  45.     while (true)  
  46.     {  
  47.         // 发送数据   
  48.         printf("我说:");  
  49.         gets(sendBuf);  
  50.         sendto(sockConnect, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrInfoServer, len);  
  51.   
  52.         // 等待并接收数据   
  53.         recvfrom(sockConnect,recvBuf, strlen(recvBuf), 0, (SOCKADDR*)&addrInfoServer, &len);  
  54.         if ('q' == recvBuf[0])  
  55.         {  
  56.             sendto(sockConnect, "q", strlen("q") + 1, 0, (SOCKADDR*)&addrInfoServer, len);  
  57.             printf("聊天结束");  
  58.             break;  
  59.         }  
  60.   
  61.         sprintf(tempBuf, "%s 说:%s", inet_ntoa(addrInfoServer.sin_addr), recvBuf);  
  62.         printf("%s\n", tempBuf);  
  63.     }  
  64.   
  65.     // 关闭套接字   
  66.     closesocket(sockConnect);  
  67.     WSACleanup();  
  68.   
  69. }  


 

 


  1. vc网络编程常用类型解析:  
  2.   
  3. 1. SOCKET 类型  
  4. SOCKET 是 socket 套接字类型,在 WINSOCK2.H 中有如下定义:  
  5. typedef unsigned u_int;  
  6. typedef u_int    SOCKET;  
  7. 可知套接字实际上就是一个无符号整形,它将被 Socket 环境管理和使用。  
  8. 套接字将被创建、设置、用来发送和接收数据,最后会被关闭。  
  9.   
  10. 2.WORD 类型、MAKEWORD、LOBYTE、HIBYTE 宏  
  11. WORD 类型是一个 16 位的无符号整型, 在 WTYPES.H 中被定义为:  
  12. typedef unsigned short  WORD;  
  13. 其目的是提供两个字节的存储, 在 Socket 中这两个字节可以表示主版本号和副版本号。  
  14. 使用 MAKEWORD 宏可以给一个 WORD 类型赋值。例如要表示主版本号 2, 副版本号 0,可以使用如下代码:  
  15. WORD wVersionRequested;  
  16. wVersionRequested   = MAKEWORD(2, 0);  
  17. 注意低位内存存储主版本号 2, 高位内存存储副版本号 0,其值为 0x0002。  
  18. 使用宏 LOBYTE 可以读取 WORD 的低位字节, HIBYTE 可以读取高位字节。  
  19.   
  20. 3.WSADATA 类型和 LPWSADATA 类型  
  21. WSADATA 类型是一个结构,描述了 Socket 库的一些相关信息,其结构定义如下:  
  22.   
  23. typedef struct WSAData  
  24. {  
  25.     WORD        wVersion;                                 
  26.     WORD        wHighVersion;                             
  27.     char        szDescription[WSADESCRIPTION_LEN + 1];    
  28.     char        szSystemStatus[WSASYS_STATUS_LEN + 1];  
  29.     unsigned short  iMaxSockets;  
  30.     unsigned short  iMaxUdpDg;  
  31.     char    FAR*    lpVendorInfo;  
  32. }WSADATA;  
  33. typedef WSADATA FAR*    LPWSADATA;  
  34.   
  35. 值得注意的是 wVersion 字段,存储了 Socket 的版本类型。LPWSADATA 是 WSADATA 的指针类型。  
  36. 他们通过 Socket 的初始化函数 WSAStartup 读取出来。  
  37.   
  38. vc网络编程常用函数解析:  
  39.   
  40. 1. WSAStartup 函数  
  41. 用于初始化 Socket 环境,函数原型:  
  42. int WSAStartup(WORD wVersionRequested,  LPWSADATA lpWSAData);  
  43. 其返回值为整型,调用方式为 PASCAL (即标准类型,PASCAL 等于__stdcall),参数有两个,  
  44. 第一个参数为 WORD 类型,指明了 Socket 的版本号,第二个参数为 LPWSADATA,指向一个用于存储 Socket 库信息的WSAStartup结构。  
  45. 返回值:  
  46. 返回值为0,则初始化成功,若不为0则为失败。  
  47.   
  48. 2.WSACleanup 函数  
  49. 这是 Socket 环境的退出函数,函数原型:  
  50. int  WSACleanup (void);  
  51. 返回值:  
  52. 返回值为0表示成功,SOCKET_ERROR 表示失败。  
  53.   
  54. 3.socket 函数  
  55. socket 套接字的创建函数,函数原型:  
  56. SOCKET socket(int af, int type, int protocol  );  
  57. 第一个参数为:int af,      代表网络地址族,目前只有一种取值有效,即 AF_INET, 代表 internet 地址族;  
  58. 第二个参数为:int type,    代表网络协议类型, SOCK_DGRAM 代表 UDP 协议, SOCK_STREAM 代表 TCP 协议。  
  59. 第三个参数为:int protocol,指定网络地址族特殊协议,目前无用,赋值0即可。  
  60. 返回值:  
  61. 返回值为 SOCKET, 若返回INVALID_SOCKET 则失败。  
  62.   
  63. 4.bind 函数  
  64. 用于将套接字绑定到一个已知地址上,函数原型:  
  65. int bind(SOCKET s, const struct sockaddr FAR *name, int namelen);  
  66. 第一个参数为:SOCKET s,            指定将被绑定的套接字。  
  67. 第二个参数为:SOCKADDR_IN *name,   是一个sockaddr结构指针,该结构中包含了要绑定的地址和端口。  
  68. 第三个参数为:int  namelen,         确定第二个参数的结构长度。  
  69.   
  70. 返回值:   成功返回0,失败返回SOCKET_ERROR。  
  71.   
  72. 下面对其涉及的类型作一番解析:  
  73. sockaddr 类型:  
  74. sockaddr 类型是用来表示 Socket 地址的类型,同上面的 socketaddr_in 类型相比,sockaddr 的适用范围更广,  
  75. 因为sockeaddr_in只适用于 TCP/IP 地址。sockaddr 的定义如下:  
  76. struct sockaddr  
  77. {  
  78.     ushort  sa_family;  
  79.     char    sa_data[14];  
  80. };  
  81. 可知sockaddr 的16个字节,而sockaddr_in也有16个字节,所以sockaddr_in是可以强制类型转换为sockadddr的。  
  82. 事实上也往往使用这种方法。  
  83.   
  84. sockaddr_in 定义了socket发送和接收数据包的地址,其定义如下:  
  85. strucr  sockaddr_in  
  86. {  
  87.     short       sin_family;  
  88.     u_short      sin_port;  
  89.     struct in_addr  sin_addr;  
  90.     char        sin_zero[8];  
  91. };  
  92.   
  93. 其中 in_addr 定义如下:  
  94. struct in_addr  
  95. {  
  96.     union  
  97.     {  
  98.         struct {u_char   s_b1, s_b2, s_b3, s_b4} S_un_b;  
  99.         struct {u_short  s_w1, s_w2} S_un_w;  
  100.         u_long S_addr;  
  101.     }S_un;  
  102. };  
  103. 首先阐述 in_addr 的信义。  
  104. 很显然它是一个存储 ip 地址的联合体,有三种表达方式:  
  105. 第一种用四个字节来表示IP地址的四个数字;  
  106. 第二种用两个双字节来表示IP地址;  
  107. 第三种用一个长整型来表示IP地址;  
  108. 给 in_addr 赋值的一种最简单方法是使用 inet_addr 函数, 它可以把一个代表IP地址的字符串赋值  
  109. 转换为in_addr类型。如:  
  110. addrServer.sin_addr = inet_addr("192.168.0.2");  
  111. 其反函数是 inet_ntoa,可以把一个 in_addr 类型转换为一个字符串。  
  112. sockaddr_in的含义比in_addr的含义要广泛,其各个字段的含义和取值如下:  
  113. 第一字段 short           sin_family,代表网络地址族,如前所述,只能取值AF_INET;  
  114. 第二字段 u_short         sin_port,  代表IP地址端口,由程序员指定;  
  115. 第三字段 struct in_addr  sin_addr,  代表IP地址;  
  116. 第四个字段char sin_zero[8],是为了保证sockaddr_in与SOCKADDR类型的长度相等而填充进来的字段。  
  117.   
  118. 5.listen 函数  
  119. 该函数让一个套接字在指定IP地址的指定端口处监听连接请求的到来,函数原型:  
  120. int listen(  SOCKET s,      int backlog  );  
  121. 该函数使得一个进程可以接受其他进程的请求,从而成为一个服务器进程。  
  122. 在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。  
  123. listen 函数一般在调用bind之后、调用accept之前调用。  
  124. 返回值:  成功则返回0,失败返回SOCKET_ERROR,可以调用函数WSAGetLastError来取得错误代码。  
  125.   
  126.   
  127. 6.accept函数  
  128. 该函数从连接请求队列中获得连接信息,并创建新的套接字用于收发数据,实现服务器与客户端的通信。函数原型:  
  129. SOCKET accept(SOCKET s,  struct sockaddr FAR *addr,  int FAR *addrlen);  
  130. 第一个参数:SOCKET          s,      监听套接字  
  131. 第二个参数:struct sockaddr addr,   存储请求连接的客户端IP地址、端口信息  
  132. 第三个参数:int             addrlen,第二个参数所占空间大小  
  133. 返回值:  
  134. 成功返回新套接字,失败返回错误信息  
  135.   
  136.   
  137. 7.connect 函数  
  138. 向指定的网络主机请求连接,函数原型:  
  139. int connect(SOCKET s, const struct sockaddr FAR *name, int namelen);  
  140. 第一个参数:SOCKET           s,       客户端用于收发数据的套接字。  
  141. 第二个参数:struct sockaddr  *name,   指定网络主机IP地址和端口号。  
  142. 第三个参数:int              namelen, 第二参数长度  
  143. 返回值:  
  144. 成功返回0,失败返回-1。  
  145.   
  146. 8.sendto、recvfrom、send、recv函数  
  147. 在 Socket 中有两套发送和接收函数。一是sendto 和recvfrom; 二是send 和 recv。  
  148. 前一套在函数参数中要指明地址(UDP协议),  
  149. 而后一套需要先将套接字和一个地址绑定,然后直接发送和接收,不需绑定地址。  
  150. 函数原型:  
  151. int sendto(  SOCKET s, const char FAR *buf, int len, int flags, const struct sockaddr FAR *to,   int      tolen);  
  152. int recvfrom(SOCKET s,       char FAR* buf, int len, int flags, struct sockaddr FAR       *from, int FAR *fromlen);  
  153.   
  154. int send(SOCKET s,const char FAR *buf,  int len, int flags);  
  155. int recv(SOCKET s,      char FAR *buf,  int len, int flags);  
  156.   
  157. 第一个参数: 套接字  
  158. 第二个参数: 数据指针  
  159. 第三个参数: 数据长度  
  160. 第四个参数: 收发数据方式的标识,如果不需要特殊要求可以设置为0,其他值可参考MSDN;  
  161. 第五个参数: 目标主机地址  
  162. 第六个参数: 地址的长度  
  163.   
  164. 返回值:   运行成功则返回收发数据的字节数,失败返回SOCKET_ERROR  
  165.   
  166. 9.closesocket 函数  
  167. 关闭套接字,函数原型:  
  168. int closesocket(  SOCKET s  );  
  169. 返回值:  成功返回0,失败返回SOCKET_ERROR。  

 

from:http://blog.csdn.net/ltag0110rtag/article/details/7390304

posted on 2012-07-19 09:58  DoubleLi  阅读(2456)  评论(0编辑  收藏  举报