winsocket入门学习
参考资料:http://c.biancheng.net/cpp/socket/
http://www.winsocketdotnetworkprogramming.com/
socket 是“套接字”意思,是计算机之间进行通信的一种约定。
通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
学习 socket,也就是学习计算机之间如何通信,并编写出实用的程序。
WD--返回WinSock的实现信息。
WD是一个WSAData结构:
typedef struct WSAData { WORD wVersion; WORD wHighVersion; #ifdef _WIN64 unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; char szDescription[WSADESCRIPTION_LEN+1]; char szSystemStatus[WSASYS_STATUS_LEN+1]; #else char szDescription[WSADESCRIPTION_LEN+1]; char szSystemStatus[WSASYS_STATUS_LEN+1]; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; #endif } WSADATA, FAR * LPWSADATA;
最简单的WinSocket程序
server.c
#define _WINSOCK_DEPRECATED_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") #define BUFFER_SIZE 1024 int main() { WSADATA wsaData; //包含系统所支持的WinSocket版本信息 SOCKET sServer; //服务器Socket,用于监听客户端请求 SOCKET sClient; //客户端Socket,用于实现与客户端的通信 int retVal; //调用各种Socket函数的返回值 char buf[BUFFER_SIZE]; //用于接受客户端数据的缓冲区 //初始化WinSocket 2.2 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup failed!\n"); return -1; } /** * 打印socket信息 * 低位字节存储主版本号,高位字节存储副版本号 */ printf("[wVersion] = %d.%d\n", LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); printf("[wHighVersion] = %d.%d\n", LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion)); printf("[szDescription] = %s\n", wsaData.szDescription); printf("[szSystemStatus] = %s\n", wsaData.szSystemStatus); printf("[iMaxSockets] = %d\n", wsaData.iMaxSockets); printf("[iMaxUdpDg] = %d\n", wsaData.iMaxUdpDg); //创建用于监听的socket sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sServer == INVALID_SOCKET) { printf("socket failed!\n"); WSACleanup(); return -1; } /** * 设置服务器socket地址 * 服务器监听地址为INADDR_ANY-->在任意本地地址(0.0.0.0)监听 * 监听端口号为9999 */ SOCKADDR_IN addrServ; addrServ.sin_family = AF_INET; addrServ.sin_port = htons(9999); addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //绑定Sockets Server到本地地址 retVal = bind(sServer, (const struct sockaddr*)&addrServ, sizeof(SOCKADDR_IN)); if (retVal == SOCKET_ERROR) { printf("bind failed!\n"); closesocket(sServer); WSACleanup(); return -1; } //在Sockets Server上进行监听 retVal = listen(sServer, 1); if (retVal == SOCKET_ERROR) { printf("listen failed!\n"); closesocket(sServer); WSACleanup(); return -1; } //接受来自客户端的请求 printf("TCP Server start...\n"); struct sockaddr_in addrClient; int addrClientlen = sizeof(addrClient); sClient = accept(sServer, (struct sockaddr FAR *)&addrClient, &addrClientlen); if (sClient == INVALID_SOCKET) { printf("accept failed!\n"); closesocket(sServer); WSACleanup(); return -1; } //printf("%s\n", inet_ntoa(addrClient.sin_addr)); while (1) { //清空接收数据的缓冲区 memset(buf, 0, sizeof(buf)); retVal = recv(sClient, buf, BUFFER_SIZE, 0); if (retVal == SOCKET_ERROR) { printf("recv failed\n"); closesocket(sServer); closesocket(sClient); WSACleanup(); return -1; } SYSTEMTIME st; GetLocalTime(&st); char szDateTime[50]; sprintf(szDateTime, "%4d-%02d-%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); //打印输出信息 printf("%s, Recv From Client [%s:%d] :%s\n", szDateTime, inet_ntoa(addrClient.sin_addr), addrClient.sin_port, buf); if (strcmp(buf, "quit") == 0) { send(sClient, "quit", strlen("quit"), 0); break; } else { char msg[BUFFER_SIZE]; sprintf(msg, "Message received - %s", buf); retVal = send(sClient, msg, strlen(msg), 0); if (retVal == SOCKET_ERROR) { printf("send failed\n"); closesocket(sServer); closesocket(sClient); WSACleanup(); return -1; } } } //清除工作 closesocket(sServer); closesocket(sClient); WSACleanup(); return 0; }client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") #define BUFFER_SIZE 1024 int main() { WSADATA wsaData; SOCKET sHost; int retVal; char buf[BUFFER_SIZE]; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup failed!\n"); return -1; } sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sHost == INVALID_SOCKET) { printf("socket failed!\n"); WSACleanup(); return -1; } SOCKADDR_IN addrServ; addrServ.sin_family = AF_INET; addrServ.sin_port = htons(9999); addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); retVal = connect(sHost, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN)); if (retVal == SOCKET_ERROR) { printf("connect failed!\n"); closesocket(sHost); WSACleanup(); return -1; } while (1) { printf("Please input a string:"); gets(buf); retVal = send(sHost, buf, strlen(buf), 0); if (retVal == SOCKET_ERROR) { printf("send failed!\n"); closesocket(sHost); WSACleanup(); return -1; } memset(buf, 0, sizeof(buf)); retVal = recv(sHost, buf, sizeof(buf), 0); if (retVal == SOCKET_ERROR) { printf("recv failed!\n"); closesocket(sHost); WSACleanup(); return -1; } printf("Recv From Server: %s\n", buf); if(strcmp(buf, "quit") == 0) { break; } } //วๅณนคื๗ closesocket(sHost); WSACleanup(); return 0; }
对于完成的client/server程序可以使用NC来测试
1)连接到 remote 主机,例子: 格式:nc -nvv 192.168.x.x 80 讲解:连到192.168.x.x的TCP80端口 2)监听 local 主机,例子: 格式:nc -l -p 80 讲解:监听本机的TCP80端口 |
Keep it simple!