1、网络编程之基于TCP

一、一些概念 

1Socket是连接应用程序与网络驱动程序的桥梁,Socket在应用程序中创建,通过绑定操作与驱动程序建立关系。

网络体系结构就是使用这些用不同媒介连接起来的不同设备和网络系统在不同的应用环境下实现互操作性,并满足各种业务需求的一种粘合剂。网络体系结构解决互质性问题彩是分层方法。

网络的7层:

应用层--->为应用程序提供网络通信服务

表示层--->数据表示

会话层--->主机间通信(两个应用进程间)

传输层--->端到端的连接,隔离网络的上下层协议,使得网络应用与下层协议无关

网络层--->寻找最优路径,转发数据包

数据链路层--->无差错的链路连接

物理层--->二进制传输

2、端口

是一种抽象的软件结构,包括一些数据结构和I/O缓冲区。与协议有关。

3、套接字存在于通信区域中。通信区域也叫地址族,它是一个抽象的概念,主要用于将通过套接字通信的进程的共有特性综合在一起。

为保证数据的正确性,在网络协议中需要制定网络字节顺序,采用统一的网络字节顺序。

二、socket的连接方式

1、基于TCP的

服务器端程序:

1)加载套接字库

2)创建套接字(socket)。

3)将套接字绑定到一个本地地址和端口上(bind)。

4)将套接字设为监听模式,准备接收客户请求(listen)。

5)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。

6)用返回的套接字和客户端进行通信(send/recv)。

7)返回,等待另一客户请求。

8)关闭套接字。

客户端程序:

1)加载套接字库

2)创建套接字(socket)。

3)向服务器发出连接请求(connect)。

4)和服务器端进行通信(send/recv)。

5)关闭套接字。

服务器端调用accept函数时会等待,连接。

客客端调用connect函数时,发出连接请求,服务器端接受,这样双方就建立起了连接。

2、相关函数

(1)htonl把一个u_long类型从主机字节序转换为网络字节序

(2)htons把一个u_short类型从主机字节序转换为网络字节序(2Bytes)

WSAStartup把其加载的苦版本的有关信息添在这个结构中对于每一个WSAStartup的成功调用,最后都应该调用WSACleanUp以便释放资源如果调用成功,将返回套接字描述符。如果调用失败,将返回INVALID_SOCKET,错误信息可通过WSAGetLastError返回。

inet_ntoa:The Windows Sockets inet_ntoa function converts an (Ipv4) Internet network address into

 a string in Internet standard dotted format.

net_addr:The Windows Sockets inet_addr function converts a string containing an (Ipv4) Internet

 Protocol dotted address into a proper address for the IN_ADDR structure.

三、例程:

服务器端:

#include <Winsock2.h>
#include <stdio.h>
main(){
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD( 1, 1);
	int err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) { return;}
	if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {
		WSACleanup( );return;
	}
	SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0); //第三个参数为零表示自动选择协议
	SOCKADDR_IN addrSrv; //定义一个地址结构体的变量
	addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(6000);//htons把一个u_short类型从主机字节序转换为网络字节序
	bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	listen(sockSrv,5);
	SOCKADDR_IN addrClient;
	int len=sizeof(SOCKADDR);
	while(1) {
		SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);//利用本套接字与客户端通信,原来的套接字继续监听。
		char sendBuf[100];
		sprintf(sendBuf,"Welcome %s to here",inet_ntoa(addrClient.sin_addr));
		send(sockConn,sendBuf,strlen(sendBuf)+1,0);
		char recvBuf[100];
		recv(sockConn,recvBuf,100,0);
		printf("%s\n",recvBuf);
		closesocket(sockConn);
	}
}
客户端:
#include <Winsock2.h>
#include <stdio.h>
void main()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD( 1, 1 );
	err = WSAStartup( wVersionRequested, &wsaData );加载套接字库
		if ( err != 0 ) {
			return;
		}
		if ( LOBYTE( wsaData.wVersion ) != 1 ||
			HIBYTE( wsaData.wVersion ) != 1 ) {
				WSACleanup( );
				return;
		}
		SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);创建套接字(socket)。
			SOCKADDR_IN addrSrv;
		addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
		addrSrv.sin_family=AF_INET;
		addrSrv.sin_port=htons(6000);
		connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));向服务器发出连接请求(connect)。
			char recvBuf[100];//和服务器端进行通信(send/recv)。
		recv(sockClient,recvBuf,100,0);
		printf("%s\n",recvBuf);
		send(sockClient,"This is me",strlen("This is me")+1,0);
		closesocket(sockClient);关闭套接字。
			WSACleanup();//必须调用这个函数清除参数
}
// SocketServer.cpp : 定义控制台应用程序的入口点。
//
#include "winsock2.h"
#include "iostream"
#include "Ws2tcpip.h"
using namespace std;

