Linux下select函数实现的聊天服务器
基于WSAAsyncSelect模型实现的聊天室图形客户端
聊天室Windows控制台客户端
Linux下select函数实现的聊天服务器
消息缓冲区类MessageBuffer,接收线程将受到的消息放入缓冲区,发送线程从缓冲区中取出消息
MessageBuffer.h
//MessageBuffer.h
#ifndef _MESSAGE_BUF_INCLUDE_
#define _MESSAGE_BUF_INCLUDE_
#include <pthread.h>
#define MESSAGE_COUNT 16
#define MESSAGE_LENGTH 2048
class MessageBuffer{
private:
pthread_mutex_t mutex;//访问缓冲的互斥量
pthread_cond_t condition;//访问缓冲区的条件变量
//消息缓冲区,循环队列
char buf[MESSAGE_COUNT][MESSAGE_LENGTH];
int rear; //循环队列的队尾
int front; //循环队列的队首
public:
bool toStop;
//构造函数
MessageBuffer();
//析构函数
virtual ~MessageBuffer();
//将消息放入消息缓冲区,当缓冲区满时阻塞,toStop=true时返回-1
int PutMessage(const char *message);
//从消息缓冲区中获得消息,当缓冲区空时阻塞,toStop=true时返回-1
int GetMessage(char *mbuf, int buflen);
};
#endif
MessageBuffer.cpp
//MessageBuffer.cpp
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include "MessageBuffer.h"
MessageBuffer::MessageBuffer() {
toStop = false;
pthread_mutex_init(&mutex,NULL);//初始化互斥量
pthread_cond_init(&condition,NULL);//初始化条件变量
rear = 0; //队尾指针指向0
front = 0; //队首指针指向0
printf("A MessageBuffer intance created./n");
}
MessageBuffer::~MessageBuffer(){
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&condition);
printf("A MessageBuffer instance destroyed./n");
}
//将消息放入消息缓冲区
int MessageBuffer::PutMessage(const char *message){
struct timespec t;
//等待互斥量
pthread_mutex_lock(&mutex);
while(!toStop && (rear+1)%MESSAGE_COUNT==front){
t.tv_sec = time(NULL)+1;
t.tv_nsec = 0;
pthread_cond_timedwait(&condition,&mutex,&t);
}
if(toStop){
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&mutex);
return -1;
}
int messageLen = strlen(message);
int copyLen = messageLen>=MESSAGE_LENGTH?MESSAGE_LENGTH-1:messageLen;
memcpy(buf[rear],message,copyLen);
buf[rear][copyLen]='/0';
rear = (rear+1)%MESSAGE_COUNT;
pthread_cond_signal(&condition);
pthread_mutex_unlock(&mutex);
return 0;
}
//从消息缓冲区中获得消息
int MessageBuffer::GetMessage(char *mbuf, int buflen){
struct timespec t;
pthread_mutex_lock(&mutex);
while(!toStop && rear==front){
t.tv_sec = time(NULL)+1;
t.tv_nsec = 0;
pthread_cond_timedwait(&condition,&mutex,&t);
}
if(toStop){
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&mutex);
return -1;
}
int messageLen = strlen(buf[front]);
int copyLen = messageLen>=buflen ? buflen-1 : messageLen;
memcpy(mbuf,buf[front],copyLen);
mbuf[copyLen]='/0';
front = (front+1)%MESSAGE_COUNT;
pthread_cond_signal(&condition);
pthread_mutex_unlock(&mutex);
return 0;
}
客户类Clients,用于维护套接字socket和套接字地址struct sockaddr_in之间的对应关系,并维护用户的姓名。
Clients.h
//Clients.h
#ifndef _CLIENTS_INCLUDE_
#define _CLIENTS_INCLUDE_
#include <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#define NAME_LEN 50
#define MAX_CLIENT 30
typedef struct client_info{
int sock;
struct sockaddr_in clientAddr;
char name[NAME_LEN];
}CLIENT_INFO;
class Clients{
private:
pthread_mutex_t mutex;
CLIENT_INFO client[MAX_CLIENT];
int clientCount;
int IPtoString(unsigned long ip, char *buf, int buflen);
int Search(int sock);
public:
Clients();//构造函数
virtual ~Clients();//析构函数
int GetClientCount();
bool PutClient(int sock,const struct sockaddr_in &clientAddr);
void RemoveClient(int sock);
bool GetAddrBySocket(int sock,struct sockaddr_in *addr);
bool PutName(int sock,const char *name, int namelen);
bool GetName(int sock, char *name, int namelen);
int GetAllSocket(int* sockArray, int arrayLen );
};
#endif
Clients.cpp
//Clients.cpp
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include "Clients.h"
Clients::Clients() {
pthread_mutex_init(&mutex, NULL);
clientCount = 0;
printf("Clients created./n");
}
Clients::~Clients() {
pthread_mutex_destroy(&mutex);
printf("Clients destroyed./n");
}
int Clients::Search(int sock){
int index = -1;
for(int i=0; i<clientCount; i++) {
if(client[i].sock==sock){
index = i;
break;
}
}
return index;
}
int Clients::IPtoString(unsigned long ip,char *buf,int buflen){
unsigned char *p = (unsigned char*)&ip;
if(buflen<16){
return -1;
}
sprintf(buf,"%u.%u.%u.%u",*p,*(p+1),*(p+2),*(p+3));
return strlen(buf);
}
int Clients::GetClientCount(){
return clientCount;
}
bool Clients::PutClient(int sock,const struct sockaddr_in &clientAddr) {
if(clientCount==MAX_CLIENT){
return false;
}
pthread_mutex_lock(&mutex);
client[clientCount].sock = sock;
client[clientCount].clientAddr = clientAddr;
int buflen = sizeof(client[clientCount].name);
int pos = IPtoString(clientAddr.sin_addr.s_addr,client[clientCount].name,buflen);
sprintf(&client[clientCount].name[pos],":%d",ntohs(clientAddr.sin_port));
clientCount++;
pthread_mutex_unlock(&mutex);
return true;
}
void Clients::RemoveClient(int sock){
pthread_mutex_lock(&mutex);
int index = Search(sock);
if(index!=-1){
for(int i=index; i<clientCount-1; i++){
client[i] = client[i+1];
}
clientCount--;
}
pthread_mutex_unlock(&mutex);
}
bool Clients::GetAddrBySocket(int sock,struct sockaddr_in *addr){
pthread_mutex_lock(&mutex);
int index = Search(sock);
if(index!=-1){
memcpy(addr,&client[index].clientAddr,sizeof(struct sockaddr_in));
}
pthread_mutex_unlock(&mutex);
return index!=-1;
}
bool Clients::PutName(int sock,const char *name,int namelen) {
pthread_mutex_lock(&mutex);
int index = Search(sock);
if(index!=-1){
int copyLen = namelen>=NAME_LEN ? NAME_LEN-1:namelen;
memcpy(client[index].name,name,copyLen);
client[index].name[copyLen]='/0';
}
pthread_mutex_unlock(&mutex);
return index!=-1;
}
bool Clients::GetName(int sock, char *name, int namelen) {
pthread_mutex_lock(&mutex);
int index = Search(sock);
if(index!=-1){
int msgLen = strlen(client[index].name);
int copyLen = (msgLen<namelen)? msgLen:(namelen-1);
memcpy(name,client[index].name,copyLen);
name[copyLen]='/0';
}
pthread_mutex_unlock(&mutex);
return index!=-1;
}
int Clients::GetAllSocket(int* sockArray, int arrayLen ) {
pthread_mutex_lock(&mutex);
int copyCount = arrayLen>clientCount ? clientCount : arrayLen;
for(int i=0; i<copyCount; i++){
sockArray[i] = client[i].sock;
}
pthread_mutex_unlock(&mutex);
return copyCount;
}
聊天室服务器主程序Server.cpp
/*server.c*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <pthread.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "MessageBuffer.h"
#include "Clients.h"
using namespace std;
#define SERVER_PORT 8000
#define BUFFER_SIZE 4096
#ifndef MAX_CLIENT
#define MAX_CLIENT 30
#endif
#ifndef NAME_LEN
#define NAME_LEN 50
#endif
MessageBuffer messageBuffer;
Clients clients;
void* ListenThread(void*);
void* RecvThread(void*);
void* SendThread(void*);
void ProcessMessage(int sock,char buf[],int bufsize,int bytes);
bool toStop=false;
int main(int argc,char* argv[]) {
if(argc!=2){
printf("Usage: %s PortNumber/n",argv[0]);
return -1;
}
unsigned short port;
if((port = atoi(argv[1]))==0){
printf("incorrect port number./n");
return -1;
}
int s;
struct sockaddr_in serverAddr;
s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s==-1){
fprintf(stderr,"create socket failed./n");
return -1;
}
bzero(&serverAddr,sizeof(struct sockaddr_in));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(s,(struct sockaddr*)&serverAddr,sizeof(serverAddr))==-1){
fprintf(stderr,"bind socket to port %d failed./n",port);
return -1;
}
if(listen(s,SOMAXCONN)==-1){
fprintf(stderr,"listen failed./n");
return -1;
}
printf("Server is listening on ");
char hostname[255];
if(gethostname(hostname,sizeof(hostname))){
printf("gethostname() failed./n");
return -1;
}
struct hostent* pHost = gethostbyname(hostname);
if(pHost){
for(int i=0; pHost->h_addr_list[i]; i++){
printf("%s ",inet_ntoa(*(in_addr*)pHost->h_addr_list[i]));
}
}
printf("/nport: %d/n",port);
pthread_t tListenId;
if(pthread_create(&tListenId,NULL,ListenThread,&s)){
printf("failed to create listen thread./n");
return -1;
}
pthread_t tRecvId;
if(pthread_create(&tRecvId,NULL,RecvThread,NULL)){
printf("failed to create recv thread./n");
return -1;
}
pthread_t tSendId;
if(pthread_create(&tSendId,NULL,SendThread,NULL)){
printf("failed to create send thread./n");
return -1;
}
while(getchar()!='q');
toStop = true;
messageBuffer.toStop = true;
pthread_join(tListenId,NULL);
pthread_join(tRecvId,NULL);
pthread_join(tSendId,NULL);
close(s);
int sock[MAX_CLIENT];
int count = clients.GetAllSocket(sock,MAX_CLIENT);
for(int i=0;i<count;i++){
close(sock[i]);
}
printf("server stopped./n");
return 0;
}
void* ListenThread(void*ps){
int s=*(int*)ps;
fd_set listenSet;
int sock;
struct sockaddr_in clientAddr;
struct timeval timeout;
while(!toStop){
FD_ZERO(&listenSet);
FD_SET(s,&listenSet);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
int ret = select(s+1,&listenSet,NULL,NULL,&timeout);
if(toStop){
printf("ListenThread: exit./n");
return NULL;
}
if(ret==-1){
printf("ListenThread: select() failed!/n");
}else if(ret==0){
printf("ListenThread: select() time out./n");
}else{
if(FD_ISSET(s,&listenSet)){
socklen_t addrlen = sizeof(struct sockaddr_in);
memset(&clientAddr,0,sizeof(struct sockaddr_in));
if((sock=accept(s,(struct sockaddr*)&clientAddr,&addrlen))==-1){
fprintf(stderr,"accept failed./n");
}
if(!clients.PutClient(sock,clientAddr)){
printf("max client limited. MAX_CLIENT=%d/n",MAX_CLIENT);
close(sock);
}
printf("accept a connection from %s:%u/n",
inet_ntoa(*(struct in_addr*)&(clientAddr.sin_addr.s_addr)),
ntohs(clientAddr.sin_port));
printf("new socket is: %u/n",sock);
}
}
}
return NULL;
}
void* RecvThread(void*){
fd_set readSet;
int sock[MAX_CLIENT];
char buf[BUFFER_SIZE];
struct timeval timeout;
while(!toStop){
int count = clients.GetAllSocket(sock,MAX_CLIENT);
if(count==0){
sleep(2);
if(toStop){
printf("RecvThread: exit./n");
return NULL;
}
continue;
}
FD_ZERO(&readSet);
int maxfd=0;
for(int i=0;i<count;i++){
printf("--%d",sock[i]);
FD_SET(sock[i],&readSet);
if(sock[i]>maxfd){
maxfd = sock[i];
}
}
printf("/n");
timeout.tv_sec = 2;
timeout.tv_usec = 0;
int ret = select(maxfd+1,&readSet,NULL,NULL,&timeout);
if(toStop){
printf("RecvThread: exit./n");
return NULL;
}
if(ret==-1){
printf("RecvThread: select() failed!/n");
}else if(ret==0){
printf("RecvThread: select() time out./n");
}else{
for(int i=0; i<count; i++){
if(FD_ISSET(sock[i],&readSet)){
int bytes=recv(sock[i],buf,sizeof(buf)-1,0);
if(bytes==-1){
printf("RecvThread: recv failed./n");
clients.RemoveClient(sock[i]);
close(sock[i]);
}else if(bytes==0){
printf("RecvThread: socket closed by the other side./n");
clients.RemoveClient(sock[i]);
close(sock[i]);
}else{
ProcessMessage(sock[i],buf,sizeof(buf),bytes);
}
}
}
}
}
return NULL;
}
void* SendThread(void*){
fd_set writeSet;
int sock[MAX_CLIENT];
char buf[BUFFER_SIZE];
struct timeval timeout;
while(!toStop){
int ret = messageBuffer.GetMessage(buf,sizeof(buf));
printf("get a message from buffer./n");
if(ret==-1){
printf("SendThread: exit./n");
return NULL;
}
int count = clients.GetAllSocket(sock,MAX_CLIENT);
FD_ZERO(&writeSet);
int maxfd = 0;
for(int i=0;i<count;i++){
FD_SET(sock[i],&writeSet);
if(sock[i]>maxfd){
maxfd = sock[i];
}
}
timeout.tv_sec = 2;
timeout.tv_usec = 0;
ret = select(maxfd+1,NULL,&writeSet,NULL,&timeout);
if(toStop){
printf("SendThread: exit./n");
return NULL;
}
if(ret==-1){
printf("SendThread: select() failed!/n");
}else if(ret==0){
printf("SendThread: select() time out./n");
}else{
for(int i=0;i<count;i++){
if(FD_ISSET(sock[i],&writeSet)){
int messageLen = strlen(buf);
int bytes = send(sock[i],buf,messageLen,0);
if(bytes==-1){
printf("SendThread: send() failed./n");
}else if(bytes!=messageLen){
printf("SendThread: send message trunked.");
}else{
//do nothing
}
}
}
}
}
return NULL;
}
void ProcessMessage(int sock,char buf[],int bufsize,int bytes){
struct sockaddr_in clientAddr;
if(!clients.GetAddrBySocket(sock,&clientAddr)){
printf("ProcessMessage: can not find socket address./n");
return;
}
char ipString[16];
unsigned char *ip = (unsigned char*)&clientAddr.sin_addr.s_addr;
sprintf(ipString,"%u.%u.%u.%u",*ip,*(ip+1),*(ip+2),*(ip+3));
unsigned short port = ntohs(clientAddr.sin_port);
buf[bytes]='/0';
printf("Message from %s:%d: %s/n",ipString,port,buf);
const char* CMD_BYE="bye";
if(strcmp(buf,CMD_BYE)==0){
send(sock,CMD_BYE,strlen(CMD_BYE),0);
clients.RemoveClient(sock);
close(sock);
printf("%s:%u disconnected./n", ipString, port);
return;
}else{
char bufWithName[BUFFER_SIZE+NAME_LEN];
char cmdname[5];
char name[NAME_LEN];
memcpy(cmdname, buf, 4);
cmdname[4] = '/0';
const char* CMD_NAME="name";
if(strcmp(cmdname,CMD_NAME)==0){
char newname[NAME_LEN];
int nameLen = strlen(buf+5);
int copyLen;
if(nameLen>=NAME_LEN){
copyLen = NAME_LEN-1;
}else{
copyLen = nameLen;
}
memcpy(newname,buf+5,copyLen);
newname[copyLen]='/0';
clients.GetName(sock,name,sizeof(name));
sprintf(bufWithName,"%s change name to %s",name,newname);
clients.PutName(sock,newname,strlen(newname));
messageBuffer.PutMessage(bufWithName);
}else{
clients.GetName(sock,name,sizeof(name));
sprintf(bufWithName,"%s: %s",name,buf);
messageBuffer.PutMessage(bufWithName);
}
}
}
编译脚本文件compile
g++ -c MessageBuffer.cpp
g++ -c Clients.cpp
g++ -c Server.cpp
g++ -lpthread -o server MessageBuffer.o Clients.o Server.o
chmod +x compile
./compile 就可以编译并链接
运行服务器
./server 8000
注意Linux下的防火墙iptables服务是否已经启动,如果启动了,需要在/etc/sysconfig/iptables中加入例外端口8000,并重启启动防火墙
/etc/init.d/iptables restart
基于WSAAsyncSelect模型实现的聊天室图形客户端
对话框头文件ClientDlg.h
// ClientDlg.h : 头文件
//
#pragma once
#include "afxcmn.h"
#include "afxwin.h"
#define WM_NETWORK WM_USER+100
// CClientDlg 对话框
class CClientDlg : public CDialog
{
// 构造
public:
CClientDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_CLIENT_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
CIPAddressCtrl m_ip;
int m_port;
CEdit m_allmsg;
CString m_nickname;
CString m_message;
afx_msg void OnBnClickedConnect();
// 服务器套接字
SOCKET sock;
struct sockaddr_in serverAddr;
CString msg;
LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
void OnConnect(SOCKET s);
void OnRead(SOCKET s);
void OnCloseSocket(SOCKET s);
afx_msg void OnBnClickedSend();
afx_msg void OnBnClickedDisconnect();
afx_msg void OnClose();
afx_msg void OnBnClickedQuit();
afx_msg void OnBnClickedChangeName();
};
对话框源文件 ClientDlg.cpp
// ClientDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "Client.h"
#include "ClientDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CClientDlg 对话框
CClientDlg::CClientDlg(CWnd* pParent /*=NULL*/)
: CDialog(CClientDlg::IDD, pParent)
, m_port(8000)
, m_nickname(_T("microtong"))
, m_message(_T("hello"))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CClientDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_IP, m_ip);
DDX_Text(pDX, IDC_PORT, m_port);
DDX_Control(pDX, IDC_ALLMESSAGE, m_allmsg);
DDX_Text(pDX, IDC_NICKNAME, m_nickname);
DDV_MaxChars(pDX, m_nickname, 30);
DDX_Text(pDX, IDC_MESSAGE, m_message);
DDV_MaxChars(pDX, m_message, 1024);
}
BEGIN_MESSAGE_MAP(CClientDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_CONNECT, &CClientDlg::OnBnClickedConnect)
ON_BN_CLICKED(IDC_SEND, &CClientDlg::OnBnClickedSend)
ON_BN_CLICKED(IDC_DISCONNECT, &CClientDlg::OnBnClickedDisconnect)
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_QUIT, &CClientDlg::OnBnClickedQuit)
ON_BN_CLICKED(IDC_CHANGENAME, &CClientDlg::OnBnClickedChangeName)
ON_MESSAGE(WM_NETWORK,&CClientDlg::OnNetwork)
END_MESSAGE_MAP()
// CClientDlg 消息处理程序
BOOL CClientDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
//为测试方便IP地址初始化为本机IP地址
m_ip.SetAddress(127,0,0,1);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CClientDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标显示。
//
HCURSOR CClientDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CClientDlg::OnBnClickedConnect()
{
UpdateData(TRUE);
DWORD ip;
m_ip.GetAddress(ip);
//初始化套接字DLL
WSADATA wsa;
if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){
MessageBox("套接字初始化失败!");
return;
}
//创建套接字
if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){
MessageBox("创建套接字失败!");
WSACleanup();
return;
}
ZeroMemory(&serverAddr,sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.S_un.S_addr = htonl(ip);
serverAddr.sin_port = htons(m_port);
//注册感兴趣的网络事件
if(WSAAsyncSelect(sock, m_hWnd, WM_NETWORK, FD_CONNECT | FD_READ | FD_CLOSE)==SOCKET_ERROR){
MessageBox("注册网络事件失败!");
closesocket(sock);
WSACleanup();
}
msg.Format("Connecting to %s:%d/n",inet_ntoa(serverAddr.sin_addr), ntohs(serverAddr.sin_port));
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel(msg);
if(connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr))==SOCKET_ERROR){
int err = WSAGetLastError();
if(err==WSAEWOULDBLOCK){
msg.Format("Waiting....../n");
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel(msg);
}else{
MessageBox("无法连接到服务器!");
closesocket(sock);
WSACleanup();
}
}
GetDlgItem(IDC_DISCONNECT)->EnableWindow(TRUE);
GetDlgItem(IDC_SEND)->EnableWindow(TRUE);
GetDlgItem(IDC_CHANGENAME)->EnableWindow(TRUE);
GetDlgItem(IDC_CONNECT)->EnableWindow(FALSE);
GetDlgItem(IDC_IP)->EnableWindow(FALSE);
GetDlgItem(IDC_PORT)->EnableWindow(FALSE);
}
LRESULT CClientDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
{
SOCKET s = (SOCKET)wParam;
WORD netEvent = WSAGETSELECTEVENT(lParam);
WORD error = WSAGETSELECTERROR(lParam);
if(error!=0){
msg.Format("Error code: %d/n",error);
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel(msg);
if(error==WSAECONNREFUSED){
msg.Format("Connection refused./n");
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel(msg);
closesocket(sock);
WSACleanup();
GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);
GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
GetDlgItem(IDC_CHANGENAME)->EnableWindow(FALSE);
GetDlgItem(IDC_CONNECT)->EnableWindow(TRUE);
GetDlgItem(IDC_IP)->EnableWindow(TRUE);
GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
}
return -1;
}
switch(netEvent){
case FD_CONNECT:
OnConnect(s);
break;
case FD_READ:
OnRead(s);
break;
case FD_CLOSE:
OnCloseSocket(s);
break;
}
return 0;
}
void CClientDlg::OnConnect(SOCKET s)
{
msg.Format("Connected to %s:%d/n",inet_ntoa(serverAddr.sin_addr),ntohs(serverAddr.sin_port));
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel(msg);
}
void CClientDlg::OnRead(SOCKET s)
{
static char buf[4096];
int bytes;
bytes = recv(s, buf, sizeof(buf), 0);
if(bytes==SOCKET_ERROR || bytes==0){
msg.Format("Socket error %d/n",WSAGetLastError());
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel(msg);
closesocket(s);
return;
}
buf[bytes] = '/0';
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel(buf);
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel("/n");
}
void CClientDlg::OnCloseSocket(SOCKET s)
{
closesocket(s);
msg = "Connection closed./n ";
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel(msg);
GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);
GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
GetDlgItem(IDC_CHANGENAME)->EnableWindow(FALSE);
GetDlgItem(IDC_CONNECT)->EnableWindow(TRUE);
GetDlgItem(IDC_IP)->EnableWindow(TRUE);
GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
WSACleanup();
}
void CClientDlg::OnBnClickedSend()
{
UpdateData(TRUE);
int bytes;
bytes = send(sock,(const char*)m_message,m_message.GetLength(),0);
if(bytes==SOCKET_ERROR){
int error = WSAGetLastError();
if(error==WSAEWOULDBLOCK){
msg = "Send failed for message: ";
msg+=m_message;
}else{
msg.Format("Connection is down.");
}
m_allmsg.SetSel(32767,32767);
m_allmsg.ReplaceSel(msg);
}
}
void CClientDlg::OnBnClickedDisconnect()
{
shutdown(sock,SD_SEND);
}
void CClientDlg::OnClose()
{
//MessageBox("close");
closesocket(sock);
WSACleanup();
CDialog::OnClose();
}
void CClientDlg::OnBnClickedQuit()
{
SendMessage(WM_CLOSE);
}
void CClientDlg::OnBnClickedChangeName()
{
UpdateData(TRUE);
char buf[100];
sprintf(buf,"name %s",m_nickname);
if(send(sock,buf,strlen(buf),0)==SOCKET_ERROR){
MessageBox("发送改名命令失败!");
return;
}
}
聊天室Windows控制台客户端
运行命令行为:Client.exe 127.0.0.1 8000
命令行参数分别为要连接的IP地址和端口
//Client.cpp
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
//引入静态链接库
#pragma comment(lib,"ws2_32.lib")
//缓冲区大小
#define BUFFER_SIZE 4096
//用来修改用户姓名的命令
const char* CMD_NAME="name";
//用来退出系统的命令
const char* CMD_BYE="bye";
//负责接收数据的线程
DWORD WINAPI ReceiveThreadProc( LPVOID lpParameter);
int main(int argc, char* argv[]){
//判断是否输入了IP地址和端口号
if(argc!=3){
printf("Usage: %s IPAddress PortNumber/n",argv[0]);
exit(-1);
}
//把字符串的IP地址转化为u_long
unsigned long ip;
if((ip=inet_addr(argv[1]))==INADDR_NONE){
printf("不合法的IP地址:%s",argv[1]);
exit(-1);
}
//把端口号转化成整数
short port;
if((port = atoi(argv[2]))==0){
printf("端口号有误!");
exit(-1);
}
printf("Connecting to %s:%d....../n",inet_ntoa(*(in_addr*)&ip),port);
WSADATA wsa;
//初始化套接字DLL
if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){
printf("套接字初始化失败!");
exit(-1);
}
//创建套接字
SOCKET sock;
if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){
printf("创建套接字失败!");
exit(-1);
}
struct sockaddr_in serverAddress;
memset(&serverAddress,0,sizeof(sockaddr_in));
serverAddress.sin_family=AF_INET;
serverAddress.sin_addr.S_un.S_addr = ip;
serverAddress.sin_port = htons(port);
//建立和服务器的连接
if(connect(sock,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR){
printf("建立连接失败!");
exit(-1);
}
//创建从服务器接收数据的线程
DWORD threadId;
CreateThread(NULL,0,ReceiveThreadProc,&sock,0,&threadId);
printf(">");
char buf[BUFFER_SIZE];
while(1){
//从控制台读取一行数据
gets(buf);
printf(">");
//发送给服务器
if(send(sock,buf,strlen(buf),0)==SOCKET_ERROR){
printf("发送数据失败!");
exit(-1);
}
}
//清理套接字占用的资源
WSACleanup();
return 0;
}
DWORD WINAPI ReceiveThreadProc( LPVOID lpParameter){
SOCKET s = *(SOCKET*)lpParameter;
char receiveBuf[BUFFER_SIZE];
int bytes;
while(1){
if((bytes=recv(s,receiveBuf,sizeof(receiveBuf),0))==SOCKET_ERROR){
printf("接收数据失败!/n");
exit(-1);
}
if(bytes==0){
printf("server is down./n");
exit(-1);
}
receiveBuf[bytes]='/0';
//如果用户输入了“bye”
if(strcmp(receiveBuf,CMD_BYE)==0){
closesocket(s);
exit(0);
}
printf("/n%s/n>",receiveBuf);
}
}