您的位置:首页 > 理论基础 > 计算机网络

简单的TCP回射服务程序与客户程序(修改自UNP一书)

2012-03-30 01:16 405 查看
#ifndef INITSOCK_H_
/*	---------------------------------------------------
	文件:InitSock.h
	功能简述:载入winsock库

	---------------------------------------------------	*/

#define INITSOCK_H_

#include<windows.h>
#include<cstdio>
#include<process.h>

class CInitSock
{
public :
	CInitSock(BYTE MinorVer = 2  , BYTE MajorVer = 2)
	{
		WORD SocketVersion = MAKEWORD(MinorVer,MajorVer) ;
		
		if(WSAStartup(SocketVersion,&wsaData) != 0 )
		{
			printf("加载Winsock库失败\n") ;
			exit(0) ;
		}
	}
	
	~CInitSock()
	{
		WSACleanup() ;
	}

private :
	WSADATA wsaData ;
} ;

typedef unsigned (_stdcall *PTHREAD_START) (void *) ;

#define chBEGINTHREADEX(psa,cbStackSize,pfnStartAddr,	\
			pvParam,fdwCreateFlags,pdwThreadID)			\
		((HANDLE) _beginthreadex(					\
			(void*)(psa),							\
			(unsigned)(cbStackSize),				\
			(PTHREAD_START)(pfnStartAddr),			\
			(void *)(pvParam),						\
			(unsigned)(fdwCreateFlags),				\
			(unsigned *)(pdwThreadID)))				

#endif



/*	-------------------------------
	文件:tcpserver.cpp
	  
	TCP回射服务程序:
	  
	经验:

	1.如果客户端主动关闭,则可以看到客户端TCP状态转换,由FIN_WAIT1
	  到TIME_WAIT。
	2.如果服务端主动关闭,用netstat观察TCP状态,没有见到任何状态
	  转换,初步估计是因为服务端和客户端运行在同一台主机上面的,在很
	  短的时间内,很快完成了4次握手的过程,所以我看不见.
	3.在beginthreadex之后不能马上调用closesocket来关闭内核对象。因为
	  在创建新线程的时候,它的引用计数没有增加。所以如果用closesocket的话
	  就会使套接字变为无效的套接字。内核对象的继承应该是在进程之间。
	4.在客户端主动退出之后,服务端也要关闭相应诉套接字,这样服务端的状态
	  才会从closewait离开
	5.用beginthreadex开新线程的时候,线程参数的传递用的是指针传递,所以不
	  用想得太复杂,直接用传地址。然后在线程函数内强制转型即可。
	6.网络状态可以用netstat ,ipconfig之类的工具来查看,虽然没有Linux
	  下那些那么强大.
    7.如果服务端被强制退出,客户发送数据将会收到复位错误。
	-------------------------------	*/

#include<winsock2.h>
#include"InitSock.h"
#include<windows.h>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<process.h>

#pragma comment(lib,"ws2_32.lib")

#define SEVER_PROT 5010
#define MAXLINE 1024

DWORD WINAPI str_echo(PVOID pvParam) ;

CInitSock InitSock ;

