1.Socket复用的基本知识
socket的通信流程就不再赘述了。那么我们再仔细想一想,操作系统如何区分一个socket的呢?
那就是:发送方IP、发送方Port、接收方IP、接收方Port、通信协议(Tcp/Udp),这也被称为五元素。
由这五个元素,我们就能知道为什么Tcp服务端的socket的端口号都相同而且能准确收发数据。
举个列子,如果在一个客端端程序中创建两个socket,如下表所示。
SOCKET | 本方IP | 本方Port | 目的IP | 目的Port | 协议 |
sokcet1 | 127.0.0.1 | 8000 | 192.168.1.1 | 9000 | Tcp |
socket2 | 127.0.0.1 | 8000 | 192.168.1.1 | 10000 | Tcp |
因为目的Port不一致,所以操作系统能够区分两个socket,所以两个socket均能正常通信。
但是如果这五个元素都一直,则采用Tcp协议进行connect时,就会出现连接错误,因为五元素出现了重复,操作系统不能区别两个socket。
也就是说,只要五元素不完全一致,操作系统就能区分socket。
2.基于TCP的端口复用Demo
下面是一个基于Tcp的端口复用案例,经测试,在一个进程中能够采用端口复用。但需要注意,五元素不能完全相同,否则不能正常通信。
##客户端:##
#include <WINSOCK2.H>
#include <STDIO.H>
#pragma comment(lib, "ws2_32.lib")
void Init(SOCKET socket){
/* 设置监听参数 */
sockaddr_in myAddr;
myAddr.sin_family = AF_INET;
myAddr.sin_port = htons(8000);
myAddr.sin_addr.S_un.S_addr = INADDR_ANY;
int opt = 1;
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));/* 绑定端口 */
if (bind(socket, (LPSOCKADDR)&myAddr, sizeof(myAddr)) == SOCKET_ERROR)
{
printf("bind error !");
}
}int Connect(SOCKET socket, int port=9000){
/* 设置目标通信参数 */
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(port);
serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");/* 发起连接 */
if (connect(socket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{
printf("connect error !");
closesocket(socket);
return 0;
}
return 1;
}void Send(SOCKET socket){
char * sendData = "你好,TCP服务端,我是客户端1!\n";
send(socket, sendData, strlen(sendData), 0);
}void Recv(SOCKET sokcet){
char recData[255];
int ret = recv(sokcet, recData, 255, 0);
if (ret > 0)
{
recData[ret] = 0x00;
printf(recData);
}
}int main(int argc, char* argv[])
{
/* 初始化socket */
WORD sockVersion = MAKEWORD(2, 2);
WSADATA data;
if (WSAStartup(sockVersion, &data) != 0){
printf("初始化WSA失败!\n");
return 0;
}/* 创建socket */
SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client == INVALID_SOCKET){
printf("invalid socket !");
return 0;
}
Init(client);
Connect(client);
Send(client);
Recv(client);SOCKET client2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client == INVALID_SOCKET){
printf("invalid socket !");
return 0;
}
Init(client2);
Connect(client2, 9090);
Send(client2);
Recv(client2);
getchar();/* 关闭和清理socket */
closesocket(client);
closesocket(client2);
WSACleanup();
return 0;
}
##服务端:##
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main(int argc, char* argv[])
{
/* 初始化WSA */
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVersion, &wsaData) != 0)
{
printf("初始化WSA失败\n");
return 0;
}
{
/* 初始化WSA */
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVersion, &wsaData) != 0)
{
printf("初始化WSA失败\n");
return 0;
}
/* 创建套接字 */
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET)
{
printf("socket error !");
return 0;
}
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET)
{
printf("socket error !");
return 0;
}
printf("请输入端口号:\n");
int port = 0;
scanf_s("%d", &port);
int port = 0;
scanf_s("%d", &port);
/* 绑定IP和端口 */
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("bind error !");
}
/* 开始监听 */
if (listen(slisten, 5) == SOCKET_ERROR)
{
printf("listen error !");
return 0;
}
if (listen(slisten, 5) == SOCKET_ERROR)
{
printf("listen error !");
return 0;
}
/* 循环接收数据 */
SOCKET sClient;
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
char revData[255];
while (true)
{
printf("等待连接...\n");
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if (sClient == INVALID_SOCKET)
{
printf("accept error !");
continue;
}
printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
SOCKET sClient;
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
char revData[255];
while (true)
{
printf("等待连接...\n");
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if (sClient == INVALID_SOCKET)
{
printf("accept error !");
continue;
}
printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
/* 接收数据 */
int ret = recv(sClient, revData, 255, 0);
if (ret > 0)
{
revData[ret] = 0x00;
printf(revData);
}
int ret = recv(sClient, revData, 255, 0);
if (ret > 0)
{
revData[ret] = 0x00;
printf(revData);
}
/* 发送数据 */
char * sendData = "你好,TCP客户端!\n";
send(sClient, sendData, strlen(sendData), 0);
closesocket(sClient);
}
char * sendData = "你好,TCP客户端!\n";
send(sClient, sendData, strlen(sendData), 0);
closesocket(sClient);
}
closesocket(slisten);
WSACleanup();
return 0;
}
WSACleanup();
return 0;
}