用一个简单的程序来学习VC Socket编程。
本程序实现一个echo服务,即回送服务,客户端向服务器端发送一段数据,服务器收到后,立即将该段数据回送给客户端。
在Visual Studio 2010开发环境中,新建一个project,选择Visual C++/Win32/Win32 Console Application,project name是echoServer,同样的方法,新建一个echoClient项目。
echoServer.cpp文件中的代码如下:
// echoServer.cpp : 这是一个控制台程序,使用socket编程方法,实现echo服务的服务器端
//本程序的socket是基于TCP的流式套接字
#include "stdafx.h"
#include
//链接一个ws2_32.lib的库文件
#pragma comment (lib,"ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
if (argc<2){
printf("请附带本程序要监听的端口参数。/n");
return -1;
}
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
//加载套接字库,创建套接字
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
printf("创建socket失败。");
return err;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup();
printf("创建socket失败。");
return -1;
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
//取本机的IP地址作为socket监听地址
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
//用第一个命令行参数作为监听的端口
int port=atoi(argv[1]);
addrSrv.sin_port=htons(port);
//绑定套接字到一个IP地址和一个端口上
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
printf("本echo服务程序正在监听端口:%d/n",port);
//将套接字设置为监听模式等待连接请求
listen(sockSrv,5);
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
//以一个无限循环的方式,不停地接收客户端socket连接
while(1)
{
//请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
//接收缓冲区的大小是50个字符
char recvBuf[50];
recv(sockConn,recvBuf,50,0);
//发送信息的缓冲区的大小也是50个字符
char sendBuf[50];
//准备要发送的信息,只是将接收到信息原样返回给客户端
sprintf(sendBuf,"%s",recvBuf);
send(sockConn,sendBuf,strlen(sendBuf)+1,0);
printf("接收来自客户端%s的信息:%s/n",inet_ntoa(addrClient.sin_addr),recvBuf);
//结束连接
closesocket(sockConn);
}
return 0;
}
此程序存在一些不足:
1、如果第一个命令行参数不是一个有效的TCP端口数字时,程序会以0作为端口。
2、当端口已经正被占用时,程序不能正常工作,不停地有提示:"接收来自客户端204.204.204.204的信息:烫烫烫烫......"
3、没有采用多线程来处理客户端的socket连接(实际上这个简单的应用没有必要用到多线程,因为客户端对服务器的连接非常短暂)
echoClient.cpp文件中的代码如下:
// echoClient.cpp : 这是一个控制台程序,使用socket编程方法,实现echo服务的客户端
//本程序的socket是基于TCP的流式套接字
#include "stdafx.h"
#include
//需要链接一个ws2_32.lib的库文件
#pragma comment (lib,"ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
if (argc<3){
printf("请附带三个参数:服务器端程序监听的IP地址和端口,以及回显的信息。/n");
return -1;
}
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
printf("创建socket失败。");
return err;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup();
printf("创建socket失败。");
return -1;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
//用第一个命令行参数作为要连接服务器程序的IP地址
SOCKADDR_IN addrSrv;
char* ip=argv[1];
addrSrv.sin_addr.S_un.S_addr=inet_addr(ip);
addrSrv.sin_family=AF_INET;
//用第二个命令行参数作为连接服务器端程序的端口
int port=atoi(argv[2]);
addrSrv.sin_port=htons(port);
//向服务器发出连接请求
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
//第三个命令行参数是要发送给服务器端程序的信息
char* msg=argv[3];
send(sockClient,msg,strlen(msg)+1,0);
//接收来自服务器端程序的信息
char recvBuf[50];
recv(sockClient,recvBuf,50,0);
//输出回显
printf("%s/n",recvBuf);
//关闭socket连接
closesocket(sockClient);
WSACleanup();
return 0;
}
此程序存在一些不足:
1、没有对命令行参数的有效性进行验证。
2、连接到服务器端失败时的错误提示是乱码。"烫烫烫烫....... "
3、如果命令行参数中第三个参数是长于50个字符的信息会超出缓冲区的容量。
要编译这两个程序需要修改project的properties,将其中的Character Set由Use Unicode Character Set修改为Use Multi-byte Charactoer Set,否则会提示_TCHAR不能转换为const char*的错误,另外,取命令行参数argv中的字符串时,如果只能取到字符串的第一个字符,也是需要修改此属性。