BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #26139同步于 2009/7/11
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

一个socket应用例子,分享给大家,嘿嘿

dayachidexia
2009/7/11镜像同步20 回复
关于winsock服务器和客户端编程2008年12月28日 星期日 23:22在网络编程中,最常用和最基础的就是WINSOCK. 现在我们讨论WINDOWS下的SOCKET编程. 大凡在WIN32平台上的WINSOCK编程都要经过下列步骤: 定义变量->获得WINDOCK版本->加载WINSOCK库->初始化->创建套接字->设置套接字选项->关闭套接字->卸载WINSOCK库->释放资源 下面介绍WINSOCK C/S的建立过程: 服务器 客户端 ________________________________________________ 1 初始化WSA 1 初始化WSA ____________________________________________________ 2 建立一个SOCKET 2 建立一个SOCKET _____________________________________________________ 3 绑定SOCKET 3 连接到服务器 _____________________________________________________ 4 在指定的端口监听 4 发送和接受数据 _____________________________________________________ 5 接受一个连接 5 断开连接 ______________________________________________________- 6 发送和接受数据 ___________________________________________________ 7 断开连接 __________________________________________________ 大家注意,在VC中进行WINSOCK编程时,需要引入如下两个库文件:WINSOCK.H(这个是WINSOCK API的头文件,WIN2K以上支持WINSOCK2,所以 可以用WINSOCK2.H);Ws2_32.lib(WINSOCK API连接库文件). 使用方式如下: #include <winsock.h> #pragma comment(lib,"ws2_32.lib") 下面我们通过具体的代码演示服务器和客户端的工作流程: 首先,建立一个WSADATA结构,通常用wsaData WSADATA wsaData; 然后,调用WSAStartup函数,这个函数是连接应用程序与winsock.dll的第一个调用.其中,第一个参数是WINSOCK 版本号,第二个参数是指向 WSADATA的指针.该函数返回一个INT型值,通过检查这个值来确定初始化是否成功.调用格式如下:WSAStartup(MAKEWORD(2,2),&wsaData),其中 MAKEWORD(2,2)表示使用WINSOCK2版本.wsaData用来存储系统传回的关于WINSOCK的资料. if(iResuit=WSAStartup(MAKEWORD(2,2),&wsaData)!=0) { printf("WSAStartup failed:%d",GetLastError()); //返回值不等与0,说明初始化失败 ExitProcess(); //退出程序 } 应用程序在完成对请求的SOCKET库使用后,要调用WSACleanup函数来接触SOCKET库的绑定,并且释放资源. 注意WSAStartup初始化后,必须建立一个SOCKET结构来保存SOCKET句柄. 下面我们建立一个SOCKET. 首先我们建立一个m_socket的SOCKET句柄,接着调用socket()函数,函数返回值保存在m_socket中.我们使用AF_INFE,SOCK_STREAM,IPPROTO_TCP 三个参数.第一个表示地址族,AF_INFE表示TCP/IP族,第二个表示服务类型,在WINSOCK2中,SOCKET支持以下三种类型; SOCK_STREAM 流式套接字 SOCK_DGRAM 数据报套接字 SOCK_RAW 原始套接字 第三个参数表示协议: IPPROTO_UDP UDP协议 用于无连接数据报套接字 IPPROTO_TCP TCP协议 用于流式套接字 IPPROTO_ICMP ICMP协议用于原始套接字 m_socket=socket(AF_INFE,SOCK_STREAM,IPPROTO_TCP); //创建TCP协议 以下代码用于检查返回值是否有错误: if(m_scoket==INVALID_SOCKET) { prinrf("Error at socket():%d\n",GetLastError()); WSACleanup(); //释放资源 return; } 说明,如果socket()调用失败,他将返回INVALID_SOCKET. 为了服务器能接受一个连接,他必须绑定一个网络地址,下面的代码展示如何绑定一个已经初始化的IP和端口的Socket.客户端程序用这个 IP地址和端口来连接服务器. sockaddr_in service; service.sin_family=AF_INET; //INTERNET地址族 service.sin_addr.s_addr=inet_addr("127.0.0.1"); //将要绑定的本地IP地址 service.sin_port=htons(27015); //27015将要绑定的端口 下面我们调用BIND函数,把SOCKET和SOCKADDR以参数的形式传入,并检查错误. if(bind(m_socket,(SOCKADDR*)&SERVICE,sizeof(service))==SOCKET_ERROR) { printf("bind() failed.\n"); closesocket(m_socket); return; } 当绑定完成后,服务器必须建立一个监听队列,以接受客户端的请求.listen()使服务器进入监听状态,该函数调用成功返回0,否则返回 SOCKET_ERROR.代码如下: if(listen(m_socket,1)==SOCKET-ERROR) { printf("error listening on socket.\n"); } 服务器端调用完LISTEN()后,如果此时客户端调用CONNECT()函数,服务器端必须在调用ACCEPT().这样服务器和客户端才算正式完成通信程序的 连接动作. 一旦服务器开始监听,我们就要指定一个句柄来表示利用ACCEPT()函数接受的连接,这个句柄是用来发送和接受数据的表示.建立一个SOCKET句柄 Socket AcceptSocket 然后利用无限循环来检测是否有连接传入.一但有连接请求,ACCEPT()函数就会被调用,并且返回这次连接的句柄. printf("waitong for a client to connect...\n"); while(1) { AcceptSocket=SOCKET_ERROR; while(AcceptSocket==SOCKET_ERROR) { AcceptSocket=accept(m_socket,NULL,NULL); } } 下面看客户端端代码: sockaddr_in clientService; clientService.sin_family=AF_INET; //INTERNET地址族 clientService.sin_addr.s_addr=inet_addr("127.0.0.1"); //将要绑定的本地IP地址 clientService.sin_port=htons(27015); //27015将要绑定的端口 下面调用CONNECT()函数: if ( connect( m_socket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) { printf( "Failed to connect.\n" ); WSACleanup(); return; } //如果调用失败清理退出 //调用成功继续读写数据 _________________________________________________________________________________________________ 到这里,服务器和客户端的基本流程介绍完毕,下面我们介绍数据交换. send(): int send { SOCKET s, //指定发送端套接字 const char FAR?*buf, //指明一个存放应用程序要发送的数据的缓冲区 int len, //实际要发送的数据字节数 int flags //一般设置为0 }; C/S都用SEND函数向TCP连接的另一端发送数据. recv(): int recv { SOCKET s, //指定发送端套接字 char FAR?*buf, //指明一个缓冲区 存放RECC受到的数据 int len, //指明BUF的长度 int flags //一般设置为0 }; C/S都使用RECV函数从TCP连接的另一端接受数据 下面将完整的程序代码提供如下,大家可直接编译运行 首先看客户端的代码: #include <stdio.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") void main() { // 初始化 Winsock. WSADATA wsaData; int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); if ( iResult != NO_ERROR ) printf("Error at WSAStartup()\n"); // 建立socket socket. SOCKET client; client = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( client == INVALID_SOCKET ) { printf( "Error at socket(): %ld\n", WSAGetLastError() ); WSACleanup(); return; } // 连接到服务器. sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" ); clientService.sin_port = htons( 27015 ); if ( connect( client, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) { printf( "Failed to connect.\n" ); WSACleanup(); return; } // 发送并接收数据. int bytesSent; int bytesRecv = SOCKET_ERROR; char sendbuf[32] = "Client: Sending data."; char recvbuf[32] = ""; bytesSent = send( client, sendbuf, strlen(sendbuf), 0 ); printf( "Bytes Sent: %ld\n", bytesSent ); while( bytesRecv == SOCKET_ERROR ) { bytesRecv = recv( client, recvbuf, 32, 0 ); if ( bytesRecv == 0 || bytesRecv == WSAECONNRESET ) { printf( "Connection Closed.\n"); break; } if (bytesRecv < 0) return; printf( "Bytes Recv: %ld\n", bytesRecv ); } return; } 下面是服务器端代码: #include <stdio.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") void main() { // 初始化 WSADATA wsaData; int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); if ( iResult != NO_ERROR ) printf("Error at WSAStartup()\n"); // 建立socket SOCKET server; server = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( server == INVALID_SOCKET ) { printf( "Error at socket(): %ld\n", WSAGetLastError() ); WSACleanup(); return; } // 绑定socket sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = inet_addr( "127.0.0.1" ); service.sin_port = htons( 27015 ); if ( bind( server, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) { printf( "bind() failed.\n" ); closesocket(server); return; } // 监听 socket if ( listen( server, 1 ) == SOCKET_ERROR ) printf( "Error listening on socket.\n"); // 接受连接 SOCKET AcceptSocket; printf( "Waiting for a client to connect...\n" ); while (1) { AcceptSocket = SOCKET_ERROR; while ( AcceptSocket == SOCKET_ERROR ) { AcceptSocket = accept( server, NULL, NULL ); } printf( "Client Connected.\n"); server = AcceptSocket; break; } // 发送接受数据 int bytesSent; int bytesRecv = SOCKET_ERROR; char sendbuf[32] = "Server: Sending Data."; char recvbuf[32] = ""; bytesRecv = recv( server, recvbuf, 32, 0 ); printf( "Bytes Recv: %ld\n", bytesRecv ); bytesSent = send( server, sendbuf, strlen(sendbuf), 0 ); printf( "Bytes Sent: %ld\n", bytesSent ); return; } 本程序仅仅描述了同步的情况! PS:本文转自百度贴吧新红盟吧 ============================================================accept()补充 绑定socket后用listen函数去监听,如果有连接请求就把该请求放到等待队列里,等待accept函数来接收。 一旦accept接收成功就创建一个新的socket来处理与client的通讯。 accept()函数 准备好了,系统调用accept()会有点古怪的地方的! 你可以想象发生这样的事情:有人从很远的地方通过一个你在侦听(listen())的端口连接(connect())到你的机器。 它的连接将加入到等待接受(accept())的队列中。你调用accept()告诉它你有空闲的连接。它将返回一个新的套接字文件描述符! 这样你就有两个套接字了,原来的一个还在侦听你的那个端口,新的在准备发送(send())和接收(recv())数据。这就是这个过程! 函数是这样定义的: #include <sys/socket.h> int accept(int sockfd, void *addr, int *addrlen); sockfd相当简单,是和isten()中一样的套接字描述符。addr是个指向局部的数据结构sockaddr_in的指针。 这是要求接入的信息所要去的地方(你可以测定哪个地址在哪个端口呼叫你)。 在它的地址传递给accept之前,addrlen是个局部的整形变量,设置为 sizeof(struct sockaddr_in) accept将不会将多余的字节给addr。如果你放入的少些,那么它会通过改变 addrlen 的值反映出来。 同样,在错误时返回-1,并设置全局错误变量 errno。 现在是你应该熟悉的代码片段。 #include <string.h> #include <sys/socket.h> #include <sys/types.h> #define MYPORT 3490 /*用户接入端口*/ #define BACKLOG 10 /* 多少等待连接控制*/ main() { int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */ struct sockaddr_in my_addr; /* 地址信息 */ struct sockaddr_in their_addr; /* connector's address information*/ int sin_size; /* don't forget your error checking for these calls: */ sockfd = socket(AF_INET, SOCK_STREAM, 0); /* 错误检查*/ my_addr.sin_family = AF_INET; /* host byte order */ my_addr.sin_port = htons(MYPORT); /* short, network byte order */ my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ bzero(&(my_addr.sin_zero); /* zero the rest of the struct */ bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); listen(sockfd, BACKLOG); sin_size = sizeof(struct sockaddr_in); new_fd = accept(sockfd, &their_addr, &sin_size); ... ... } 注意,在系统调用 send() 和 recv() 中你应该使用新的套接字描述符 new_fd。 如果你只想让一个连接进来,那么你可以使用 close() 去关闭原来的文件描述符sockfd来避免同一个端口更多的连接。
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
ShallWeYoung机器人#1 · 2009/7/11
赞~
shenlei机器人#2 · 2009/7/11
解决了很多人的小学期问题?
Kumbayaco机器人#3 · 2009/7/11
赞mm
mcg3721机器人#4 · 2009/7/13
赞~大牛啊~ 【 在 dayachidexia 的大作中提到: 】 : 关于winsock服务器和客户端编程2008年12月28日 星期日 23:22在网络编程中,最常用和最基础的就是WINSOCK. 现在我们讨论WINDOWS下的SOCKET编程. : : 大凡在WIN32平台上的WINSOCK编程都要经过下列步骤: : ...................
nickluchen机器人#5 · 2009/7/13
其实把《UNIX环境高级编程》的socket那章看一遍就行了。
noname机器人#6 · 2009/7/13
【 在 nickluchen 的大作中提到: 】 : 其实把《UNIX环境高级编程》的socket那章看一遍就行了。 APUE里关于网络套接字那部分写得太简单了吧。。。
nickluchen机器人#7 · 2009/7/13
如果只是要求会用socket,看那本书我觉得足够了。 其实我觉得APUE感觉就像个大手册…… 【 在 noname 的大作中提到: 】 : APUE里关于网络套接字那部分写得太简单了吧。。。
wangzai机器人#8 · 2009/7/13
感谢楼主,我在vc上试了下,代码保存为cpp文件才能被编译,保存为c格式的话编译不过,这是为什么呢?
nickluchen机器人#9 · 2009/7/13
新版的vs好像只认cpp吧,新建源文件的时候只能建立cpp,不能建立C 【 在 wangzai 的大作中提到: 】 : 感谢楼主,我在vc上试了下,代码保存为cpp文件才能被编译,保存为c格式的话编译不过,这是为什么呢?