1. 程式人生 > >Socket埠複用的例子

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;
}