本篇博客是在windows系统下的CodeBlocks环境下编写而成的,Linux系统以及其他编译环境暂不适用
关于如何CodeBlocks如何安装和配置/,可以参考链接(转自萤火虫塔莉):CodeBlocks的安装以及编译器的配置
常见问题:
编译时不能识别socket,需要手动导入lib库。具体方法链接:https://blog.csdn.net/buaa1214wwj/article/details/52033868
如果导这两个lib库仍然不能识别,出现以下情况,重新建立C++工程。
二、socket用法简介
(1)socket概念
- socket翻译为套接字,它是计算机之间通信的一种约定或一种方式,通过socket这种约定,一台计算机可以接受其他计算机的数据,也可以向其他计算机发送数据。
- 学习socket,也就是学习计算机如何通信,并且编写出实用的程序。
(2)Windows程序示例
服务器演示代码Server.cpp
#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
int main(){
//初始化 DLL
WSADATA wsaData;
WSAStartup( MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//绑定套接字
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET; //使用IPv4地址
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
sockAddr.sin_port = htons(1234); //端口
bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//进入监听状态
listen(servSock, 20);
//接收客户端请求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
//向客户端发送数据
char *str = "Hello World!";
send(clntSock, str, strlen(str)+sizeof(char), NULL);
//关闭套接字
closesocket(clntSock);
closesocket(servSock);
//终止 DLL 的使用
WSACleanup();
return 0;
}
客户端演示代码Client.cpp
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
int main(){
//初始化DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//向服务器发起请求
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockAddr.sin_port = htons(1234);
connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//接收服务器传回的数据
char szBuffer[MAXBYTE] = {0};
recv(sock, szBuffer, MAXBYTE, NULL);
//输出接收到的数据
printf("Message form server: %s\n", szBuffer);
//关闭套接字
closesocket(sock);
//终止使用 DLL
WSACleanup();
system("pause");
return 0;
}
(3)程序解释
1. #pragma comment (lib, "ws2_32.lib") :Windows下的socket程序依赖Winsock.dll或者ws2_32.dll,所以必须提前加载2. WSAStartup()函数对dll初始化,以指明Winsock的版本号,原型:
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
1)wVersionRequested 为 WinSock 规范的版本号,一般采用2.2版本,类型为 WORD,等价于 unsigned short,是一个整数,所以需要用 MAKEWORD() 宏函数对版本号进行转换。
2)lpWSAData 为指向 WSAData 结构体的指针。
3.socket() 函数创建套接字,原型:(Windows 不把套接字作为普通文件对待,而是返回 SOCKET 类型的句柄)
SOCKET socket(int af, int type, int protocol);
1)af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET(IPv4) 和 AF_INET6(IPv6) 。
2)type 为数据传输方式,常用的有 SOCK_STREAM (面向连接的数据传输方式)和 SOCK_DGRAM(无连接的数据传输方式)。
3)protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
4. bind() 函数服务器端将套接字与特定的IP地址和端口绑定;connect() 函数客户端用来建立连接
1) bind()原型:
int bind(SOCKET sock, const struct sockaddr *addr, int addrlen);
2)connet()原型:
int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);
sock 为 socket 文件描述符,addr 为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出
sockaddr_in 结构体,成员变量如下:
struct sockaddr_in{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
uint16_t sin_port; //16位的端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用,一般用0填充
};
5.listen()函数让套接字进入被动监听状态,它的原型为:
int listen(SOCKET sock, int backlog);
sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
6.accept() 函数当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求,它的原型为:
SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。
特别说明:listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
7. send() 函数发送数据,recv() 函数接受数据
1)send()原型:
int send(SOCKET sock, const char *buf, int len, int flags);
2)recv()原型:
int recv(SOCKET sock, char *buf, int len, int flags);
sock 为要发送数据的套接字,buf 为要发送的数据的缓冲区地址,len 为要发送的数据的字节数,flags 为发送数据时的选项。
8.closesocket()函数关闭套接字,原型为:
void closesocket(SOCKET Sock);
9.WSACleanup()函数终止 DLL 的使用。