socket编程之实战练习

对于使用socket进行网络编程说起来还是有规律可循的,弄懂了其模式流程,你感觉也就是那个样。学习socket网络编程时弄通几个小例程对以后的学习是有很大的帮助的。

下面给出流式套接字的编程流程:

先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

对于基于UDP协议的socket编程模型服务端不需要有listen()和accept()过程,服务端和客户端是对等的,在用socket()建立套接字后就可以使用sendto()和recvfrom()收发数据了。

下面给出一个完整的socket编程的服务端和客户端的例程,注意里面的防错检查程序的编写很重要,不能少。最后不要忘记关闭socket和清理网络库!!

服务端:

#include<Winsock2.h>
#include<stdio.h>
#include<stdlib.h>

#define PORT 5000
//运行时注意连接ws2_32.lib库(可以使用#pragma comment(lib,"ws2_32.lib"),也可以在VC++6.0的工程设置中链接。后者比较麻烦,每次运行都要链接一次)
void main()
{
	int port = PORT;
	WSADATA wsaData;
	SOCKET sListen,sAccept;
	int iLen;  //客户地址长度
	int iSend;  //发送数据长度
	char buf[] = "HELLO,HOW ARE YOU!";  //指定需要发送的信息
	struct sockaddr_in serv,client;     //服务器,客户的地址
	if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0)  //初始化SOCKET库
	{
		printf("Winsock load failed\n");
		return;
	}
	sListen = socket(AF_INET,SOCK_STREAM,0);  //创建套接字
	if(sListen == INVALID_SOCKET)
	{
		printf("socket failed:%d\n",WSAGetLastError());
		return;
	}
	//建立服务器的地址结构
	serv.sin_family = AF_INET;
	serv.sin_port = htons(port);  //把一个双字节主机字节顺序的数据转换为网络字节顺序
	serv.sin_addr.s_addr = htonl(INADDR_ANY);//把四字节主机字节顺序转换为网络字节顺序,INADDR_ANY为系统指定的IP地址
	if(bind(sListen,(LPSOCKADDR)&serv,sizeof(serv)) == SOCKET_ERROR)  //绑定套接字(绑定sListen和serv)
	{
		printf("bind() failed:%d\n",WSAGetLastError());
		return;
	}
	if(listen(sListen,5) == SOCKET_ERROR)  //进入监听状态
	{
		printf("listen()failed:%d\n",WSAGetLastError());
		return;
	}
	iLen = sizeof(client);   //初始化客户地址长度
	while(1)            //进入循环,等待客户连接请求
	{
		sAccept = accept(sListen,(struct sockaddr*)&client,&iLen);  //接收客户端的连接,该函数是阻塞的,同样recv函数也是阻塞的,这也引出了I/O模型的作用。
		if(sAccept == INVALID_SOCKET)
		{
			printf("accept() failed:%d\n",WSAGetLastError());
			break;
		}
		//接收到的客户端的端口号是那边的系统分配的,输出客户IP地址和端口
		printf("accepted client IP:[%s],port:[%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
		iSend = send(sAccept,buf,sizeof(buf),0);  //给连接的客户发送信息
		if(iSend == SOCKET_ERROR)
		{
			printf("send() failed:%d\n",WSAGetLastError());
			break;
		}
		else if(iSend == 0)
			break;
		else
			printf("send() byte:%d\n",iSend);
		closesocket(sAccept);//关闭用于接收数据的socket,一定不能忘,不然会造成内存泄露
	}
	closesocket(sListen);////关闭用于监听连接的socket,一定不能忘,不然会造成内存泄露
	WSACleanup();//清理网络库
}


客户端:

#include<Winsock2.h>
#include<stdio.h>
#define PORT 5000
#define BUFFER 1024

//运行时注意连接ws2_32.lib库(可以使用#pragma comment(lib,"ws2_32.lib"),也可以在VC++6.0的工程设置中链接。后者比较麻烦,每次运行都要链接一次)
void main(int argc,char *argv[])
{
	WSADATA wsaData;
	SOCKET client;
	int port = PORT;
	int iLen;  //从服务器接收的数据长度
	char buf[BUFFER];   //接收数据的缓冲
	struct sockaddr_in serv;   //服务器端地址结构
	memset(buf,0,sizeof(buf));  //接收数据缓冲区初始化
	if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0)
	{
		printf("Winsock load failed\n");
		return;
	}
	//创建需要连接服务器的地址结构
	serv.sin_family = AF_INET;  
	serv.sin_port = htons(port); //把一个双字节主机字节顺序的数据转换为网络字节顺序
	serv.sin_addr.s_addr = inet_addr(argv[1]);  //将命令行的IP地址转化为二进制表示的网络字节顺序IP地址
	client = socket(AF_INET,SOCK_STREAM,0);   //建立客户端流套接字
	if(client == INVALID_SOCKET)
	{
		printf("socket() failed:%d\n",WSAGetLastError());
		return;
	}
	if(connect(client,(struct sockaddr*)&serv,sizeof(serv)) == INVALID_SOCKET)  //请求与服务器建立TCP连接
	{
		printf("connect() failed:%d\n",WSAGetLastError());
		return;
	}
	else
	{
		iLen = recv(client,buf,sizeof(buf),0);   //从服务器接收数据
		if(iLen == 0)
			return;
		else if(iLen == SOCKET_ERROR)
		{
			printf("recv() failed:%d\n",WSAGetLastError());
			return;
		}
		printf("recv() data from server:%s\n",buf);
	}
	closesocket(client);//关闭socket,一定不能忘,不然会造成内存泄露
	WSACleanup();//清理网络库
	//让程序等待
	printf("press any key to continue");
	while(1);
}


在网络编程中经常出错的一个地方就是网络字节顺序的问题。主要原因是计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换,否则就会出现数据不一致。
   下面是几个字节顺序转换函数:
·htonl():把32位值从主机字节序转换成网络字节序
·htons():把16位值从主机字节序转换成网络字节序
·ntohl():把32位值从网络字节序转换成主机字节序
·ntohs():把16位值从网络字节序转换成主机字节序

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章