Socket端口复用的例子

1.Socket复用的基本知识

       socket的通信流程就不再赘述了。那么我们再仔细想一想,操作系统如何区分一个socket的呢?

那就是:发送方IP、发送方Port、接收方IP、接收方Port、通信协议(Tcp/Udp),这也被称为五元素。

由这五个元素,我们就能知道为什么Tcp服务端的socket的端口号都相同而且能准确收发数据。

         举个列子,如果在一个客端端程序中创建两个socket,如下表所示。

SOCKET本方IP本方Port目的IP目的Port协议
sokcet1127.0.0.18000192.168.1.19000Tcp
socket2127.0.0.18000192.168.1.110000

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")
int main(int argc, char* argv[])
{
 /* 初始化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;
 }
 printf("请输入端口号:\n");
 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;
 }
 /* 循环接收数据 */
 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);
  }
  /* 发送数据 */
  char * sendData = "你好,TCP客户端!\n";
  send(sClient, sendData, strlen(sendData), 0);
  closesocket(sClient);
 }
 closesocket(slisten);
 WSACleanup();
 return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值