用VC++6.0 Sockets API实现聊天室程序

来源:互联网 发布:极限编程中结对编程 编辑:程序博客网 时间:2024/05/21 11:17

用VC++6.0 Sockets API实现聊天室程序
1.VC++网络编程及Windows Sockets API简介

VC++对网络编程的支持有socket支持,WinInet支持,MAPI和ISAPI支持等。其中,Windows Sockets API是TCP/IP网络环境里,也是Internet上进行开发最为通用的API。最早美国加州大学Berkeley分校在UNIX下为TCP/IP协议开发了一个API,这个API就是著名的Berkeley Socket接口(套接字)。在桌面操作系统进入Windows时代后,仍然继承了Socket方法。在TCP/IP网络通信环境下,Socket数据传输是一种特殊的I/O,它也相当于一种文件描述符,具有一个类似于打开文件的函数调用-socket()。可以这样理解:Socket实际上是一个通信端点,通过它,用户的Socket程序可以通过网络和其他的Socket应用程序通信。Socket存在于一个"通信域"(为描述一般的线程如何通过Socket进行通信而引入的一种抽象概念)里,并且与另一个域的Socket交换数据。Socket有三类。第一种是SOCK_STREAM(流式),提供面向连接的可靠的通信服务,比如telnet,http。第二种是SOCK_DGRAM(数据报),提供无连接不可靠的通信,比如UDP。第三种是SOCK_RAW(原始),主要用于协议的开发和测试,支持通信底层操作,比如对IP和ICMP的直接访问。

2.Windows Socket机制分析

2.1一些基本的Socket系统调用

主要的系统调用包括:socket()-创建Socket;bind()-将创建的Socket与本地端口绑定;connect()与accept()-建立Socket连接;listen()-服务器监听是否有连接请求;send()-数据的可控缓冲发送;recv()-可控缓冲接收;closesocket()-关闭Socket。

2.2Windows Socket的启动与终止

启动函数WSAStartup()建立与Windows Sockets DLL的连接,终止函数WSAClearup()终止使用该DLL,这两个函数必须成对使用。

2.3异步选择机制

Windows是一个非抢占式的操作系统,而不采取UNIX的阻塞机制。当一个通信事件产生时,操作系统要根据设置选择是否对该事件加以处理,WSAAsyncSelect()函数就是用来选择系统所要处理的相应事件。当Socket收到设定的网络事件中的一个时,会给程序窗口一个消息,这个消息里会指定产生网络事件的Socket,发生的事件类型和错误码。

2.4异步数据传输机制

WSAAsyncSelect()设定了Socket上的须响应通信事件后,每发生一个这样的事件就会产生一个WM_SOCKET消息传给窗口。而在窗口的回调函数中就应该添加相应的数据传输处理代码。

3.聊天室程序的设计说明

3.1实现思想

在Internet上的聊天室程序一般都是以服务器提供服务端连接响应,使用者通过客户端程序登录到服务器,就可以与登录在同一服务器上的用户交谈,这是一个面向连接的通信过程。因此,程序要在TCP/IP环境下,实现服务器端和客户端两部分程序。

3.2服务器端工作流程

服务器端通过socket()系统调用创建一个Socket数组后(即设定了接受连接客户的最大数目),与指定的本地端口绑定bind(),就可以在端口进行侦听listen()。如果有客户端连接请求,则在数组中选择一个空Socket,将客户端地址赋给这个Socket。然后登录成功的客户就可以在服务器上聊天了。

3.3客户端工作流程

客户端程序相对简单,只需要建立一个Socket与服务器端连接,成功后通过这个Socket来发送和接收数据就可以了。

4.核心代码分析

限于篇幅,这里仅给出与网络编程相关的核心代码,其他的诸如聊天文字的服务器和客户端显示读者可以自行添加。

4.1服务器端代码

开启服务器功能:

void OnServerOpen() //开启服务器功能
{
 WSADATA wsaData;
 int iErrorCode;
 char chInfo[64];
 if (WSAStartup(WINSOCK_VERSION, &wsaData)) //调用Windows Sockets DLL
  { MessageBeep(MB_ICONSTOP);
   MessageBox("Winsock无法初始化!", AfxGetAppName(), MB_OK|MB_ICONSTOP);
   WSACleanup();
   return; }
 else
  WSACleanup();
  if (gethostname(chInfo, sizeof(chInfo)))
  { ReportWinsockErr("/n无法获取主机!/n ");
   return; }
  CString csWinsockID = "/n==>>服务器功能开启在端口:No. ";
  csWinsockID += itoa(m_pDoc->m_nServerPort, chInfo, 10);
  csWinsockID += "/n";
  PrintString(csWinsockID); //在程序视图显示提示信息的函数,读者可自行创建
  m_pDoc->m_hServerSocket=socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
  //创建服务器端Socket,类型为SOCK_STREAM,面向连接的通信
  if (m_pDoc->m_hServerSocket == INVALID_SOCKET)
  { ReportWinsockErr("无法创建服务器socket!");
   return;}
  m_pDoc->m_sockServerAddr.sin_family = AF_INET;
  m_pDoc->m_sockServerAddr.sin_addr.s_addr = INADDR_ANY;
  m_pDoc->m_sockServerAddr.sin_port = htons(m_pDoc->m_nServerPort);
  if (bind(m_pDoc->m_hServerSocket, (LPSOCKADDR)&m_pDoc->m_sockServerAddr,   
     sizeof(m_pDoc->m_sockServerAddr)) == SOCKET_ERROR) //与选定的端口绑定
   {ReportWinsockErr("无法绑定服务器socket!");
    return;}
   iErrorCode=WSAAsyncSelect(m_pDoc->m_hServerSocket,m_hWnd,
   WM_SERVER_ACCEPT, FD_ACCEPT);
   //设定服务器相应的网络事件为FD_ACCEPT,即连接请求,
   // 产生相应传递给窗口的消息为WM_SERVER_ACCEPT
  if (iErrorCode == SOCKET_ERROR)
   { ReportWinsockErr("WSAAsyncSelect设定失败!");
    return;}
  if (listen(m_pDoc->m_hServerSocket, QUEUE_SIZE) == SOCKET_ERROR) //开始监听客户连接请求
   {ReportWinsockErr("服务器socket监听失败!");
    m_pParentMenu->EnableMenuItem(ID_SERVER_OPEN, MF_ENABLED);
    return;}
  m_bServerIsOpen = TRUE; //监视服务器是否打开的变量
 return;
}