#pragma comment(lib, "ws2_32.lib")
const int MAX_REQUEST = 1024;
const int BUF_SIZE = 4096;
const char* DEFAULT_PORT = "1000";
//服务端
int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET SocketForListen = INVALID_SOCKET;
	SOCKET SocketForClient = INVALID_SOCKET;
	struct addrinfo *result = NULL, hints;
	int iResult;

	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (0 != iResult)
	{
		cout << "WSAStartup() Failed: " << iResult << endl;
		return 1;
	}

	SOCKADDR_IN AddrServerService; //地址
    AddrServerService.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	AddrServerService.sin_family = AF_INET;
	AddrServerService.sin_port = htons(1000);
    
	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = AI_PASSIVE;
	iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
	if (0 != iResult)
	{
		cout << "getaddrinfo failed: " << iResult << endl;
		WSACleanup();
		return 1;
	}
	SocketForListen = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (INVALID_SOCKET == SocketForListen)
	{
		cout << "socket failed: " << WSAGetLastError() << endl;
		freeaddrinfo(result);
		WSACleanup();
		return 1;
	}

	iResult = bind(SocketForListen, result->ai_addr, (int)result->ai_addrlen);
	if (SOCKET_ERROR == iResult)
	{
		cout << "bind failed: " <<  WSAGetLastError() << endl;
		freeaddrinfo(result);
		closesocket(SocketForListen);
		WSACleanup();
		return 1;
	}
	cout << "Bind" << endl;
	freeaddrinfo(result);
	iResult = listen(SocketForListen, SOMAXCONN);
	cout << "Start Listen..." << endl;
	if (iResult == SOCKET_ERROR)
	{
		cout << "Listen failed: " <<  WSAGetLastError() << endl;
		closesocket(SocketForListen);
		WSACleanup();
		return 1;
	}

	while(1)
	{
		cout << "Ready to accept: " << endl;
		SocketForClient = accept(SocketForListen, NULL, NULL);
		cout << "Accept a connection" << endl;
		if (INVALID_SOCKET == SocketForClient)
		{
			cout << "accept failed: " << WSAGetLastError() << endl;
			closesocket(SocketForListen);
			break;// 等待连接错误,退出循环
		}
		//可以在这里为每一个连接创建一个数据发送的接收线程,使服务端可以立即接收其它客户
		//端的连接
		char *szRecvBuf = NULL;
		szRecvBuf = new char[MAX_REQUEST];
		iResult = recv(SocketForClient, szRecvBuf, MAX_REQUEST, 0);
		if (0 == iResult) // connection has been closed
		{
			cout << "Connection closing..." << endl;
			delete[] szRecvBuf;
			closesocket(SocketForClient);
			return 1;
		}
		else if (SOCKET_ERROR == iResult) //recv error,socket error
		{
			cout << "recv Failed" << endl;
			delete[] szRecvBuf;
			closesocket(SocketForClient);
			return 1;
		}
		else if (iResult > 0) //接收成功
		{
			cout << iResult << " Has been reveived: " << szRecvBuf;
		}	
		closesocket(SocketForClient);
	}
	return 0;
}
// TestSocket.cpp : 定义控制台应用程序的入口点。
//

#include <stdio.h>
#include "winsock2.h"
#include "iostream"
using namespace std;
#define RECV_BUFFER_SIZE 8192
#pragma  comment(lib, "ws2_32.lib")

//客户端
int main(int argc, char* argv[])
{
	WSADATA wsaDate; //库
	int iResult = WSAStartup(MAKEWORD(2, 2), &wsaDate); //initiate socket libary.
	if (iResult != 0)
	{
		cout << "Error at WSAStartUP()\n";
	}
	//if (LOBYTE(wsaDate.wVersion) != 2 || HIBYTE(WSADATA.wVersion) != 2)
	//{
	//	WSACleanup();
	//	return 1;
	//}

	SOCKET ConnectSocket; //socket
	ConnectSocket = socket(AF_INET, //IPV4
		SOCK_STREAM, //TCP
		0);
	if (ConnectSocket == INVALID_SOCKET)
	{
		cout << "Error at Socket(): " << WSAGetLastError();
		WSACleanup();
		return 1;
	}

	//set the client's protocol,address,port
	SOCKADDR_IN AddrClientService; //地址
	AddrClientService.sin_family = AF_INET;
	AddrClientService.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	AddrClientService.sin_port = htons(1000); 

	char* recvbuf = NULL;  //receive buffer
	int iBytesSent;
	int iBytesRecv = 0;
	char cSendBuf[32] = "Get Information "; //默认发送的数据

	if (connect(ConnectSocket,
				(SOCKADDR*)&AddrClientService,
				sizeof(AddrClientService)) == SOCKET_ERROR)
	{
		cout << "Failed To Connect" << endl;
		WSACleanup();
		return 1;
	}

	iBytesSent = send(ConnectSocket, cSendBuf, strlen(cSendBuf)+1, 0);
	if (iBytesSent == SOCKET_ERROR)
	{
		cout << "send error " << WSAGetLastError() << endl;
		closesocket(ConnectSocket);
		return 1;
	}
	cout << "Bytes Sent: " << iBytesSent << endl;

	recvbuf = new char[RECV_BUFFER_SIZE];
	while(iBytesRecv != SOCKET_ERROR)
	{
		iBytesRecv = recv(ConnectSocket, recvbuf, RECV_BUFFER_SIZE, 0);
		if (iBytesRecv == 0)
		{
			cout << "Connection Closed" << endl;
			break;
		}
		cout << "Bytes recv: " << iBytesRecv << endl;
	}
	delete[] recvbuf;
	WSACleanup();
	return 0;
}

参考

[1]孙鑫的《VC++深入》

[2] 对于socket,讲得非常好

http://www.cnblogs.com/goodcandle/archive/2005/12/10/socket.html#2025579

上一篇:4、对变量在栈上存储顺序,及函数返回值与参数在栈上存放顺序的思考(2)


下一篇:[译]15个关于Chrome的开发必备小技巧