1 计算机网络基础
1.1 OSI参考模型(略)
1.2 TCP/IP协议
传输控制协议/网际协议,TCP/IP将网络分为四层,从高往低一次为应用层-》传输层-》网络层-》数据链路层。其中,传输层协议包括TCP,UDP;网络层协议包括IP、ARP等;
2 套接字编程基础
2.1 套接字
套接字是网络编程的基础,最初由加利福尼亚大学为UNIX开发的网络通信编程接口,为了在WINDOWS 操作系统上使用,微软与第三方厂商共同制定了一套标准,Winsock.。
套接字实际上是一个指向传输提供者的句柄,Winsock中通过该句柄实现网络通信与管理。
可以理解为实现网络通信和管理的操作接口。
2.1.1 套接字类型
根据套接字作用不同,分为三类:原始套接字、流式套接字和数据包套接字。
原始套接字(SOCK_RAW):能够使程序人员对底层的网络机制进行控制,接收的数据包头中含有IP头。
流式套接字(SOCK_STREAM):提供了双向、有序、可靠的数据传输服务,在通信前需要双方建立连接,TCP协议采用了该套接字。
数据包套接字(SOCK_DGRAM):与流式套接字相对应,提供双向数据流,但是不能保证数据传输的可靠性、有序性和无重复性。UDP协议采用了该套接字。
//2.2 套接字I/O模型
(暂缺)
2.3 套接字函数
为了使用套接字进行网络程序开发,Windows操作系统提供了一组套接字函数,使用这些函数,可以实现功能强大的网络编程。常用可分为三大类:初始化库函数(WSAStartup()、WSACleanup())、通信时使用函数(socket()、bind()、listen()、accept()、closesocket()、connect()、recv()、send()、select()、ioctlsocket())和地址转换函数(htons()、htonl()、inet_addr())。
其中,WSAStartup()用于初始化动态链接库函数,WSACleanup()用于释放动态链接库初始化时分配的资源。
注:套接字函数通常封装在Ws2_32.dll动态链接库中,其头文件Winsock2.h提供了套接字函数的原型,库文件Ws2_32.lib提供了Ws2_32.dll文件的输出节,在使用套接字函数前,需包含头文件 ,并链接Ws2_32.lib。
#include <winsock2.h> #pragma comment(lib,"ws2_32.lib")
3 网络编程步骤
实现网络间通信,需要一个服务器端和一个客户端。这两者之间的具体实现有点不同,大体步骤为:
3.1 服务器端
(1)创建套接字:socket()创建套接字,指明传输协议
例如:
sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//sListen代表返回的套接字 //AF_INET为IPV4地址家族
//SOCK_STREAM为套接字类型 //IPPROTO_TCP指明传输协议
(2)构建本地地址信息
//构建本地地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序 saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址
(3)bind()函数把一个地址族中的特定地址赋给socket。
(4)监听:lidten()
(5)等待连接:accept()
3.2 客户端
(1)创建套接字
(2)构建服务器地址
(3)连接服务器connect()
4 实例
下面通过一个简单的客户端/服务器端程序,讲解通信编程的具体实现:
//服务器端 // Server.cpp : 定义控制台应用程序的入口点。 //TCP测试程序的服务器端程序 #include "stdafx.h" #include <stdio.h> #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") #define SERVER_PORT 5208 //侦听端口 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; int ret, nLeft, length; SOCKET sListen, sServer; //侦听套接字,连接套接字 struct sockaddr_in saServer, saClient; //地址信息 char *ptr;//用于遍历信息的指针 //WinSock初始化 wVersionRequested=MAKEWORD(2, 2); //希望使用的WinSock DLL 的版本 ret=WSAStartup(wVersionRequested, &wsaData); if(ret!=0) { printf("WSAStartup() failed!\n"); //return 0; } //创建Socket,使用TCP协议 sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sListen == INVALID_SOCKET) { WSACleanup(); printf("socket() faild!\n"); //return 0; } //构建本地地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序 saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址 //绑定 ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer)); if (ret == SOCKET_ERROR) { printf("bind() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //关闭套接字 WSACleanup(); //return 0; } //侦听连接请求 ret = listen(sListen, 5); if (ret == SOCKET_ERROR) { printf("listen() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //关闭套接字 //return 0; } printf("Waiting for client connecting!\n"); printf("Tips: Ctrl+c to quit!\n"); //阻塞等待接受客户端连接 while(1)//循环监听客户端,永远不停止 { length = sizeof(saClient); sServer = accept(sListen, (struct sockaddr *)&saClient, &length); if (sServer == INVALID_SOCKET) { printf("accept() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //关闭套接字 WSACleanup(); return 0; } char sendMessage[]="\nhello client"; //发送信息给客户端 send(sServer,sendMessage,strlen(sendMessage)+1,0); char receiveMessage[5000]; nLeft = sizeof(receiveMessage); ptr = (char *)&receiveMessage; while(nLeft>0) { //接收数据 ret = recv(sServer, ptr, 5000, 0); //非负,成功;-1,失败 if (ret == SOCKET_ERROR) { printf("recv() failed!\n"); return 0; } if (ret == 0) //客户端已经关闭连接 { printf("Client has closed the connection\n"); break; } nLeft -= ret; ptr += ret; } printf("receive message:%s\n", receiveMessage);//打印我们接收到的消息。 } // closesocket(sListen); // closesocket(sServer); // WSACleanup(); return 0; }
//TCP测试程序的客户端程序 #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") #define SERVER_PORT 5208 //侦听端口 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; //WSADATA为Windows套接字结构体 int ret; SOCKET sClient; //连接套接字 struct sockaddr_in saServer; //服务器地址信息 char *ptr; BOOL fSuccess = TRUE; //--------------------------------------------------------------- //WinSock初始化 //--------------------------------------------------------------- wVersionRequested = MAKEWORD(2, 2); //希望使用的WinSock DLL的版本 ret = WSAStartup(wVersionRequested, &wsaData); //加载套接字库 if(ret!=0) { printf("WSAStartup() failed!\n"); //return 0; } //确认WinSock DLL支持版本2.2 if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2) { WSACleanup(); //释放为该程序分配的资源,终止对winsock动态库的使用 printf("Invalid WinSock version!\n"); //return 0; } //创建Socket,使用TCP协议 sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sClient == INVALID_SOCKET) { WSACleanup(); printf("socket() failed!\n"); //return 0; } //构建服务器地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意转化为网络节序 saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //连接服务器 ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer)); if (ret == SOCKET_ERROR) { printf("connect() failed!\n"); closesocket(sClient); //关闭套接字 WSACleanup(); //return 0; } char sendMessage[]="hahaha!!!"; ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0); if (ret == SOCKET_ERROR) { printf("send() failed!\n"); } else printf("client info has been sent!"); char recvBuf[100]; recv(sClient,recvBuf,100,0); printf("%s\n",recvBuf); closesocket(sClient); //关闭套接字 WSACleanup(); //getchar(); 没啥用,让你最后在显示终端可以输入一串字符,但是不能发送 //return 0; }