int main(void)
{
	SOCKET sListenfd , sConnectfd ;	
	int iClilen = 0 ;
	DWORD dwThreadId = 0 ;
	sockaddr_in Cliaddr , Servaddr ;
	DWORD dwError = 0 ;
	HLOCAL hHlocal = NULL ;
	HANDLE	hThread = NULL ;

	sListenfd = socket(AF_INET,SOCK_STREAM,0) ;

	memset(&Servaddr,0,sizeof(Servaddr)) ;
	memset(&Cliaddr,0,sizeof(Cliaddr)) ;

	Servaddr.sin_family = AF_INET ;
	Servaddr.sin_port = htons(SEVER_PROT) ;
	Servaddr.sin_addr.s_addr = htonl(INADDR_ANY) ;

	if(SOCKET_ERROR == bind(sListenfd,(SOCKADDR *)&Servaddr,sizeof(Servaddr)))
	{
		dwError = WSAGetLastError() ;
		printf("绑定错误,错误代码为: %d\n",dwError) ;
		return -1 ;
	}

	if(SOCKET_ERROR == listen(sListenfd,5))
	{
		dwError = WSAGetLastError() ;
		printf("监听错误,错误代码为 : %d\n",dwError) ;
		return -1 ;
	}
	
	for( ; ; )
	{
		iClilen = sizeof(Cliaddr) ;
	
		printf("等待一个新连接....\n") ;
		sConnectfd = accept(sListenfd,(SOCKADDR *)&Cliaddr,&iClilen) ;

		if(SOCKET_ERROR == sConnectfd)
		{
			dwError = WSAGetLastError() ;
			printf("连接错误,错误代码为 : %d\n",dwError) ;

			closesocket(sConnectfd)  ;
			continue ;
		}
		else
		{
			hThread = chBEGINTHREADEX(NULL,0,str_echo,(PVOID)&sConnectfd,0,&dwThreadId) ;
//			closesocket(sConnectfd) ;								//为什么不能减少他的引用计数呢 ?
			CloseHandle(hThread) ;
		}

	}	

	closesocket(sListenfd) ;

	return 0 ;
}

DWORD WINAPI str_echo(PVOID pvParam)
{
	SOCKET  sConnfd = *((SOCKET *)pvParam) ;		//这里转型....
		
	int n = 0 ;  //改为int 就不会死循环了,因为下面可以返回-1
	TCHAR szBuf[MAXLINE] ;

	while((n = recv(sConnfd,szBuf,sizeof(szBuf),0)) > 0 )		//客户端突然退出会发生死循环
	{
		szBuf
 = '\0' ;
		printf("套接口 %d : 接收到 %u 字节的数据,内容为:%s\n",sConnfd,n,szBuf) ;
		send(sConnfd,szBuf,n,0) ;
	}

	closesocket(sConnfd) ;						//如果不加这一句的话,就会造成半关闭,服务器一直
												//停留在CLOSE_WAIT

	return 0 ;
}



/*	--------------------------------------------------------------
	文件:tcpclient.cpp

	简单的TCP回射客户程序

	经验:回车符也被当作一个字符来发送

	--------------------------------------------------------------	*/
#include<winsock2.h>
#include"InitSock.h"
#include<windows.h>
#include<cstdio>
#include<cstdlib>
#include<cstring>

#pragma comment(lib,"ws2_32.lib")

#define SERVER_PORT  5010
#define MAXLINE	1024

void str_cli(FILE *fp , SOCKET sockfd) ;

CInitSock InitSock ;

int main(void)
{
	SOCKET sConnfd ;
	sockaddr_in	Servaddr ;
	DWORD dwError = 0 ;

	memset(&Servaddr,0,sizeof(Servaddr)) ;
	
	sConnfd = socket(AF_INET,SOCK_STREAM,0) ;

	Servaddr.sin_family = AF_INET ;
	Servaddr.sin_port = htons(SERVER_PORT) ;
	Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1") ;

	if(SOCKET_ERROR == connect(sConnfd,(SOCKADDR *)&Servaddr,sizeof(Servaddr)) )
	{
		dwError = WSAGetLastError() ;
		printf("连接出错,错误代码: %d\n",dwError) ;
		return -1 ;
	}

	str_cli(stdin,sConnfd) ;

	closesocket(sConnfd) ;

	return 0 ;
}

void str_cli(FILE *fp , SOCKET sockfd) 
{
	TCHAR szSendLine[MAXLINE] ;
	TCHAR szRecvLine[MAXLINE] ;
	int nRecv = 0;

	while(fgets(szSendLine,MAXLINE,fp) != NULL)
	{
		szSendLine[strlen(szSendLine)] = '\0' ;
		nRecv = send(sockfd,szSendLine,strlen(szSendLine),0) ; 		//最后一个字符是回车符
		if((nRecv = recv(sockfd,szRecvLine,MAXLINE,0)) == 0)
		{
			puts("程序退出.") ;
			return  ;
		}	
		
		szRecvLine[nRecv] = '\0' ;

		fputs(szRecvLine,stdout) ;
	}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: