标签:用户 sem npe mem invalid dem char error ada
WinSock接口:Windows处理网络的API
套接字socket
流套接字:SOCKET_STREAM 可靠连接 TCP HTTP POP3
数据报套接字:SOCKET_DGRAM 不可靠连接 UDP
寻址方式:
1:sockaddr的第1个版本
struct sockaddr{
u_short sa_family; //地址家族
char sa_data[14]; //数据
}
2.sockaddr的TCP/IP版本的 sockaddr_in
struct sockaddr_in{
short sin_family; //地址家族
u_short sin_port; //端口号
struct in_addr sin_addr; //IP地址
char sa_data[14]; //空字节,设为0,8个字节
}
WinSock编程流程
1:WinSock的装入、初始化、释放
int WSAstartup{
WORD wVersionRequested; //WinSock的版本
LPWSADATA lpWSAData; //指针,返回DLL的详细信息
}
int WSAcleanup(void)
2:套接字的创建和关闭
SOCKET socket{
int af; //套接字地址格式
int type; //套接字类型
int protocol; //套接字使用协议
}
SOCKET_STREAM:TCP可靠的流套接字
SOCKET_DGRAM :UDP不可靠的数据报套接字
SOCKET_RAW :原始套接字
3.绑定套接字
int bind(
SOCKET s; //套接字句柄
const struct socketaddr* name; //关联的本地地址
int namelen; //地址的长度
)
4.设置套接字进入监听状态
int listen(
SOCKET s; //套接字句柄
int backlog; //套接字尚未连接的最大数量
)
5.接受连接请求
SOCKET accept(
SOCKET s;
struct socketaddr* addr; //指向地址
int* addlen; //指向地址长度的指针
)
6.收发数据
int send(
SOCKET s; //套接字句柄
const char FAR* buf; //缓冲区地址
int len,; //缓冲区长度
int flags; //指定的调用方式,通常设为0;
)
int recv(SOCKET s,const char FAR*,int len,int)

