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位值從網絡字節序轉換成主機字節序

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