socket通讯

tcp/ip分层:
(物理介质-->) 链路层(如网卡)-->网络层(如ip)-->传输层(如tcp,udp)-->应用层(如www,ftp,telnet)

ip地址是一个32位的二进制值,常用点分十进制表示,如61.135.150.71
ip地址能唯一标识一台主机,而端口号(16位的二进制数)则标识这台主机上的一个进程(通信进程需要绑定一个端

口号).
一般应用进程使用的端口号范围: 1024至49151

实现了ip协议的程序(不确定进程)和实现了udp协议的程序(确定进程)提供无连接不可靠的传输.
实现了tcp协议的程序(要建立并维护连接)提供面向连接的可靠的传输.
应用程序要实现多播应该使用upd协议(程序,dll),因为tcp要建立并维护连接(开销大).

应用程序使用tcp/ip协议,就是使用实现了tcp/ip协议的程序(组件,dll等).
socket是实现tcp/ip协议的程序(或接口)
socket最先用在unix平台,后来产生于winsock用于windows平台.


//socket函数gethostbyname使用一例(vc console)
#include <stdio.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")

int main(int nCount, char* argv[])
{
 WSADATA wsaData;
 int err;
 struct hostent* pHost = NULL;  //描述主机信息的结构体

 if (nCount != 2)
 {
  return 0;
 }
 
 err = WSAStartup(MAKEWORD(1, 1), &wsaData); //加载Ws2_32.dll
 if (err != 0)
 {
  printf("load ws2_32.dll failed");
  return 0;
 }

 pHost = gethostbyname(argv[1]);
 if (pHost == NULL)
 {
  printf("gethostbyname failed");
  return 0;
 }
 printf("%s:/n", argv[1]);
 printf("host name: %s/nhost alias: %s/n", pHost->h_name, pHost->h_aliases[0]);
 printf("host ip: %s/n", inet_ntoa(*(struct in_addr*)(pHost->h_addr))); 

 WSACleanup();

 return 1;
}

 

一个简单的socket通信c/s例子(vc6.0测试):
服务器端:
//成员变量(public):
 int server_socket; //侦听与接收连接
 int clientSocket;  //接收与发送数据
 sockaddr_in local; //填充ip,端口号等的结构体
 sockaddr_in client;

//行为:
UINT RecvThread(LPVOID param)
{
 CJiiiDlg* pDlg = (CJiiiDlg*)param;
 BYTE buffer[1024];  //BYTE--unsigned char
 memset(buffer, 0, 1024);
 int num;
 while (TRUE)
 {
  num = recv(pDlg->clientSocket, (char*)buffer, 1024, 0); //收数据
  if(num == 0 || num == SOCKET_ERROR)
  {
   closesocket(pDlg->clientSocket);
  }
  else
  {
   buffer[num] = '/0';
   CString str(buffer);

   CTime t = CTime::GetCurrentTime();
   CString strTime = t.Format("%H:%M:%S ");
   CString strInfo("");
   CString strPre("");
   pDlg->GetDlgItemText(IDC_EDIT1, strPre);
   strInfo = strTime + str + "/r/n/n" + strPre;
   pDlg->SetDlgItemText(IDC_EDIT1, strInfo);
  }
 }
}

UINT AcceptThread(LPVOID param)
{
 CJiiiDlg* pDlg = (CJiiiDlg*)param;

 memset(&pDlg->client, 0, sizeof(pDlg->client));
 int len = sizeof(sockaddr_in);
 while (TRUE)
 {
  pDlg->clientSocket = accept(pDlg->server_socket, (LPSOCKADDR)&pDlg->client,
   &len);  //client记录连接者的ip和端口号
  //clientSocket的作用确定下来了:用于接收(recv)和发送(send)数据
  if (pDlg->clientSocket != -1)
  {
   AfxBeginThread(RecvThread, (LPVOID)pDlg);
  }
 }
}

