内容:
基于流式套接字的C/S通信程序,程序要求:客户端与服务器建立连接之后,客户端向服务器发送一个简单的四则算式(只含一个算符),服务器收到这个算式后,对其进行计算,并将计算结果回送给客户端,客户端将计算结果显示出来。。
服务端
加载listen_socket服务: SOCKET listen_socket;
构建 listen_socket 对象: listen_socket = socket(FA_INET,SOCK_STREAM,0);
绑定 sockaddr_in server_local;
// INADDR_ANY 表示0.0.0.0 可监听本地多个ip地址 可以运行在不同机器不用硬编码
// htonl()将32为无符号数从主机字节序转换为网络字节序(多字节字符存储方式可能为大端或者小端,这里可以统一处理,增加程序的健壮性,可运行于不同机器)
server_local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_local.sin_port = htons(6000); //绑定服务器监听端口
server_local.sin_family = AF_INET;
bind(listen_socket,(SOCKADDR*)&server_local,sizeof(server_local));
侦听 listen(server_local,5) //5表示 等待连接队列的最大长度
接受客户端连接请求、产生另一个work_socket对象与客户端通信;
sockaddr_in from;
SOCKET client;
int fromlen = sizeof(from);
client = accept(listen_socket,(SOCKADDR*)&from,&fromlen);
接收与发送
send(client,(char*)send_buffer,strlen(send_buffer),0);
recv(client,(char*)recv_buffer,strlen(recv_buffer),0)
关闭socket
shutdown(client_socket,SD_RECRIVE|SD_SEND|SEND_BOTH); //中断连接释放套接字资源
注销socket服务
closesocket(client);//关闭socket
客户端
加载socket服务 SOCKET client;
构建socket对象 client = socket(AF_INET,SOCK_STREAM,0);
绑定服务器IP并connect 服务
sockaddr_in server_addr;
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //将字符串转为长整形IP地址
server_addr.sin_port = htons(6000); //htons :host to network short
server_addr.sin_family = AF_INET;
connect(client,(SOCKADDR*)&server_addr, sizeof(server_addr));
接收与发送
recv send第二个参数均为char *
recv(client, recv_buffer, sizeof(recv_buffer),0);
send(client,send_buffer, sizeof(send_buffer),0);
关闭socket
shutdown(client,SD_BOTH);
注销socket服务
closesocket(client);
源代码
IDE VS2015
项目结构
init:
//InitSock.h
#pragma once
#include "winsock2.h"
class CInitSock
{
public:
CInitSock();
~CInitSock();
};
//InitSock.cpp
#include "stdafx.h"
#include "InitSock.h"
CInitSock::CInitSock()
{
WSADATA wsaData;
WORD socket_version = MAKEWORD(2,2);
if (::WSAStartup(socket_version,&wsaData)!=0)
{
exit(0);
}
}
CInitSock::~CInitSock()
{
::WSACleanup();
}
服务端客户端 设计的运算结构体
//Arithmetic.h
#pragma once
typedef struct arithmetic
{
char _operator; //+ - * /
int data_1; //操作数1
int data_2; //操作数2
} Arithmetic;
[点击并拖拽以移动]
server:
// server.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "conio.h"
#include "InitSock.h" //管理WinSock库 WSAStartup() WSACleanup完成初始化及回收。。
#include "iostream"
#include "Windows.h" //线程
#include "Arithmetic.h"
#pragma comment(lib,"ws2_32.lib")//静态导入win32库
//服务器线程
DWORD WINAPI ServerThread(LPVOID pParam);
int main()
{
int nRetCode = 0;
std::cout << "PRESS ESCAPE退出服务端程序\n";
DWORD threadID;
HANDLE hThread;
hThread = CreateThread(NULL, 0, ServerThread, NULL, 0, &threadID); // 创建线程
//WaitForSingleObject(hThread, INFINITE);
//CloseHandle(hThread); // 关闭内核对象
//AfxBeginThread(ServerThread, 0);
while (_getch() != 27);
return nRetCode;
}
DWORD WINAPI ServerThread(LPVOID pParam)
{
std::cout << "启动TCP服务器初始化。。。\n";
CInitSock cInitSock;
//填充sockaddr_in
sockaddr_in server_local;
server_local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_local.sin_port = htons(6000);
server_local.sin_family = AF_INET;
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0);//创建TCP Socket
if (INVALID_SOCKET == listen_socket)
{
std::cout << "创建服务器失败 \n";
return 0;
}
//bind
if (0 != bind(listen_socket, (SOCKADDR *)&server_local, sizeof(server_local)))
{
std::cout << "服务器绑定失败 \n";
return 0;
}
//listen
if (0 != listen(listen_socket, 5))
{
std::cout << "listen failure \n";
return 0;
}
//accept
sockaddr_in from;
SOCKET client;
int fromlen = sizeof(from);
char buffer[1024] = { 0 };
while (true)
{
client = accept(listen_socket, (SOCKADDR *)&from, &fromlen);
if (client == INVALID_SOCKET);
{
std::cout << "Failed Accept \n";
}
sprintf(buffer, "hello client from server : \n", inet_ntoa(from.sin_addr));
send(client, buffer, strlen(buffer), 0);
std::cout << "Connection from " << inet_ntoa(from.sin_addr) << "\n";
Arithmetic arithmetic;
recv(client, (char*)&arithmetic, sizeof(arithmetic), 0);
printf("%receive expression: %d %c %d = ? from client\n", arithmetic.data_1, arithmetic._operator, arithmetic.data_2);
//计算结果
int ans = 0;
switch (arithmetic._operator)
{
case '+':
ans = arithmetic.data_1 + arithmetic.data_2;
break;
case '-':
ans = arithmetic.data_1 - arithmetic.data_2;
break;
case '*':
ans = arithmetic.data_1 * arithmetic.data_2;
break;
case '/':
ans = arithmetic.data_1 / arithmetic.data_2;
break;
default:
break;
}
memset(buffer, 0, sizeof(buffer));
printf("send answer = %d to client\n", ans);
buffer[strlen(buffer)] = '\0';
send(client, (char*)&ans, sizeof(ans),0);
//close clientsocket
shutdown(client, SD_BOTH);
closesocket(client);
}
shutdown(listen_socket, SD_BOTH);
closesocket(listen_socket);
return 0;
}
客户端设计:
// Client.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "InitSock.h"
#include "iostream"
#include "conio.h"
#include "Windows.h"
#include "Arithmetic.h"
#pragma comment(lib,"ws2_32.lib")//静态导入win32库
DWORD WINAPI ClientThread(LPVOID pParam);
int main()
{
int nRetCode = 0;
Sleep(5000);
std::cout << "PRESS ESCAPE退出客户端程序\n";
DWORD threadID;
HANDLE hThread;
hThread = CreateThread(NULL, 0, ClientThread, NULL, 0, &threadID); // 创建线程
while (_getch() != 27);
return nRetCode;
}
DWORD WINAPI ClientThread(LPVOID pParam)
{
CInitSock cInitSock;
SOCKET client = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in server_addr;
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(6000);
server_addr.sin_family = AF_INET;
printf("客户端已经启动\r\n");
int conn;
conn = connect(client, (SOCKADDR*)&server_addr, sizeof(server_addr));
if (conn!=0)
{
std::cout << _T("客户端发起连接失败\n");
return 0;
}
char recv_buffer[1024];
int nRecv = 0;
nRecv = recv(client, recv_buffer, sizeof(recv_buffer),0);
if (nRecv > 0)
{
recv_buffer[nRecv] = '\0';
printf("接收到的数据:%s\n", recv_buffer);
//std::cout << recv_buffer;
memset(recv_buffer, 0, strlen(recv_buffer));
Arithmetic arithmetic;
arithmetic.data_1 = 1;
arithmetic.data_2 = 1;
arithmetic._operator = '+';
printf("send expression: %d %c %d = ? to server\n", arithmetic.data_1, arithmetic._operator, arithmetic.data_2);
send(client,(char*)&arithmetic, sizeof(arithmetic),0);
memset(recv_buffer, 0, sizeof(recv_buffer));
//接收四则运算结果
int ans = 0;
nRecv = recv(client, (char*)&ans, sizeof(ans),0);
printf("receive answer = %d from server\n", ans);
}
shutdown(client, SD_BOTH);
closesocket(client);
system("pause");
return 0;
}
结果
总结:
新建一个VS解决方案,分别添加Client和Server两个Win32控制台项目并设置为同时启动方式为2个项目同时启动,注意在客户端启动时先Sleep(5000),等待服务器启动完成然后connect服务端。在每个子项目的主线程中新建一个线程进行socket操作,方便在主线程中控制程序结束。