多线程编程实例----服务器客户端简单通信

来源:互联网 发布:淘宝满减退款漏洞 编辑:程序博客网 时间:2024/04/29 19:49

    这是一个非常简单的通信程序,不需要什么前奏来说明这个问题,可以直接来看程序的实现,首先是服务器端的程序:

int main(int argc, char **argv)
{
 WSADATA wsd;
 SOCKET sListen, sClient;
 int iAddrSize;
 HANDLE hThread;
 DWORD dwThread;
 struct sockaddr_in local, client;
 
 
 ValidateArgs(argc, argv);
 
 
 if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 {
  printf("Failed to load WinSock!/n");
  return 1;
 }

 
 sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
 if (sListen == SOCKET_ERROR)
 {
  printf("socket() failed: %d/n", WSAGetLastError());
  return 1;
 }
 if (bInterface)
 {
  local.sin_addr.s_addr = inet_addr(szAddress);
  if (local.sin_addr.s_addr == INADDR_NONE)
  {
   usage();
  }
 }
 else
 {
  local.sin_addr.s_addr = htonl(INADDR_ANY);
 }
 local.sin_family = AF_INET;
 local.sin_port = htons(iPort);

 
 if (bind(sListen,
       (struct sockaddr*)&local,
    sizeof(local)) == SOCKET_ERROR)
 {
  printf("bind() failed: %d/n", WSAGetLastError());
  return 1;
 }
 
 listen(sListen, 8);

 
 while(1)
 {
  iAddrSize = sizeof(client);
  
  sClient = accept(sListen,
       (struct sockaddr*)&client,
       &iAddrSize);
  if (sClient == INVALID_SOCKET)
  {
   printf("accept() failed: %d/n",WSAGetLastError());
   break;
  }
  printf("Acceped client: %s:%d/n",
      inet_ntoa(client.sin_addr),
      ntohs(client.sin_port));

  hThread = CreateThread(NULL,
          0,
          ClientThread,
                (LPVOID)sClient,
          0,
          &dwThread);
  if (hThread == NULL)
  {
   printf("CreateThread() failed: %d/n", GetLastError());
   break;
  }
  CloseHandle(hThread);
 }
 closesocket(sListen);
 WSACleanup();
 return 0;
}

    这是主程序,首先是一个验证参数的过程,程序提供了三种参数格式:-p:x说的是监听的端口号,-i:str说的是监听的IP,这只适用于一台主机多个IP的情况,-o说的是服务器只接收消息,而不发送回应消息,验证参数的代码如下:


void ValidateArgs(int argc, char **argv)
{
 int i;

 for (i = 1; i < argc; i++)
 {
  if ((argv[i][0] =='-') || (argv[i][0] == '/'))
  {
   switch (tolower(argv[i][1]))
   {
   
   case 'p':
    iPort = atoi(&argv[i][3]);
    break;

   
   case 'i':
    bInterface = TRUE;
    if (strlen(argv[i]) > 3)
     strcpy(szAddress, &argv[i][3]);
    break;

   
   case 'o':
    bRecvOnly = TRUE;
    break;

   
   default:
    usage();
    break;
   }
  }
 }
}

    验证完参数之后,就开一般的TCP/IP通信的步骤了,即首先创建一个socket,然后绑定在服务器的某个端口上,然后listen进行监听,最后是accept接收到一个连接,就创建一个单独的线程专门与用这个连接和某个客户端进行通信,来看看通信线程的入口函数:


