#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[n] = '\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) ;
}
}