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("发送信息失败"));
}
}