响应客户发送聊天文字到服务器:ON_MESSAGE(WM_CLIENT_READ, OnClientRead)

LRESULT OnClientRead(WPARAM wParam, LPARAM lParam)
{
 int iRead;
 int iBufferLength;
 int iEnd;
 int iRemainSpace;
 char chInBuffer[1024];
 int i;
 for(i=0;(i   //MAXClient是服务器可响应连接的最大数目
  {}
 if(i==MAXClient) return 0L;
  iBufferLength = iRemainSpace = sizeof(chInBuffer);
  iEnd = 0;
  iRemainSpace -= iEnd;
  iBytesRead = recv(m_aClientSocket[i], (LPSTR)(chInBuffer+iEnd), iSpaceRemaining, NO_FLAGS);   //用可控缓冲接收函数recv()来接收字符
  iEnd+=iRead;
 if (iBytesRead == SOCKET_ERROR)
  ReportWinsockErr("recv出错!");
  chInBuffer[iEnd] = '/0';
 if (lstrlen(chInBuffer) != 0)
  {PrintString(chInBuffer); //服务器端文字显示
   OnServerBroadcast(chInBuffer); //自己编写的函数,向所有连接的客户广播这个客户的聊天文字
  }
 return(0L);
}

对于客户断开连接,会产生一个FD_CLOSE消息,只须相应地用closesocket()关闭相应的Socket即可,这个处理比较简单。

4.2客户端代码

连接到服务器:

void OnSocketConnect()
{ WSADATA wsaData;
 DWORD dwIPAddr;
 SOCKADDR_IN sockAddr;
 if(WSAStartup(WINSOCK_VERSION,&wsaData)) //调用Windows Sockets DLL
 {MessageBox("Winsock无法初始化!",NULL,MB_OK);
  return;
 }
 m_hSocket=socket(PF_INET,SOCK_STREAM,0); //创建面向连接的socket
 sockAddr.sin_family=AF_INET; //使用TCP/IP协议
 sockAddr.sin_port=m_iPort; //客户端指定的IP地址
 sockAddr.sin_addr.S_un.S_addr=dwIPAddr;
 int nConnect=connect(m_hSocket,(LPSOCKADDR)&sockAddr,sizeof(sockAddr)); //请求连接
 if(nConnect)
  ReportWinsockErr("连接失败!");
 else
  MessageBox("连接成功!",NULL,MB_OK);
  int iErrorCode=WSAAsyncSelect(m_hSocket,m_hWnd,WM_SOCKET_READ,FD_READ);
  //指定响应的事件,为服务器发送来字符
 if(iErrorCode==SOCKET_ERROR)
 MessageBox("WSAAsyncSelect设定失败!");
}

接收服务器端发送的字符也使用可控缓冲接收函数recv(),客户端聊天的字符发送使用数据可控缓冲发送函数send(),这两个过程比较简单,在此就不加赘述了。

5.小结

通过聊天室程序的编写,可以基本了解Windows Sockets API编程的基本过程和精要之处。本程序在VC++6.0下编译通过,在使用windows 98/NT的局域网里运行良好。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 18岁老是掉头发怎么办 17岁头发老是掉怎么办 19岁老是掉头发怎么办 植发的了毛囊炎怎么办 长得好看不上镜怎么办 鬓角的头发总翘怎么办 戴贞洁锁硬了怎么办 欠很多钱还不了怎么办 理财平台跑路了怎么办 联通4g信号不好怎么办 老年人耳朵嗡嗡响什么原因怎么办 单侧耳朵嗡嗡响怎么办 蚊子钻到耳朵里怎么办 小飞虫进耳朵里怎么办 耳朵里飞进去虫怎么办 耳朵里进虫子了怎么办 耳朵进虫子了怎么办啊 耳朵里进了蚊子怎么办 有虫子爬进耳朵怎么办 虫子死在耳朵里怎么办 早上起床口苦口臭怎么办 耳朵被耳屎堵了怎么办 油耳朵被堵住了怎么办 棉签掏耳朵堵了怎么办 耳朵被气堵住了怎么办 掏耳朵发炎了疼怎么办 掏耳朵掏深了疼怎么办 耳朵里进了虫子怎么办? 耳朵里飞进小虫怎么办 一只耳朵听力差怎么办 62岁耳朵有点聋怎么办 一支耳朵有点聋怎么办 80岁老人耳朵聋怎么办 被nlp课程洗脑了怎么办 手上张了个鸡眼怎么办 手上长了个鸡眼怎么办 6岁儿童手指脱皮怎么办 手指骨折后关节僵硬怎么办 手指外伤后关节肿大僵硬怎么办 胳膊骨折了手肿怎么办 耳朵被肘了耳鸣怎么办