Socket
网络连接函数
#include
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2,2);
if(WSAStartup(wVersionRequested,&wsaData) != 0)
{
//Winsock初始化错误
return;
}
if(wsaData.wVersion != wVersionRequested)
{
//Winsock版本不匹配
WSACleanup();
return;
}
//说明WinsockDLL正确加载,可以执行以下代码
创建套接口socket()
将套接口与该主机上提供服务的某端口联系在一起。
函数原型:
int bind( SOCKET s, const struct sockaddr FAR * name, int namelen);
返回值:成功返回0,失败返回SOCKET_ERROR,使用WSAGetLastError()获得具体的错误号。
函数参数:
把"xxx.xxx.xxx.xxx"的10进制的IP地址转换为32位整数表示方法
函数原型:
unsigned long inet_addr( const char FAR *cp);
函数参数:cp代表IP地址的点格式的字符串。
返回值:成功返回用32位整数表示的IP地址(按网络字节排列顺序),失败返回INADDR_NONE
在一个服务器端用socket()调用成功创建了一个套接口,并用bind()函数和一个指定的地址关联后,就需要指示该套接口进入监听连接请求状态,这需要通过listen()函数来实现
函数原型:
int listen(SOCKET s,int backlog);
函数参数:
当服务器端建立好套接口并与一个本地地址绑定后,就进入监听状态,等待客户发出连接请求。在客户端套接口建立好之后,就调用connect()函数来与服务器建立连接。
函数原型:
int connect( SOCKET s,const struct sockaddr FAR * name,int namelen);
函数参数:
在客户端使用该函数请求建立连接时,将激活建立连接的三次握手,用来建立一条到服务器TCP的连接。如果调用该函数前没有调用bind()来绑定本地地址,则由系统隐式绑定一个地址到该套接口
该函数用在UDP的客户端时,connect()函数并不是真正地发出建立请求连接的请求,调用将从本地操作系直接返回。这样可以将服务器的地址信息保存下来,在后续UDP端口发送数据时,由套接口自动在发送函数中填入服务器地址,而不需要由应用程序在调用发送函数时填入.
在服务器端通过listen()函数调用表示服务器进入监听客户的连接请求状态,而在服务器端调用accept()函数时表示可以接收来自客户端由connect()发出的连接请求,双方进入连接状态。
函数原型:
SOCKET accept(SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen);
函数参数:
返回值:请参考socket()函数。
备注:该函数用于面向连接的服务器端,在IP协议族中,只用于TCP服务器端
在已经建立连接的套接口上发送数据,可以使用send()函数
函数原型:int send( SOCKET s, const char FAR * buf, int len, int flags);
函数参数:
对于已建立连接的套接口来说,要从套接口上接收数据,就要使用recv()函数。
函数原型:int recv(SOCKET s,char FAR * buf, int len,int flags);
函数参数:
在一个套接口上的读写操作完成后,应该首先使用shutdown()函数来关闭套接口的读通道、写通道或读写通道,这样做的好处是当双方不再有数据要发送或接收时,可以通知对方,以防止数据丢失,并能“优雅”地关闭连接。
函数原型:
int shutdown(SOCKET s,int how );
返回值:请参考bind()函数。
shutdown函数只关闭读写通道,并不关闭套接口,且套接口所占有的资源将被一直保留到closesocket()调用之前。一个套接口不再使用时一定要关闭这个套接口,以释放与该套接口关联的所有资源,包括等候处理的数据。
函数原型:
int closesocket(SOCKET s );
函数参数:s表示即将被关闭的套接口
返回值:请参考bind()函数
recvfrom()
对于无连接的套接口来说,要从套接口上接收一个数据报并保存发送数据的源地址,就要使用recvfrom()函数。
函数原型:int recvfrom( SOCKET s,char FAR * buf,
int len,int flags,
struct sockaddr FAR * from,
int FAR * fromlen);
函数参数:
sendto()
对于无连接的套接口来说,要从套接口上发送一个数据报,就要使用sendto()函数
函数原型:int sendto(SOCKET s,const char FAR * buf,
int len,int flags,
const struct sockaddr FAR * to,int tolen);
函数参数:
下面是一个简单的服务器端与客户端的实现
//Server
#pragma comment(lib, "ws2_32.lib")
#include
#include
using namespace std;
//处理服务
void do_service(SOCKET conn)
{
char buf[1024] = { 0 };
while (1)
{
//接收
int ret = recv(conn, buf, sizeof(buf), 0);
if (ret == SOCKET_ERROR)
{
cout << "error with code = " << WSAGetLastError() << endl;
exit(1);
}
if (ret == 0)
{
cout << "client close" << endl;
break;
}
if (ret > 0)
{
cout << buf << endl;
send(conn, buf, strlen(buf), 0); //回射回去
}
memset(buf, 0, sizeof buf); //置0
}
closesocket(conn);
}
int main(void)
{
WORD wVersionRequested; //两个字节,指定使用的版本号
WSADATA wsaData; //WSADATA 结构存储 Windows 套接字调用返回的初始化信息对
int err;
//makeword(a,b)是将两个byte型合并成一个word型,一个在高8位(b),一个在低8位(a)
wVersionRequested = MAKEWORD(2, 2); //指定版本号
err = WSAStartup(wVersionRequested, &wsaData); //启动Windows Socket
if (err != 0) {
return 1;
}
//检查协议栈的安装信息
if (LOBYTE(wsaData.wVersion) != 2 ||
HIBYTE(wsaData.wVersion) != 2) {
WSACleanup();
return 1;
}
//或者使用下面的方法
//if (wsaData.wVersion != wVersionRequested)
//{
// //Winsock版本不匹配
// WSACleanup();
// return 1;
//}
SOCKET listenfd; //创建socket
listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //TCP协议
if (listenfd == INVALID_SOCKET) //创建失败
{
cout << "error with code = " << WSAGetLastError() << endl; //WSAGetLastError()获取错误码
exit(1);
}
sockaddr_in servaddr; //服务器地址
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //地址族
//servaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //指定IP地址 4字节主机字节序转换为网络字节序
servaddr.sin_port = htons(8888); //端口号 2字节主机字节序转换为网络字节序
int ret;
int opt = 1;
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)); //设置地址重复利用
if (ret == SOCKET_ERROR)
{
cout << "error with code = " << WSAGetLastError() << endl;
exit(1);
}
//绑定
ret = bind(listenfd, (sockaddr*)&servaddr, sizeof(servaddr));
if (ret == SOCKET_ERROR)
{
cout << "error with code = " << WSAGetLastError() << endl;
exit(1);
}
//监听
ret = listen(listenfd, SOMAXCONN);
if (ret == SOCKET_ERROR)
{
cout << "error with code = " << WSAGetLastError() << endl;
exit(1);
}
SOCKET conn;
sockaddr_in peeraddr;
int peerlen;
while (1)
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (sockaddr*)&peeraddr, &peerlen);
if (conn == INVALID_SOCKET)
{
cout << "error with code = " << WSAGetLastError() << endl;
exit(1);
}
//将一个十进制网络字节序转换为点分十进制IP格式的字符串 2字节网络字节序转换为主机字节序
cout << inet_ntoa(peeraddr.sin_addr) << " " << ntohs(peeraddr.sin_port) << endl;
//cout << inetNtop(peeraddr.sin_addr) << " " << ntohs(peeraddr.sin_port) << endl;
do_service(conn);
}
WSACleanup();
return 0;
}
//Client
#pragma comment(lib, "ws2_32.lib")
#include
#include
using namespace std;
int main(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2); //514
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
return 1;
}
if (LOBYTE(wsaData.wVersion) != 2 ||
HIBYTE(wsaData.wVersion) != 2) {
WSACleanup();
return 1;
}
SOCKET sock;
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
{
cout << "1error with code = " << WSAGetLastError() << endl;
exit(1);
}
sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
//servaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_port = htons(8888);
int ret;
ret = connect(sock, (sockaddr*)&servaddr, sizeof(servaddr));
if (ret == -1)
{
cout << "2error with code = " << WSAGetLastError() << endl;
exit(1);
}
char buf[1024] = { 0 };
char recvbuf[1024] = { 0 };
while (1)
{
cin >> buf;
if (strcmp(buf, "quit") == 0)
break;
//发送
ret = send(sock, buf, strlen(buf), 0);
if (ret == -1)
{
cout << "3error with code = " << WSAGetLastError() << endl;
exit(1);
}
//接收
ret = recv(sock, recvbuf, sizeof(buf), 0);
if (ret == -1)
{
cout << "4error with code = " << WSAGetLastError() << endl;
exit(1);
}
if (ret == 0)
{
cout << "server close" << endl;
break;
}
if (ret > 0)
{
cout << recvbuf << endl;
}
memset(buf, 0, sizeof buf);
memset(recvbuf, 0, sizeof recvbuf);
}
//关闭
closesocket(sock);
return 0;
}
对线程见 多线程编程