ServerDemo.cpp文件
#include <winsock2.h> // 为了使用Winsock API函数
#include <stdio.h>
#include <windows.h>
// 告诉连接器与WS2_32库连接
#pragma comment(lib,"WS2_32.lib")
int main(int argc, char* argv[])
{
// 初始化WS2_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 0);
::WSAStartup(sockVersion, &wsaData);
// 创建套节字
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(s == INVALID_SOCKET)
{
printf("Failed socket() \n");
::WSACleanup();
return 0;
}
// 填充sockaddr_in结构
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
// 绑定这个套节字到一个本地地址
if(::bind(s, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("Failed bind() \n");
::WSACleanup();
return 0;
}
// 进入监听模式
if(::listen(s, 2) == SOCKET_ERROR)
{
printf("Failed listen()");
::WSACleanup();
return 0;
}
// 循环接受客户的连接请求
sockaddr_in remoteAddr;
int nAddrLen = sizeof(remoteAddr);
SOCKET client;
char szText[] = " 10ServerDemo! \r\n";
while(TRUE)
{
// 接受一个新连接
client = ::accept(s, (SOCKADDR*)&remoteAddr, &nAddrLen);
if(client == INVALID_SOCKET)
{
printf("Failed accept()");
continue;
}
printf(" 接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
// 向客户端发送数据
::send(client, szText, strlen(szText), 0);
// 关闭同客户端的连接
::closesocket(client);
}
// 关闭监听套节字
::closesocket(s);
// 释放WS2_32库
::WSACleanup();
return 0;
}
ClientDemo.cpp文件
#include <winsock2.h> // 为了使用Winsock API函数
#include <stdio.h>
#include <windows.h>
// 告诉连接器与WS2_32库连接
#pragma comment(lib,"WS2_32.lib")
int main(int argc, char* argv[])
{
// 初始化WS2_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 0);
::WSAStartup(sockVersion, &wsaData);
// 创建套节字
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(s == INVALID_SOCKET)
{
printf("Failed socket() \n");
::WSACleanup();
return 0;
}
// 也可以在这里调用bind函数绑定一个本地地址
// 否则系统将会自动安排
// 填写远程地址信息
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(8888);
// 注意,这里要填写服务器程序(10ServerDemo程序)所在机器的IP地址
// 如果你的计算机没有联网,直接使用127.0.0.1即可
servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
{
printf("Failed connect() \n");
::WSACleanup();
return 0;
}
// 接收数据
char buff[256];
int nRecv = ::recv(s, buff, 256, 0);
if(nRecv > 0)
{
buff[nRecv] = ‘\0‘;
printf(" 接收到数据:%s", buff);
}
// 关闭套节字
::closesocket(s);
// 释放WS2_32库
::WSACleanup();
return 0;
}
TCPServer.h文件
#include <afxwin.h> #include <afxcmn.h> #include <winsock2.h> // 告诉连接器与WS2_32库连接 #pragma comment(lib,"WS2_32.lib") #define MAX_SOCKET 56 // 定义此服务器所能接受的最大客户量 class CMyApp : public CWinApp { public: BOOL InitInstance(); }; class CMainDialog : public CDialog { public: CMainDialog(CWnd* pParentWnd = NULL); protected: // 创建套节字,并设置为监听状态,准备接受客户的连接 BOOL CreateAndListen(int nPort); // 关闭所有套节字,包括监听套节字和所有accept函数返回的套节字 void CloseAllSocket(); // 向客户连接列表中添加一个客户 BOOL AddClient(SOCKET s); // 从客户连接列表中移处一个客户 void RemoveClient(SOCKET s); protected: // 两个子窗口控件,一个是状态栏,一个是列表框 CStatusBarCtrl m_bar; CListBox m_listInfo; // 监听套节字句柄 SOCKET m_socket; // 客户连接列表 SOCKET m_arClient[MAX_SOCKET]; // 套节字数组 int m_nClient; // 上述数组的大小 protected: virtual BOOL OnInitDialog(); virtual void OnCancel(); // 开启或停止服务 afx_msg void OnStart(); // 清空信息 afx_msg void OnClear(); // 套节字通知事件 afx_msg long OnSocket(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() };
TCPServer.cpp文件
#include "TCPClient.h" #include "resource.h" // 定义网络事件通知消息 #define WM_SOCKET WM_USER + 1 CMyApp theApp; BOOL CMyApp::InitInstance() { // 初始化Winsock库 WSADATA wsaData; WORD sockVersion = MAKEWORD(2, 0); ::WSAStartup(sockVersion, &wsaData); // 弹出主窗口对话框 CMainDialog dlg; m_pMainWnd = &dlg; dlg.DoModal(); // 释放Winsock库 ::WSACleanup(); return FALSE; } CMainDialog::CMainDialog(CWnd* pParentWnd):CDialog(IDD_MAINDIALOG, pParentWnd) { } BEGIN_MESSAGE_MAP(CMainDialog, CDialog) ON_BN_CLICKED(IDC_START, OnStart) ON_BN_CLICKED(IDC_CLEAR, OnClear) ON_MESSAGE(WM_SOCKET, OnSocket) END_MESSAGE_MAP() BOOL CMainDialog::OnInitDialog() { CDialog::OnInitDialog(); // 设置图标 SetIcon(theApp.LoadIcon(IDI_MAIN), FALSE); // 创建状态栏,设置它的属性 m_bar.Create(WS_CHILD|WS_VISIBLE|SBS_SIZEGRIP, CRect(0, 0, 0, 0), this, 101); m_bar.SetBkColor(RGB(0xa6, 0xca, 0xf0)); // 背景色 int arWidth[] = { 200, -1 }; m_bar.SetParts(2, arWidth); // 分栏 m_bar.SetText(" Windows程序设计进阶之路!", 1, 0); // 第一个栏的文本 m_bar.SetText(" 空闲", 0, 0); // 第二个栏的文本 // 设置列表框控件到m_listInfo对象的关联 m_listInfo.SubclassDlgItem(IDC_INFO, this); // 初始化监听套节字和连接列表 m_socket = INVALID_SOCKET; m_nClient = 0; // 下面是取得本地IP地址的过程,将它显示在状态栏的第一个分栏中 // 取得本机名称 char szHost[256]; ::gethostname(szHost, 256); // 通过本机名称取得地址信息 HOSTENT* pHost = gethostbyname(szHost); if(pHost != NULL) { CString sIP; // 得到第一个IP地址 in_addr *addr =(in_addr*) *(pHost->h_addr_list); // 显示给用户 sIP.Format(" 本机IP:%s", inet_ntoa(addr[0])); m_bar.SetText(sIP, 0, 0); } return TRUE; } void CMainDialog::OnStart() { if(m_socket == INVALID_SOCKET) // 开启服务 { // 取得端口号 CString sPort; GetDlgItem(IDC_PORT)->GetWindowText(sPort); int nPort = atoi(sPort); if(nPort < 1 || nPort > 65535) { MessageBox("端口号错误!"); return; } // 创建监听套节字,使它进入监听状态 if(!CreateAndListen(nPort)) { MessageBox("启动服务出错!"); return; } // 设置相关子窗口控件状态 GetDlgItem(IDC_START)->SetWindowText("停止服务"); m_bar.SetText(" 正在监听……", 0, 0); GetDlgItem(IDC_PORT)->EnableWindow(FALSE); } else // 停止服务 { // 关闭所有连接 CloseAllSocket(); // 设置相关子窗口控件状态 GetDlgItem(IDC_START)->SetWindowText("开启服务"); m_bar.SetText(" 空闲", 0, 0); GetDlgItem(IDC_PORT)->EnableWindow(TRUE); } } void CMainDialog::OnCancel() { CloseAllSocket(); CDialog::OnCancel(); } void CMainDialog::OnClear() { m_listInfo.ResetContent(); } long CMainDialog::OnSocket(WPARAM wParam, LPARAM lParam) { // 取得有事件发生的套节字句柄 SOCKET s = wParam; // 查看是否出错 if(WSAGETSELECTERROR(lParam)) { RemoveClient(s); ::closesocket(s); return 0; } // 处理发生的事件 switch(WSAGETSELECTEVENT(lParam)) { case FD_ACCEPT: // 监听中的套接字检测到有连接进入 { if(m_nClient < MAX_SOCKET) { // 接受连接请求,新的套节字client是新连接的套节字 SOCKET client = ::accept(s, NULL, NULL); // 设置新的套节字为窗口通知消息类型 int i = ::WSAAsyncSelect(client, m_hWnd, WM_SOCKET, FD_READ|FD_WRITE|FD_CLOSE); AddClient(client); } else { MessageBox("连接客户太多!"); } } break; case FD_CLOSE: // 检测到套接字对应的连接被关闭。 { RemoveClient(s); ::closesocket(s); } break; case FD_READ: // 套接字接受到对方发送过来的数据包 { // 取得对方的IP地址和端口号(使用getpeername函数) // Peer对方的地址信息 sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); int nSockAddrLen = sizeof(sockAddr); ::getpeername(s, (SOCKADDR*)&sockAddr, &nSockAddrLen); // 转化为主机字节顺序 int nPeerPort = ::ntohs(sockAddr.sin_port); // 转化为字符串IP CString sPeerIP = ::inet_ntoa(sockAddr.sin_addr); // 取得对方的主机名称 // 取得网络字节顺序的IP值 DWORD dwIP = ::inet_addr(sPeerIP); // 获取主机名称,注意其中第一个参数的转化 hostent* pHost = ::gethostbyaddr((LPSTR)&dwIP, 4, AF_INET); char szHostName[256]; strncpy(szHostName, pHost->h_name, 256); // 接受真正的网络数据 char szText[1024] = { 0 }; ::recv(s, szText, 1024, 0); // 显示给用户 CString strItem = CString(szHostName) + "["+ sPeerIP+ "]: " + CString(szText); m_listInfo.InsertString(0, strItem); } break; } return 0; } BOOL CMainDialog::CreateAndListen(int nPort) { if(m_socket == INVALID_SOCKET) ::closesocket(m_socket); // 创建套节字 m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(m_socket == INVALID_SOCKET) return FALSE; // 填写要关联的本地地址 sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(nPort); sin.sin_addr.s_addr = INADDR_ANY; // 绑定端口 if(::bind(m_socket, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) { return FALSE; } // 设置socket为窗口通知消息类型 ::WSAAsyncSelect(m_socket, m_hWnd, WM_SOCKET, FD_ACCEPT|FD_CLOSE); // 进入监听模式 ::listen(m_socket, 5); return TRUE; } BOOL CMainDialog::AddClient(SOCKET s) { if(m_nClient < MAX_SOCKET) { // 添加新的成员 m_arClient[m_nClient++] = s; return TRUE; } return FALSE; } void CMainDialog::RemoveClient(SOCKET s) { BOOL bFind = FALSE; for(int i=0; i<m_nClient; i++) { if(m_arClient[i] == s) { bFind = TRUE; break; } } // 如果找到就将此成员从列表中移除 if(bFind) { m_nClient--; // 将此成员后面的成员都向前移动一个单位 for(int j=i; j<m_nClient; j++) { m_arClient[j] = m_arClient[j+1]; } } } void CMainDialog::CloseAllSocket() { // 关闭监听套节字 if(m_socket != INVALID_SOCKET) { ::closesocket(m_socket); m_socket = INVALID_SOCKET; } // 关闭所有客户的连接 for(int i=0; i<m_nClient; i++) { ::closesocket(m_arClient[i]); } m_nClient = 0; }
TCPClient.h文件
#include <afxwin.h>
#include <afxcmn.h>
#include <winsock2.h>
// 告诉连接器与WS2_32库连接
#pragma comment(lib,"WS2_32.lib")
class CMyApp : public CWinApp
{
public:
BOOL InitInstance();
};
class CMainDialog : public CDialog
{
public:
CMainDialog(CWnd* pParentWnd = NULL);
protected:
// 连接服务器
BOOL Connect(LPCTSTR pszRemoteAddr, u_short nPort);
// 向文本框中添加文本
void AddStringToList(LPCTSTR pszText, BOOL bRecv = TRUE);
protected:
// 状态栏子窗口控件
CStatusBarCtrl m_bar;
// 用于与服务器取得连接的套节字句柄
SOCKET m_socket;
protected:
virtual BOOL OnInitDialog();
virtual void OnCancel();
// 取得或断开连接
afx_msg void OnButtonConnect();
// 发送数据
afx_msg void OnButtonSend();
// 清空编辑框
afx_msg void OnButtonClear();
// 套节字通知事件
afx_msg long OnSocket(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
TCPClient.cpp文件
#include "TCPClient.h"
#include "resource.h"
// 定义网络事件通知消息
#define WM_SOCKET WM_USER + 1
CMyApp theApp;
BOOL CMyApp::InitInstance()
{
// 初始化Winsock库
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 0);
::WSAStartup(sockVersion, &wsaData);
// 弹出主窗口对话框
CMainDialog dlg;
m_pMainWnd = &dlg;
dlg.DoModal();
// 释放Winsock库
::WSACleanup();
return FALSE;
}
CMainDialog::CMainDialog(CWnd* pParentWnd):CDialog(IDD_MAINDIALOG, pParentWnd)
{
}
BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
ON_BN_CLICKED(IDC_CONNECT, OnButtonConnect)
ON_BN_CLICKED(IDC_SEND, OnButtonSend)
ON_BN_CLICKED(IDC_CLEAR, OnButtonClear)
ON_MESSAGE(WM_SOCKET, OnSocket)
END_MESSAGE_MAP()
BOOL CMainDialog::OnInitDialog()
{
CDialog::OnInitDialog();
// 设置图标
SetIcon(theApp.LoadIcon(IDI_MAIN), FALSE);
// 创建状态栏,设置它的属性
m_bar.Create(WS_CHILD|WS_VISIBLE|SBS_SIZEGRIP, CRect(0, 0, 0, 0), this, 101);
m_bar.SetBkColor(RGB(0xa6, 0xca, 0xf0)); // 背景色
int arWidth[] = { 200, -1 };
m_bar.SetParts(2, arWidth); // 分栏
m_bar.SetText(" Windows程序设计进阶之路!", 1, 0); // 第一个栏的文本
m_bar.SetText(" 空闲", 0, 0); // 第二个栏的文本
// 初始化发送按钮和发送编辑框的状态
GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
GetDlgItem(IDC_TEXT)->EnableWindow(FALSE);
// 初始化连接套节字
m_socket = INVALID_SOCKET;
return TRUE;
}
void CMainDialog::OnCancel()
{
if(m_socket != INVALID_SOCKET)
::closesocket(m_socket);
CDialog::OnCancel();
}
void CMainDialog::OnButtonClear()
{
GetDlgItem(IDC_INFO)->SetWindowText("");
}
void CMainDialog::OnButtonConnect()
{
if(m_socket == INVALID_SOCKET) // 连接服务器
{
// 取得服务器地址
CString sAddr;
GetDlgItem(IDC_ADDR)->GetWindowText(sAddr);
if(sAddr.IsEmpty())
{
MessageBox("请输入服务器地址!");
return;
}
// 取得端口号
CString sPort;
GetDlgItem(IDC_PORT)->GetWindowText(sPort);
int nPort = atoi(sPort);
if(nPort < 1 || nPort > 65535)
{
MessageBox("端口号错误!");
return;
}
// 试图连接服务器
if(!Connect(sAddr, nPort))
{
MessageBox("连接服务器出错!");
return;
}
// 设置用户界面
GetDlgItem(IDC_CONNECT)->SetWindowText("取消");
m_bar.SetText(" 正在连接……", 0, 0);
}
else // 断开服务器
{
// 关闭套节字
::closesocket(m_socket);
m_socket = INVALID_SOCKET;
// 设置用户界面
GetDlgItem(IDC_CONNECT)->SetWindowText("连接服务器");
m_bar.SetText(" 空闲", 0, 0);
GetDlgItem(IDC_ADDR)->EnableWindow(TRUE);
GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
GetDlgItem(IDC_TEXT)->EnableWindow(FALSE);
GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
}
}
long CMainDialog::OnSocket(WPARAM wParam, LPARAM lParam)
{
// 取得有事件发生的套节字句柄
SOCKET s = wParam;
// 查看是否出错
if(WSAGETSELECTERROR(lParam))
{
if(m_socket != SOCKET_ERROR)
OnButtonConnect();
m_bar.SetText(" 连接出错!", 0, 0);
return 0;
}
// 处理发生的事件
switch(WSAGETSELECTEVENT(lParam))
{
case FD_CONNECT: // 套节字正确的连接到服务器
{
// 设置用户界面
GetDlgItem(IDC_CONNECT)->SetWindowText("断开连接");
GetDlgItem(IDC_ADDR)->EnableWindow(FALSE);
GetDlgItem(IDC_PORT)->EnableWindow(FALSE);
GetDlgItem(IDC_TEXT)->EnableWindow(TRUE);
GetDlgItem(IDC_SEND)->EnableWindow(TRUE);
m_bar.SetText(" 已经连接到服务器", 0, 0);
}
break;
case FD_READ: // 套接字接受到对方发送过来的数据包
{
// 从服务器接受数据
char szText[1024] = { 0 };
::recv(s, szText, 1024, 0);
// 显示给用户
AddStringToList(CString(szText) + "\r\n");
}
break;
case FD_CLOSE:
OnButtonConnect();
break;
}
return 0;
}
void CMainDialog::OnButtonSend()
{
if(m_socket == INVALID_SOCKET)
{
return;
}
// 取得要发送的字符串
CString sText;
GetDlgItem(IDC_TEXT)->GetWindowText(sText);
// 添加一个“回车换行”
// 注意,添加它并不是必须的,但是如果使用本软件作为客户端调试网络协议,
// 比如SMTP、FTP等,就要添加它了。因为这些协议都要求使用“回车换行”作为一个命令的结束标记
sText += "\r\n";
// 发送数据到服务器
if(::send(m_socket, sText, sText.GetLength(), 0) != -1)
{
AddStringToList(sText, FALSE);
GetDlgItem(IDC_TEXT)->SetWindowText("");
}
}
BOOL CMainDialog::Connect(LPCTSTR pszRemoteAddr, u_short nPort)
{
// 创建套节字
m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(m_socket == INVALID_SOCKET)
{
return FALSE;
}
// 设置socket为窗口通知消息类型
::WSAAsyncSelect(m_socket, m_hWnd,
WM_SOCKET, FD_CONNECT | FD_CLOSE | FD_WRITE | FD_READ);
// 假定szAddr是IP地址
ULONG uAddr = ::inet_addr(pszRemoteAddr);
if(uAddr == INADDR_NONE)
{
// 不是IP地址,就认为这是主机名称
// 从主机名取得IP地址
hostent* pHost = ::gethostbyname(pszRemoteAddr);
if(pHost == NULL)
{
::closesocket(m_socket);
m_socket = INVALID_SOCKET;
return FALSE;
}
// 得到以网络字节顺序排列的IP地址
uAddr = ((struct in_addr*)*(pHost->h_addr_list))->s_addr;
}
// 填写服务器地址信息
sockaddr_in remote;
remote.sin_addr.S_un.S_addr = uAddr;
remote.sin_family = AF_INET;
remote.sin_port = htons(nPort);
// 连接到远程机
::connect(m_socket, (sockaddr*)&remote, sizeof(sockaddr));
return TRUE;
}
void CMainDialog::AddStringToList(LPCTSTR pszText, BOOL bRecv)
{
CString strEdit;
GetDlgItem(IDC_INFO)->GetWindowText(strEdit);
if(bRecv)
{
strEdit += "【Recv】:";
strEdit += pszText;
}
else
{
strEdit += "【Send】:";
strEdit += pszText;
}
GetDlgItem(IDC_INFO)->SetWindowText(strEdit);
}
Windows程序设计笔记4:第10章:TCP/IP和网络通信
标签:用户 sem npe mem invalid dem char error ada
原文地址:http://www.cnblogs.com/hustercn/p/6827862.html