什么是Socket编程?
我们平常使用的电脑或手机等电子产品,在大部分情况下同一电子产品都会同时运行多个应用程序,比如说微信、QQ等,那为什么一台电子产品可以同时运行多个应用程序呢?比如说你在手机上同时运行着微信和QQ,而我现在给你的微信发了一条信息,当这条信息到达你的手机上时,它怎么知道应该将这条信息交给微信而不是QQ的?这是因为每一个应用程序都打开了一个Socket并绑定到了一个端口上,当信息到达手机后,再根据端口发送给相应地应用程序。
常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对面向连接的TCP服务应用;数据报式是一种无连接的Socket,对应于无连接的UDP服务应用。接下来,分服务器端和客户端两部分来详细介绍流式Socket的实现。
服务器端:
1.调用socket()函数创建一个Socket
//socket()函数原型
SOCKET socket(
int af, //地址族,一般是AF_INET,表示使用IP地址族
int type, //socket类型,SOCK_STREAM或SOCK_DGRAM
int protocol//协议类型,通常取值 0
)
2.调用bind()函数将创建的套接字与主机上提供服务的某端口绑定在一起
//bind()函数原型
int bind(
SOCKET s, //要绑定的套接字
const struct sockaddr FAR * name, //指向SOCKADDR结构的地址
int namelen//地址参数(name)的长度
);
3.调用listen()函数指示绑定后的套接字sockServer监听客户端的请求
//listen()函数原型
int listen(
SOCKET s, //进行监听的socket
int backlog//客户端可以连接的请求个数
);
4.监听到有请求后调用accept()函数接受连接请求
SOCKET accept(
SOCKET s, //处于监听状态的socket
struct sockaddr FAR * addr, //客户机IP地址的sockaddr指针
int FAR * addrlen//地址的长度
);
5.在已经建立的连接上调用send()/recv()函数发送或接收数据
//send()函数原型
int send(
SOCKET s,
const char FAR * buf, //发送数据缓冲区
int len, //缓冲区长度
int flags //用于控制数据传输方式,0表示按正常方式发送数据
);
//recv()函数原型
int recv(
SOCKET s,
char FAR * buf, //接收数据缓冲区
int len, //缓冲区长度
int flags // 0表示接收的是正常数据,无特殊行为
);
服务器端完整代码为:
//server.cpp
#include< iostream>
#include< winsock2.h>
#include< WS2tcpip.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
constexpr auto serverIP = "127.0.0.1";//服务器端的IP地址,这里设为本机回环地址;
constexpr auto serverPort = 6789;//服务器端进程的端口号,值大于1023即可;
int main() {
//第一步:WinSock初始化
WORD wVersion = MAKEWORD(2, 2);
WSADATA wsaData;
//WSAStartup,Windows Sockets Asynchronous,Windows异步套接字的启动命令。
if (WSAStartup(wVersion, &wsaData)) {
cout << "WSAStartup......\n";
return 0;
}
//创建socket
SOCKET sockServer;
sockServer = socket(AF_INET, SOCK_STREAM, 0);
//服务器地址
SOCKADDR_IN serverAddr, clientAddr;
serverAddr.sin_family = AF_INET;
in_addr dst;
//点分十进制的IPv4地址转换为网络字节的IP(整型)
int res = inet_pton(AF_INET, serverIP, (void*)&dst);
//将serverIP地址转换为in_addr的结构体,并复制在dst中
if (res == 1) {
serverAddr.sin_addr.S_un.S_addr = dst.s_addr;
}
else {
cout << "The address is error." << endl;
}
serverAddr.sin_port = htons(serverPort);//host to network short
//绑定
bind(sockServer, (SOCKADDR*)&serverAddr, sizeof(SOCKADDR));
//监听
listen(sockServer, 10);//10(服务器最多可以处理的用户数)
//处理
int acceptResult = sizeof(SOCKADDR);
SOCKET sockAccept;
cout << "The server is waiting for client's connection request..." << endl;
sockAccept = accept(sockServer, (SOCKADDR*)&clientAddr, &acceptResult);
if (sockAccept == INVALID_SOCKET) {
cout << "The server failed to accept the client's connection request." << endl;
return 0;
}
else {
cout << "The server accepted the client' connection request." << endl;
}
//cout <"<" ;
cin >> sendbuffer;
send(sockAccept, sendbuffer, strlen(sendbuffer) + 1, 0);
}
}
closesocket(sockServer);
WSACleanup();
return 0;
}
客户端:
1.调用socket()函数创建一个Socket,代码为:
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
2.调用bind()函数将创建的套接字与主机上提供服务的某端口绑定在一起
//connect()函数原型
int connect(
SOCKET s, //将要连接的socket
const struct sockaddr FAR * name, //目标socket地址
int namelen //地址参数(name)的长度
);
3.在已经建立的连接上调用send()/recv()函数发送或接收数据,对应的代码为:
//设发送缓存为sendbuffer;
send(sockClient, sendbuffer, strlen(sendbuffer) + 1, 0);
//设接收缓存为recivebuffer;
recv(sockClient, receivebuffer, 256, 0);
客户端完整代码为:
//client.cpp
#include< iostream>
#include< winsock2.h>
#include< ws2tcpip.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
constexpr auto serverIP = "127.0.0.1";//服务器端的IP地址;
constexpr auto serverPort = 6789;//服务器端进程的端口号;
int main() {
//WinSock初始化
WORD wVersion = MAKEWORD(2, 2);
WSADATA wsaData;
//WSAStartup,Windows Sockets Asynchronous,Windows异步套接字的启动命令。
if (WSAStartup(wVersion, &wsaData)) {
cout << "WSAStartup......" << endl;
return 0;
}
//创建套接字
SOCKET sockClient;
sockClient = socket(AF_INET, SOCK_STREAM, 0);//1.address family,地址族协议,IPv4
//2.流式套接字
//3.已使用TCP,默认0
//服务器地址
SOCKADDR_IN serverAddr;
serverAddr.sin_family = AF_INET;
in_addr dst;
//点分十进制的IPv4地址转换为网络字节的IP(整型)
int IPResult = inet_pton(AF_INET, serverIP, &dst);
//将serverIP地址转换为in_addr的结构体,并复制在dst中
if (IPResult == 1) {
serverAddr.sin_addr.S_un.S_addr = dst.s_addr;
}
else {
cout << "The IP is error." << endl;
}
serverAddr.sin_port = htons(serverPort);
//请求连接
int connectResult = connect(sockClient, (SOCKADDR*)&serverAddr, sizeof(SOCKADDR));
if (connectResult != 0) {
cout << "The client's connection request failed." << endl;
}
else {
cout << "The client's connection request is accepted." << endl;
}
//发送及接收数据
char sendbuffer[256] = { '\0' };
char receivebuffer[256] = { '\0' };
for (;;) {
//发送数据
cout << "The client says:>";
cin >> sendbuffer;
if (strcmp(sendbuffer, "quit") == 0) {
break;
}
else {
send(sockClient, sendbuffer, strlen(sendbuffer) + 1, 0);
}
//接收数据
recv(sockClient, receivebuffer, 256, 0);
cout << "The server says:>" << receivebuffer << endl;
}
closesocket(sockClient);//关闭套接字
WSACleanup();//注销及释放分配资源
return 0;
}