DWORD WINAPI ClientThread(LPVOID lpParam)
{
 SOCKET sock = (SOCKET)lpParam;
 char szBuff[DEFAULT_BUFFER];
 int ret, nLeft, idx;
 while (1)
 {
  
  ret = recv(sock, szBuff,DEFAULT_BUFFER, 0);
  if (ret == 0)
   break;
  else if (ret == SOCKET_ERROR)
  {
   printf("recv() failed: %d/n", WSAGetLastError());
   break;
  }
  szBuff[ret] = '/0';
  printf("RECV: %s/n", szBuff);

  if (!bRecvOnly)
  {
   nLeft = ret;
   idx = 0;
   while (nLeft > 0)
   {
    
    ret = send(sock, &szBuff[idx], nLeft, 0);
    if (ret == 0)
     break;
    else if (ret == SOCKET_ERROR)
    {
     printf("send() failed: %d/n", WSAGetLastError());
     break;
    }
    nLeft -=ret;
    idx +=ret;
   }
  }

 }
 return 0;
}

    这就是一个通信线程函数,用recv接收数据,用send发送数据,不是很难。

    下面来看看客户端的实现:


void ValidateArgs(int argc, char *argv[])
{
 int i;
 for (i = 0; i < argc; i++)
 {
  if ((argv[i][0] == '-') || (argv[i][0] == '/'))
  {
   switch (tolower(argv[i][1]))
   {
    
    case 'p':
     if (strlen(argv[i]) > 3)
      iPort = atoi (&argv[i][3]);
     break;

    
    case 's':
     if (strlen(argv[i]) > 3)
      strcpy(szServer, &argv[i][3]);
     break;

    
    case 'n':
     if (strlen(argv[i]) > 3)
      dwCount = atol(&argv[i][3]);
     break;

    
    case 'o':
     bSendOnly = TRUE;
     break;
     
    default:
     usage();
     break;
   }
  }
 }
}

int main(int argc, char *argv[])
{
 WSADATA wsd;
 SOCKET sClient;
 char szBuffer[DEFAULT_BUFFER];
 int ret;
 struct sockaddr_in server;
 struct hostent *host = NULL;
 DWORD i;
 
 
 ValidateArgs(argc, argv);
 
 if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 {
  printf("Failed to load Winsock library!/n");
  return 1;
 }
 strcpy(szMessage, DEFAULT_MESSAGE);
 
 
 sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 if (sClient == INVALID_SOCKET)
 {
  printf("socket() failed: %d/n", WSAGetLastError());
  return 1;
 }
 server.sin_family = AF_INET;
 server.sin_port = htons(iPort);
 server.sin_addr.s_addr = inet_addr(szServer);

 if (server.sin_addr.s_addr == INADDR_NONE)
 {
  host = gethostbyname(szServer);
  if (host == NULL)
  {
   printf("Unable to resolve server: %s/n", szServer);
   return 1;
  }
  CopyMemory(&server.sin_addr,
       host->h_addr_list[0],
          host->h_length);
 }
 
 if (connect(sClient,
          (struct sockaddr*)&server,
       sizeof(server))
  == SOCKET_ERROR)
 {
  printf("connect() failed: %d/n", WSAGetLastError());
  return 1;
 }

 
 for (i = 0; i < dwCount; i++)
 {
  ret = send(sClient, szMessage, strlen(szMessage), 0);
  if (ret ==0 )
  {
   break;
  }
  else if (ret == SOCKET_ERROR)
  {
   printf("send() failed: %d/n", WSAGetLastError());
   break;
  }
  printf("Send %d bytes/n", ret);
  if (!bSendOnly)
  {
   ret = recv(sClient, szBuffer, DEFAULT_BUFFER, 0);
   if (ret == 0)
   {
    break;
   }
   else if (ret == SOCKET_ERROR)
   {
    printf("recv() failed: %d/n", WSAGetLastError());
    break;
   }
   szBuffer[ret] = '/0';
   printf("RECV [%d bytes]: %s/n", ret, szBuffer);
  }
 }
 closesocket(sClient);
 WSACleanup();
 return 0;
}

    客户端的程序非常的简单,创建一个socket,然后connect服务器,就可以进行recv和send了,不要创建线程,很轻松的就可以完成通信了,当然更好的情况是将客户端的connect单独放在一个线程里面进行,在服务器端,最后也是将accept单独放在一个线程里面进行,这样的话可以更好的响应用户的操作了。

 

原创粉丝点击