//成为服务器:
void CJiiiDlg::OnButton1()
{
 // TODO: Add your control notification handler code here
 //加载dll
 WSADATA Data;
 int status = WSAStartup(MAKEWORD(1, 1), &Data); //初始化Socket,使用WinSock1.1
 if (status != 0)
 {
  MessageBox(_T("加载Ws2_32.dll失败"));
  return ;
 }

 server_socket = socket(AF_INET, SOCK_STREAM, 0); //socket号的作用还未确定!
 if (server_socket == -1)
 {
  MessageBox(_T("创建socket号失败"));
  return;
 }
 
 memset(&local, 0, sizeof(local));
 local.sin_family = AF_INET;   
 local.sin_addr.s_addr = INADDR_ANY;  //ip(网卡)
 CString strPort("");
 GetDlgItemText(IDC_EDIT2, strPort);
 local.sin_port = htons(atoi(strPort)); //端口号
 int err = bind(server_socket, (LPSOCKADDR)&local, sizeof(local));
 //把当前进程绑定到ip(网卡)和端口号; server_socket值的
 //作用此时才确定下来:用于侦听(listen)和接收(accept)连接.
 if (err == -1)
 {
  MessageBox(_T("bind失败"));
  return;
 }
 //侦听
 status = listen(server_socket, 5); 
 if (status == -1)
 {
  MessageBox(_T("侦听失败"));
  return;
 }

 AfxBeginThread(AcceptThread, (LPVOID)this); //未理会创建失败

 GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);
}

void CJiiiDlg::OnClose()
{
 WSACleanup();  //从内存中去掉Ws2_32.dll, 可能还做了内存的清理工作
 
 CDialog::OnClose();
}

//发送
void CJiiiDlg::OnButton2()
{
 CString strInfo("");
 CString strName("");

 GetDlgItemText(IDC_EDIT4, strName);
 if (strName == "")
 {
  strName = "服务器";
 }

 GetDlgItemText(IDC_EDIT3, strInfo);
 if (strInfo == "") return;
 strInfo = strName + "说: " + strInfo; 
 int numsnd = send(clientSocket, strInfo, strInfo.GetLength(), 0);
 if (numsnd == SOCKET_ERROR || numsnd == 0)
 {
  MessageBox(_T("发送信息失败"));
 }
}

客户端:
//成员变量(public):
 int toServer;  //连接,发送和接收的socket号

//行为:
//接收
UINT RecvThread(LPVOID param)
{
 CGfdDlg* pDlg = (CGfdDlg*)param;
 BYTE buffer[1024];  //BYTE--unsigned char
 memset(buffer, 0, 1024);
 int num;
 while (TRUE)
 {
  num = recv(pDlg->toServer, (char*)buffer, 1024, 0); //收数据
  if(num == 0 || num == SOCKET_ERROR)
  {
   closesocket(pDlg->toServer);
  }
  else
  {
   buffer[num] = '/0';
   CString str(buffer);

   CTime t = CTime::GetCurrentTime();
   CString strTime = t.Format("%H:%M:%S ");
   CString strInfo("");
   CString strPre("");
   pDlg->GetDlgItemText(IDC_EDIT4, strPre);
   strInfo = strTime + str + "/r/n/n" + strPre;
   pDlg->SetDlgItemText(IDC_EDIT4, strInfo);
  }
 }
}

//初始化socket; 连接服务器
void CGfdDlg::OnButton1()
{
 //加载dll
 WSADATA Data;
 int status = WSAStartup(MAKEWORD(1, 1), &Data); //初始化Socket,使用WinSock1.1
 if (status != 0)
 {
  MessageBox(_T("加载Ws2_32.dll失败"));
  return ;
 }

 DWORD dwIP;
 toServer = socket(AF_INET, SOCK_STREAM, 0); //toServer作用未明

 SOCKADDR_IN ServerAddr;
 ServerAddr.sin_family = AF_INET;
 CString strPort("");
 GetDlgItemText(IDC_EDIT2, strPort);
 ServerAddr.sin_port = htons(atoi(strPort));
 ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);
 ServerAddr.sin_addr.s_addr = htonl(dwIP);   

 if (connect(toServer, (sockaddr*)&ServerAddr, sizeof(ServerAddr))
   == -1) //toServer作用确定:用于发送和接收数据
 {
  MessageBox(_T("连接服务器失败"));
  closesocket(toServer);
  return;
 } 

 AfxBeginThread(RecvThread, (LPVOID)this);

 GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);
}

//发送
void CGfdDlg::OnButton2()
{
 CString strInfo("");
 CString strName("");

 GetDlgItemText(IDC_EDIT3, strName);
 if (strName == "")
 {
  strName = "未命名";
 }

 GetDlgItemText(IDC_EDIT1, strInfo);
 if (strInfo == "") return;
 strInfo = strName + "说: " + strInfo;
 int numsnd = send(toServer, strInfo, strInfo.GetLength(), 0);
 if (numsnd == SOCKET_ERROR || numsnd == 0)
 {
  MessageBox(_T("发送信息失败"));
 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值