(1)在windows下进行网络编程,必须包括头文件winsock2.h和语句#parmga comment(lib,""win2_32.lib")
并且在程序开始的地方写上WSAStartup()
(2)以太网地址结构为
struct sockaddr_in
{
short sin_family;/* 地址族,因为是IP协议,所以必须为AF_INET */
unsigned short sin_port; /* 端口号 */
struct in_addr sin_addr;/* IP地址 */
unsigned char sin_zero[8];/* 填充 */
};
struct in_addr{unsigned long s_addr;}
这个结构与“127.0.0.1”这种结构的IP地址不同,它是一个无符号长整数,于是我们在使用的时候需要将字符串形式表示的IP地址和32位二进制地址时需要进行转换
int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);
(3)字节顺序
不同类型机器间的字节顺序是不同的,socket编程函数提供了字节转换的函数,就算本机的内部字节表示顺序与网络字节顺序相同也应该在传输数据之前先调用数据转换函数
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);
UDP编程的几个阶段
服务器先创建Socket 然后用bind()函数将服务器地址和套接字绑定起来
客户端创建Socket 设置好服务器端的地址后,直接使用sendto函数将信息发送到服务端
服务器端使用recvfrom接受
代码
首先需要在程序开始执行的地方加入(客户端和服务器端均可)
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
客户端代码
SOCKET s = socket(AF_INET,SOCK_DGRAM, 0);
SOCKADDR_IN server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(10000);
server_addr.sin_addr.S_un.S_addr = inet_addr("192.168.24.44");
int ret = sendto(s,"hello",5,0,(sockaddr *)&server_addr,sizeof(server_addr));
if (ret == SOCKET_ERROR)
{
MessageBox("send error");
return;
}
服务器端代码
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
char buf[100] = {0};
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = 10000;
server_addr.sin_addr.S_un.S_addr = inet_addr("192.168.24.44");
int r = bind(s,(sockaddr*)&server_addr,sizeof(server_addr));
if (r != 0)
{
MessageBox("bind error");
return;
}
sockaddr_in client_addr;
int client_len = sizeof(client_addr);
int ret = recvfrom(s, buf, 100, 0, (sockaddr *)&client_addr, &client_len);
if (ret == SOCKET_ERROR)
{
MessageBox(_T("recv error"));
return;
}
TCP的socket编程较UDP更为复杂,
服务器端
1)使用socket初始化套接字
2)使用bind将服务器端IP信息和套接字绑定
3)使用listen侦听客户端建立连接请求
4)使用accept处理收到的客户端的请求
5)使用recv和send接受和发送数据,和客户端通信
6)使用close关闭并释放socket
客户端比较容易,先使用socket初始化套接字后,直接connect连接服务器,连接好后就可以recv和send了
void CTCPServerDlg::OnBnClickedOk()
{
SOCKET serverSocket,clientSocket;
sockaddr_in serverAddr;
int error;
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0)
{
MessageBox(_T("server socket error"));
return;
}
int reuse = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, ( const char* )&reuse, sizeof(reuse));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(10000);
if ((bind(serverSocket, (sockaddr *)&serverAddr, sizeof(serverAddr))) < 0)
{
error = WSAGetLastError();
return;
}
if (listen(serverSocket,5) < 0)
{
MessageBox(_T("listen error"));
return;
}
while (1)
{
clientSocket = accept(serverSocket, NULL, NULL);
if (clientSocket < 0)
{
MessageBox(_T("accept error"));
return;
}
doClientRequest(clientSocket);
}
OnOK();
}
void CTCPServerDlg::doClientRequest(int clientSocket)
{
char buf[100];
int n,error;
while(1)
{
memset(buf,0,100);
n = recv(clientSocket, buf, 100, 0);
if (n<0)
{
error = WSAGetLastError();
MessageBox(_T("recv error "));
return;
}
if (n==0)
{
MessageBox(_T("client exit"));
return;
}
MessageBox((buf));
}
}
void CTCPClientDlg::OnBnClickedOk()
{
int clientSocket;
sockaddr_in serverAddr;
clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket < 0)
{
MessageBox(_T("client socket error"));
return;
}
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.S_un.S_addr = inet_addr("192.168.24.178");
serverAddr.sin_port = htons(8010);
if (connect(clientSocket,(sockaddr *)&serverAddr,sizeof(serverAddr)) < 0)
{
MessageBox(_T("connect error"));
return;
}
doRequest(clientSocket);
OnOK();
}
void CTCPClientDlg::doRequest(int clientSocket)
{
char buf[100];
strcpy(buf,"hellotoni");
if(send(clientSocket,buf,sizeof(buf),0) < 0)
{
MessageBox(_T("send error"));
return;
}
}
最开始服务器端一直报bind error错误,使用WSAGetLastError()函数,发现错误代码10093,还未执行的成功 WSAStartup。
原来是忘记了在程序起始添加WSAStartup