實(shí)現(xiàn)英漢互譯
思路
我們?cè)诳蛻舳税l(fā)英文,服務(wù)端做翻譯工作,讓翻譯好的中文再次發(fā)給我們的客戶端,然后打印出來(lái)。
服務(wù)端代碼
翻譯的操作
創(chuàng)建一個(gè)txt文件里面包含英漢互譯的數(shù)據(jù)
dict.txt
banana:香蕉
apple:蘋(píng)果
pig:豬
beef:牛肉
hello:你好
對(duì)txt中的數(shù)據(jù)進(jìn)行操作
分割函數(shù)
將英漢通過(guò)冒號(hào)分開(kāi)。
// 分割函數(shù)
static bool cutString(const string &target, string *s1, string *s2, const string &sep)
{
// apple:蘋(píng)果
auto pos = target.find(sep);
if (pos == string::npos)
{
return false;
}
*s1 = target.substr(0, pos);
*s2 = target.substr(pos + sep.size());
return true;
}
將文件數(shù)據(jù)插入map里面
// 按行將文件里面的數(shù)據(jù)給插入到map里面
static void initDict()
{
dict.clear();
ifstream in(dictTxt, std::ios::binary);
if (!in.is_open())
{
std::cerr << "open file " << dictTxt << " error" << endl;
exit(OPEN_ERR);
}
string line;
std::string key, value;
while (getline(in, line))
{
// cout << line << endl;
if (cutString(line, &key, &value, ":"))
{
dict.insert(make_pair(key, value));
}
}
in.close();
cout << "load dict success" << endl;
}
重新加載文件
通過(guò)捕捉2號(hào)(ctrl c)信號(hào)來(lái)進(jìn)行重新加載文件。
void reload(int signo)
{
(void)signo;
initDict();
}
// ./udpClient server_ip server_port
int main(int argc, char *argv[])
{
signal(2, reload); // 通過(guò)發(fā)2號(hào)信號(hào)來(lái)使dict.txt中的數(shù)據(jù)進(jìn)行更新
}
網(wǎng)絡(luò)通信的操作
將翻譯后的數(shù)據(jù)發(fā)送給客戶端
void handlerMessage(int sockfd, string message, uint16_t clientport, string clientip)
{
// 就可以對(duì)message進(jìn)行特定的業(yè)務(wù)處理,而不關(guān)心message怎么來(lái)的 --- server通信和業(yè)務(wù)邏輯解耦!
// 嬰兒版的業(yè)務(wù)邏輯
string response_message;
auto iter = dict.find(message);
if (iter == dict.end())
response_message = "unknown";
else
response_message = iter->second;
// 開(kāi)始返回
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
// 在服務(wù)端收到客戶端數(shù)據(jù)的時(shí)候我們獲取到了客戶端的端口號(hào)和ip,因此回調(diào)到了這個(gè)函數(shù)中,
// 此時(shí)我們就可以使用客戶端的端口號(hào)和ip來(lái)給客戶端發(fā)送翻譯后的信息
sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&client, sizeof(client));
}
客戶端代碼
創(chuàng)建socket
void initClient()
{
// 1. 創(chuàng)建socket
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (_sockfd == -1)
{
cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
exit(2);
}
cout << "socket success: " << _sockfd << endl;
// 2. client要不要bind[不需要的] , client要不要顯示的bind,需不需要程序員自己bind? 不需要
// 寫(xiě)服務(wù)器的一家公司,寫(xiě)客戶端是無(wú)數(shù)家公司 -- 因此讓OS自動(dòng)形成端口進(jìn)行bind! -- OS在什么時(shí)候,如何bind
}
數(shù)據(jù)處理
將用戶輸入的數(shù)據(jù)發(fā)送給服務(wù)端,并且接受服務(wù)端翻譯后的數(shù)據(jù)并進(jìn)行打印。
void run()
{
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(_serverip.c_str());
server.sin_port = htons(_serverport);
string message;
char buffer[1024];
while (!_quit)
{
// fprintf(stderr, "Please Enter# ");
// fflush(stderr);
// fgets(buffer, sizeof(buffer), stdin);
cout << "Please Enter# ";
cin >> message;
// buffer[strlen(buffer) - 1] = 0;
// message = buffer;
sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
char recv_buffer[1024];
// 接受翻譯后的信息
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
//
ssize_t s = recvfrom(_sockfd, recv_buffer, sizeof(recv_buffer) - 1, 0, (struct sockaddr *)&peer, &len);
if (s > 0)
{
// 讀取數(shù)據(jù)
buffer[s] = 0;
}
cout << "服務(wù)器翻譯成# " << recv_buffer << endl;
// recv_buffer[0] = 0;
memset(recv_buffer, 0, sizeof(buffer)); // 清空緩沖區(qū)
}
}
成果展示
程序替換
思路
主要使用popen接口同時(shí)實(shí)現(xiàn)管道+創(chuàng)建子進(jìn)程+程序替換的功能。將我們客戶端輸入的命令信息發(fā)送給服務(wù)端。服務(wù)端將該命令經(jīng)過(guò)popen接口讓它執(zhí)行命令運(yùn)行出來(lái)的結(jié)果放在一個(gè)文件中,然后在將文件中的內(nèi)容讀取出來(lái)發(fā)送給客戶端。
popen接口
#include <stdio.h>
FILE *popen(const char *command,const char *type); // 相當(dāng)于pipe+fork+exec*
int pclose(FILE *stream);
參數(shù)
const char *command
未來(lái)要執(zhí)行的命令字符串:?
ls -a -l 等 ?可以將這些命令執(zhí)行的結(jié)果返回到一個(gè)文件當(dāng)中
const char *type
對(duì)文件的操作方式"r" "w"?"a" 等
返回值
返回 nullptr 說(shuō)明執(zhí)行失敗了
服務(wù)端代碼
void execCommand(int sockfd, string cmd, uint16_t clientport, string clientip)
{
// 1. com解析,ls -a -l
// 2. 如果必要,可能需要fork,exec*
if (cmd.find("rm") != string::npos || cmd.find("mv") != string::npos || cmd.find("remdir") != string::npos)
{
cerr << clientip << " : " << clientport << " 正在做一個(gè)非法的操作: " << cmd << endl;
return;
}
string response;
FILE *fp = popen(cmd.c_str(), "r");
if (fp == nullptr)
response = cmd + " exec failed";
char line[1024];
// 按行讀取
while (fgets(line, sizeof(line), fp))
{
response += line;
}
pclose(fp);
// 開(kāi)始返回
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
// 在服務(wù)端收到客戶端數(shù)據(jù)的時(shí)候我們獲取到了客戶端的端口號(hào)和ip,因此回調(diào)到了這個(gè)函數(shù)中,
// 此時(shí)我們就可以使用客戶端的端口號(hào)和ip來(lái)給客戶端發(fā)送翻譯后的信息
sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
}
成果展示?
多線程聊天室
思路
通過(guò)在客戶端的角度創(chuàng)建多線程,一個(gè)線程進(jìn)行發(fā)消息,一個(gè)線程進(jìn)行讀消息。這樣我們就能讓多個(gè)線程(用戶)一起聊天。
讓用戶的id(ip + "-" + port)作為key值,User作為value值。來(lái)形成一個(gè)個(gè)unordered_map類(lèi)型的容器。如果用戶上線了就將該對(duì)象添加到unordered_map容器里面,下線就從unordered_map容器刪除。
通過(guò)輸入"online"來(lái)將該用戶添加到unordered_map容器里面,意味上線。
通過(guò)輸入"offline"來(lái)將該用戶從unordered_map容器里面刪除,意味下線。
我們服務(wù)端收到客戶端發(fā)來(lái)的數(shù)據(jù)的時(shí)候通過(guò)isOnline函數(shù)來(lái)判斷該用戶是否在unordered_map容器里面,如果在就將該信息發(fā)送給客戶端并且客戶端接受后打印出來(lái),如果不在直接打印"未上線"。
用戶信息代碼代碼 onlineUser.hpp
#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
using namespace std;
class User
{
public:
User(const string &ip, const uint16_t &port)
: _ip(ip), _port(port)
{
}
~User()
{
}
string ip()
{
return _ip;
}
uint16_t port()
{
return _port;
}
private:
string _ip;
uint16_t _port;
};
class OnlineUser
{
public:
OnlineUser() {}
~OnlineUser() {}
void addUser(const string &ip, const uint16_t &port)
{
string id = ip + "-" + to_string(port);
users.insert(make_pair(id, User(ip, port)));
}
void delUser(const string &ip, const uint16_t &port)
{
string id = ip + "-" + to_string(port);
users.erase(id);
}
bool isOnline(const string &ip, const uint16_t &port)
{
string id = ip + "-" + to_string(port);
return users.find(id) == users.end() ? false : true;
}
void broadcastMessage(int sockfd, const string &ip, const uint16_t &port, const string &message)
{
for (auto &user : users)
{
// 開(kāi)始返回
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(user.second.port());
client.sin_addr.s_addr = inet_addr(user.second.ip().c_str());
// 將用戶id也加入
string s = ip + "-" + to_string(port) + "# ";
s += message;
sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr *)&client, sizeof(client));
}
}
private:
unordered_map<string, User> users;
};
服務(wù)端代碼
// demo 3
void routeMessage(int sockfd, string message, uint16_t clientport, string clientip)
{
// 判斷是否上線
// 上線就將該用戶添加到onlineuser
if (message == "online")
onlineuser.addUser(clientip, clientport);
// 下線就將該用戶在onlineuser中刪除
if ((message == "offline"))
onlineuser.delUser(clientip, clientport);
// 如果以下if為真 那么說(shuō)明用戶已經(jīng)上線,因此需要將用戶發(fā)的信息進(jìn)行路由
if (onlineuser.isOnline(clientip, clientport))
{
// 消息的路由
onlineuser.broadcastMessage(sockfd, clientip, clientport, message);
}
else
{
// 開(kāi)始返回
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
string response_message = "你還沒(méi)有上線,請(qǐng)先上線,運(yùn)行: online";
sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&client, sizeof(client));
}
客戶端代碼(多線程)
static void *readMessage(void *args)
{
int sockfd = *(static_cast<int *>(args));
pthread_detach(pthread_self());
while (true)
{
char buffer_recv[1024];
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
ssize_t s = recvfrom(sockfd, buffer_recv, sizeof(buffer_recv) - 1, 0, (struct sockaddr *)&peer, &len);
if (s >= 0)
buffer_recv[s] = 0;
cout << buffer_recv << endl;
}
return nullptr;
}
void run()
{
pthread_create(&_reader, nullptr, readMessage, (void *)&_sockfd);
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(_serverip.c_str());
server.sin_port = htons(_serverport);
string message;
char buffer[1024];
while (!_quit)
{
fprintf(stderr, "Please Enter# ");
fflush(stderr);
fgets(buffer, sizeof(buffer), stdin);
buffer[strlen(buffer) - 1] = 0;
message = buffer;
sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
}
成果展示
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-515784.html
Windows與Linux通信
思路
讓Linux當(dāng)服務(wù)端,Windows當(dāng)客戶端。讓W(xué)indows與Linux通信,主要用到了一些Windows系統(tǒng)的socket創(chuàng)建的接口和sendto等接口,這些接口與Linux是及其相似的不做過(guò)多贅述。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-515784.html
Windows代碼
windows_client.cpp
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<string>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib") // 將ws2_32.lib的庫(kù)
using namespace std;
// 顯示的定義并且初始化ip和port
uint16_t serverport = 8080;
string serverip = "124.223.97.182";
int main()
{
WSAData wsd;
//啟動(dòng)Winsock
//查看庫(kù)的版本是否正確
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
cout << "WSAStartup Error = " << WSAGetLastError() << endl;
return 0;
}
else cout << "WSAStartup Success" << endl;
//創(chuàng)建套接字
const SOCKET csock = socket(AF_INET, SOCK_DGRAM, 0);
if (csock == SOCKET_ERROR)
{
cout << "sock Error = " << WSAGetLastError() << endl;
return 1;
}
else cout << "socket Success" << endl;
//定義一個(gè) struct sockaddr_in類(lèi)型的結(jié)構(gòu)體
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);// 主機(jī)轉(zhuǎn)網(wǎng)絡(luò)
server.sin_addr.s_addr = inet_addr(serverip.c_str()); // 主機(jī)轉(zhuǎn)網(wǎng)絡(luò) 字符串轉(zhuǎn)整型
#define NUM 1024
char inbuffer[NUM];
string line;
while (true)
{
//發(fā)送邏輯
cout << "Please Enter# ";
getline(cin, line);
int n = sendto(csock, line.c_str(), line.size(), 0, (struct sockaddr*)&server, sizeof(server));
if (n < 0)
{
cerr << "sendto error!" << endl;
break;
}
struct sockaddr_in peer;
int peerlen = sizeof(peer);
//收取數(shù)據(jù)
inbuffer[0] = 0; // C風(fēng)格的清空
n = recvfrom(csock, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &peerlen);
if (n > 0)
{
/*inbuffer[n] = 0;
cout << "serever 返回的消息是# " << inbuffer << endl;*/
}
else break;
}
closesocket(csock);
// 釋放資源
WSACleanup();
return 0;
}
Linux代碼
makefile
cc=g++
udpServer:udpServer.cc
$(cc) -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f udpServer
udpServer.hpp
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <functional>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
using namespace std;
namespace Server
{
enum
{
SOCKET_ERR = 1,
BIND_ERR,
OPEN_ERR
};
const int NUM = 1024;
static const string defaultIp = "0.0.0.0";
typedef function<void(int, string, uint16_t, string)> func_t;
class udpServer
{
public:
udpServer(const func_t &cb, const uint16_t &port, const string ip = defaultIp)
: _callback(cb), _serverport(port), _serverip(defaultIp), _sockfd(-1)
{
// cout << "拷貝構(gòu)造" << endl;
}
void initServer()
{
// 1. 套接字的創(chuàng)建
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (_sockfd == -1)
{
cerr << "socket error :" << errno << " : " << strerror(errno) << endl;
exit(SOCKET_ERR);
}
cout << "socket success : " << _sockfd << endl;
// 2. bind
struct sockaddr_in local;
bzero(&local, sizeof(local)); // 初始化local
local.sin_family = AF_INET;
// 1. 主機(jī)轉(zhuǎn)網(wǎng)絡(luò) 點(diǎn)分十進(jìn)制轉(zhuǎn)int
local.sin_addr.s_addr = inet_addr(_serverip.c_str());
// local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(_serverport);
int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
if (n == -1)
{
cerr << "bind error: " << errno << " : " << strerror(errno) << endl;
exit(BIND_ERR);
}
}
void start()
{
char buffer[NUM];
for (;;)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
//
ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
if (s > 0)
{
// 讀取數(shù)據(jù)
buffer[s] = 0;
uint16_t clientport = ntohs(peer.sin_port);
string clientip = inet_ntoa(peer.sin_addr);
string message = buffer;
cout << clientip << " [" << clientport << "]# " << message << endl;
// 回調(diào) 文件描述符和客戶端的端口號(hào)和ip
_callback(_sockfd,message,clientport,clientip);
}
}
}
~udpServer()
{
}
private:
int _sockfd;
uint16_t _serverport;
string _serverip;
func_t _callback;
};
} // udpServer
udpServer.cc
#include "udpServer.hpp"
#include <memory>
#include <fstream>
#include <unordered_map>
#include <signal.h>
using namespace Server;
using namespace std;
static void Usage(string proc)
{
cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
void handlerMessage(int sockfd, string message, uint16_t clientport, string clientip)
{
string response_message = message;
response_message += " [server echo]";
// 開(kāi)始返回
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&client, sizeof(client));
}
// ./udpClient server_ip server_port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(1);
}
uint16_t port = atoi(argv[1]); // 字符串轉(zhuǎn)整數(shù)
unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port));
usvr->initServer();
usvr->start();
return 0;
}
成果展示
到了這里,關(guān)于UDP套接字的通信(實(shí)現(xiàn)英漢互譯/程序替換/多線程聊天室/Windows與Linux通信)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!