简单完成一个点对点通讯的基本操作:
一切皆socket。之前上课讲的socket编程,听的云里雾里,就听懂这一句。今天用一下午写了一个简单windows对windows的API通信的应用。在这里整理一下。配置:
客户机: windows
服务器: windows ip :192.168.1.4
环境: dev
网络: 同一局域网下:客户机WLAN,服务器有线宽带
首先 windows下socket和linux下socket有共同点也有很多不一致的地方。他有很多函数的一些细节不一样。
//error //windows WSAGetLastError() //linux #include
错误信息
保存在全局的errno变量 //linux不初始化 Windows初始化
启动时需要用WSAStartup(),退出时需要WSACleanup( ) //关闭 linux –> close(socketfd)
windows –> closesocket(socketfd) //socket类型不同 linux –> int类型
//select函数的第一个参数nfds在windows其实是没有意义 linux –> maxfd,文件描述符的范围
等等……
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。(百度百科
简而言之,我的理解是套接字是和linux一切皆文件的思想类似的。他把数据当成文件IO到网络中读写通信。socket提供的接口函数就是类似对文件的操作。如何定位到另一台电脑就用ip+端口的形式。
具体流程:
我们在一个局域网内 通过ip定位到另一台电脑,通过端口定位到另一台电脑的某个程序。然后通过socket接口的函数访问另一台电脑,发送数据、接收数据(因为局域网所以只能自己玩,自己的无线或者校园网应该就可以
注意
/dev需要设置编译选项。工具->编译选项->编译时加入以下命令打勾->输入 -lwsock32
编译前加入 #pragma comment(lib, “ws2_32.lib”)
windows用SOCKET类型标识SOCKET linux用int
windows 需要初始化
WSAStartup()和WSACleanup()成对出现。
linux的clean换成cleansocket 等等。
突然不想写了 看比赛去 告辞
贴代码了,代码内注释写的很清楚
客户端
#include
#include //包含了winsock.h
#pragma comment(lib, "ws2_32.lib")//引入winsock2.h
#define Port 5000//定义服务器端口号
#define IP_ADDRESS "192.168.1.4"//服务器ip
using namespace std;
int main(int argc, char* argv[])
{
WSADATA s; // WSAData结构
SOCKET ClientSocket;
struct sockaddr_in severaddr; // sockaddr_in型的结构体 存放地址
int ret = 0;
char SendBuffer[MAX_PATH]; // 默认260
//一、初始化Windows Socket //每个Winsock程序必须使用WSAStartup载入合适的Winsock动态链接库
int op=0;op= WSAStartup(MAKEWORD(2, 2), &s);// 参数一是WORD wVersionRequested、参数二是LPWSADATA lpWSAData 是 指向WSAData结构的指针
// MAKEWORD连接两个无符号参数,首个参数为低字节 表示调用winsock2.2版本
if ( op!= 0) //WSAStartup会向该结构中填充其载入的Winsock动态链库的信息。
{
printf("Init Windows Socket Failed! Error: %d\n", GetLastError());
getchar();
return -1;
}
cout<<"Init Windows Socket Success"<<endl;
while (1)
{
// 创建一个套接口
// 如果这样一个套接口用connect()与一个指定端口连接
// 则可用send()和recv()与该端口进行数据报的发送与接收
// 当会话结束后,调用closesocket()
//二、创建一个socket描述符 类似file 使用socket函数
//注//socket函数 windows下返回scoket类型 linux返回int
ClientSocket = socket(AF_INET, // IP 地址类型 ipv4 //函数原型SOCKET socket(int af, int type, int protocol);
SOCK_STREAM, //套接字类型SOCK_STREAM 流格式/面向连接的####SOCK_DGRAM 数据报/无连接的
IPPROTO_TCP); //传输协议 常见TCP和 UDP
if (ClientSocket == INVALID_SOCKET)//无效套接字
{
printf("Create Socket Failed! Error: %d\n", GetLastError());// windows下错误处理GetLastError() linux 要#include 错误信息保存在全局的errno变量
getchar();
return -1;
}
cout<<"Create Socket Success"<<endl;
//三、初始化服务器地址severaddr 为sockaddr_in类型
severaddr.sin_family = AF_INET;// IP 地址类型 ipv4
severaddr.sin_addr.s_addr = inet_addr(IP_ADDRESS); //IP地址 inet_addr将一个点分十进制的IP转换成一个长整数型数
severaddr.sin_port = htons(Port); // 端口号 、 // 将主机的无符号短整形数转换成网络字节顺序
memset(severaddr.sin_zero, 0X00, 8); // zero不使用,对齐作用 memset新申请的内存做初始化工作
//四、客户端调用connect()发出连接请求
ret = connect(ClientSocket, //客户端的socket描述字 参数一
(struct sockaddr*)&severaddr,//服务器的socket地址 参数二
sizeof(severaddr)); //socket地址的长度 参数三
if (ret == SOCKET_ERROR)
{
printf("Socket Connect Failed! Error:%d\n", GetLastError());
getchar();
return -1;
}
else
{
printf("Socket Connect Succeed!\n");
}
printf("Input Data: Input 'q' Break\n");
while (1)
{
scanf("%s", &SendBuffer);
//五、发送数据到服务器 //send向已连接的socket发送数据 无错返回所发送数据的总数 错返回SOCKET_ERROR。
//int send( SOCKET s, const char FAR *buf, int len, int flags );
ret = send(ClientSocket, //指定发送端套接字描述符
SendBuffer, //存放应用程序要发送数据的缓冲区
(int)strlen(SendBuffer), //发送的数据的字节数
0); //一般置0
if (ret == SOCKET_ERROR)
{
printf("Send Information Failed! Error:%d\n", GetLastError());
getchar();
break;
}
cout<<"Send Information Success"<<endl;
break;
}
//关闭socket windows用closesocket linux用close
closesocket(ClientSocket);
if (SendBuffer[0] == 'q') // 设定输入第一个字符为q时退出
{
printf("Quit!\n");
break;
}
}
WSACleanup();//与WSAStartup成对出现 解除绑定 Winsock动态链接库
getchar();
return 0;
}
服务器端
#include
#include
#include
#pragma comment(lib, "ws2_32.lib")
#define SERVER_PORT 5000
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 100
using namespace std;
int main() // (int argc, char* argv[])
{
WSADATA wd;
SOCKET SeverSocket;
struct sockaddr_in severaddr;
int opt = 1;//用于setsocketopt
memset(&severaddr,0,sizeof(severaddr)); // 置字节字符串前n个字节为0,包括'\0'
//一、初始化Windows Socket
int op=WSAStartup(MAKEWORD(2, 2), &wd);
if ( op!= 0)
{
printf("Init Windows Socket Failed! Error: %d\n", GetLastError());
getchar();
return -1;
}
cout<<"Init Windows Socket Success"<<endl;
//二、创建一个Socket
SeverSocket = socket(PF_INET, SOCK_STREAM, 0);
if (SeverSocket == INVALID_SOCKET)//无效套接字
{
printf("Create Socket Failed! Error: %d\n", GetLastError());// windows下错误处理GetLastError() linux 要#include 错误信息保存在全局的errno变量
getchar();
return -1;
}
cout<<"Create Socket Success"<<endl;
//三、设置服务器端 severaddr
severaddr.sin_family = AF_INET;
severaddr.sin_addr.s_addr =inet_addr("192.168.1.4"); // 转网络序
severaddr.sin_port = htons(SERVER_PORT);
// bind a socket
setsockopt(SeverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(int));//重复调用 close后不会立即返关闭 还会等一等
if(bind(SeverSocket, (struct sockaddr*)&severaddr, sizeof(severaddr)))//sever端要绑定\bind()一个地址(如ip地址+端口号)以提供服务 client端就不用
{
printf("Server Bind Port: %d Failed!\n", SERVER_PORT);
exit(1);
}
cout<<"Server Bind Port Success"<<endl;
// 监听Socket
if (listen(SeverSocket, LENGTH_OF_LISTEN_QUEUE))
{
printf("Server Listen Failed!\n");
exit(1);
}
cout<<"Server Listen Success"<<endl;
while(1)
{
struct sockaddr_in client_addr;
SOCKET client_socket;
socklen_t length;
char Buffer[BUFFER_SIZE];
// 连接客户端Socket
length = sizeof(client_addr);
client_socket = accept(SeverSocket, (struct sockaddr*)&client_addr, &length);//第一个参数为服务器的socket描述字,
if (client_socket < 0) //第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,
{
//第三个参数为协议地址的长度。
printf("Server Accept Failed!\n"); //返回已连接套接字
break;
}
// 从客户端接收数据
while(1)
{
memset(Buffer,0,BUFFER_SIZE);
length = recv(client_socket, Buffer, BUFFER_SIZE, 0);
if (length < 0)
{
printf("Server Recieve Data Failed!\n");
break;
}
if ('q' == Buffer[0])
{
printf("Quit!\n");
break;
}
printf("%s\n", Buffer);
break;
}
closesocket(client_socket);
}
closesocket(SeverSocket);
WSACleanup();
return 0;
}
PS(如果想对虚拟机通讯,需要设置桥接模式