參考
傳送門 - 1 - csdn - 2112222222222
傳送門 - 2 - bilibili - 憧憬少
傳送門 - 3 -
要求
- 開發一個聊天程式
- 包含客戶端和服務器段
- 編程語言不限
- 要能在兩臺PC機上運行
如何實作
通過 socket 實作 兩臺pc之間的聊天
什么是socket
- Socket是應用層與TCP/IP協議族通信的中間軟體抽象層,它是一組介面,
- 在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket介面后面,對用戶來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議,
socket 是如何連接的
- 服務器端先初始化Socket
- 然后與埠系結(bind),對埠進行監聽(listen),呼叫accept阻塞,等待客戶端連接,
- 在這時如果有個客戶端初始化一個Socket,然后連接服務器(connect)
- 如果連接成功,這時客戶端與服務器端的連接就建立了
- 客戶端發送資料請求,服務器端接收請求并處理請求,然后把回應資料發送給客戶端,客戶端讀取資料
- 最后關閉連接,一次互動結束,
參考檔案:傳送門
實作環境
- Visual Studio 2013
- windows 7 (機房電腦)
相關問題解決
解決 mircsoft visual studio 2013 無法打開 winsock2.h 頭檔案
- 確保
#pragma comment (lib, "ws2_32.lib") // 鏈接ws2_32.lib檔案
該陳述句放置在前面,首先鏈接ws2_32.lib檔案
- 打開 專案

選擇 專案屬性
配置屬性調整平臺工具集為XP那一項

記得點擊應用,然后確定
解決無法打開 “stdafx.h” 檔案的問題
microsoft visual studio 2013版本已經提前幫助專案預編譯該檔案了,所以不需要include

編譯運行產生方法不安全提示時解決辦法

server于client都將SDL檢查設定為否
結果代碼
服務器端代碼
//server
#pragma comment(linker, "/STACK:36777216")
//#pragma GCC optimize ("O2")
/**
* This code has been written by YueGuang, feel free to ask me question. Blog: http://www.moonl1ght.xyz
* created:
*/
#define LOCAL
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//#include <tr1/unordered_set>
//#include <tr1/unordered_map>
//#include <array>
using namespace std;
// un template header
#pragma comment (lib, "ws2_32.lib") // 鏈接ws2_32.lib檔案
#include <Winsock2.h> //windows socket編程頭檔案
//自定義頭檔案
//}/* .................................................................................................................................. */
/*
bug 說明區域
1.顏色設定setcolor還不能使用
*/
/*
變數解釋說明區域 QAQ
*/
// 全域常量
const int BUF_SIZE = 2048;
const int SEND_SIZE = 1000;
const int MAX_BUF_SIZE = 500;
const int NICKNAME_LEN = 20;
const int MAX_CLIENT_COUNT = 20;
// 全域變數
SOCKET sockSer, sockCli;
SOCKADDR_IN addrSer, addrCli;// address 地址 客戶端地址和服務器地址
SOCKET NewConnection; //用于接受來自客戶端的鏈接
int clientCount = 0;
int naddr = sizeof(SOCKADDR_IN);
int Ret;
char sendbuf[BUF_SIZE];
char inputbuf[BUF_SIZE];
char recvbuf[BUF_SIZE];
//該結構體的目的是允許多臺PC機對服務器進行訪問
//全域函式
using namespace std;
int main(){
//\
第一步,加載socket函式,載入socket庫
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){
//setColor(COLOR_ERROR);
cout << "吶吶吶,載入socket庫失敗!" << '\n';
system("pause");
return 0;
}
//\
第二步,創建一個監聽套接字sockser, 創建socket(地址描述(AF_INET格式 - ipv4),指定socket型別(使用的是流式套接字即TCP協議), 指定協議
if ((sockSer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET){
printf("當前為無效套接,程式結束");
system("pause");
return 0;
}
//\
第三步,初始化服務器的地址包,填寫相關資訊
/*
* 1.AF_INET優先賦值,這是由于該值是告訴winsock我們使用的是ip地址簇
* 2.填寫用來通訊的ip地址
* 3.填寫埠號
*
*/
char ip[] = "192.168.81.90";
cout << "吶吶吶,本地IP是" << ip << "該電腦已經開啟!\n";
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(5000);
addrSer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//\
第四步,將創建的sockser套接字和上面填寫的相關地址資訊系結在一起,bind函式有很多,我們要選擇的是sock中的所以加頭檔案的時候要注意不要多加
if (bind((SOCKET)sockSer, (SOCKADDR *)&addrSer, sizeof(addrSer)) == SOCKET_ERROR)
{
printf("BIND_ERROR: %d\n", SOCKET_ERROR);
return 0;
}
cout << "二次元世界連接成功!" << endl;
//\
第五步,讓服務器Socket開啟監聽,并且設定最大的等待連接數,等待連接數(半連接)過大會給服務器造成負載
if (listen(sockSer, 5) == SOCKET_ERROR){
printf("LISTEN_ERROR: %d\n", SOCKET_ERROR);
system("pause");
return 0;
}
//\
第六步,客戶端連接到達時,本服務器需接受連接,注意接受鏈接用的是客戶端的變數即Cli
int ClientAddrLen = sizeof(addrCli);
printf("正在接受連接...");
if ((NewConnection = accept(sockSer, (SOCKADDR *)&addrCli, &ClientAddrLen)) == INVALID_SOCKET)
{
printf("ACCPET_ERROR: %d\n", INVALID_SOCKET);
closesocket(sockSer);
return 0;
}
printf("檢測到一個來自三次元的連接: %s 埠:%d\n", inet_ntoa(addrCli.sin_addr), ntohs(addrCli.sin_port));
//\
第七步,開始接聽,true情況下行程不關閉就不會結束,但需要考慮電腦
while (true)
{
//接收資料
Ret = recv(NewConnection, recvbuf, BUF_SIZE, 0);
if (Ret > 0)
printf("JOJO對你說: %s\n", recvbuf);
else if (Ret < 0)
printf("RECV_ERROR: %d\n", SOCKET_ERROR);
else
{
printf("對方覺得二次元濃度過高,退出了聊天!");
break;
}
//發送資料
printf("\n說:");
scanf("%s", sendbuf);
if (strcmp(sendbuf, "quit") == 0) //退出
break;
if (send(NewConnection, sendbuf, BUF_SIZE, 0) == SOCKET_ERROR)
{
printf("訊息發送失敗!\n");
break;
}
}
//關閉連接
shutdown(NewConnection, SD_BOTH);
closesocket(NewConnection);
//關閉socket庫
closesocket(sockSer);
//清空加載項
if (WSACleanup() == SOCKET_ERROR)
{
printf("WSACLEANUP_ERROR: %d\n", WSAGetLastError());
return 0;
}
system("pause");
return 0;
}
客戶端代碼
#pragma comment(linker, "/STACK:36777216")
//client
//client
#pragma comment(linker, "/STACK:36777216")
//#pragma GCC optimize ("O2")
/**
* This code has been written by YueGuang, feel free to ask me question. Blog: http://www.moonl1ght.xyz
* created:
*/
#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
//#include <tr1/unordered_set>
//#include <tr1/unordered_map>
//#include <array>
using namespace std;
// un template header
#pragma comment (lib, "ws2_32.lib") // 鏈接ws2_32.lib檔案
#include <Winsock2.h> //windows socket編程頭檔案
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//全域常量
const int BUF_SIZE = 2048;
const int SEND_SIZE = 1000;
const int MAX_BUF_SIZE = 200;
//全域變數
SOCKET sockSer, sockCli;
SOCKADDR_IN addrSer, addrCli;// address 地址 客戶端地址和服務器地址
int naddr = sizeof(SOCKADDR_IN);
char sendbuf[BUF_SIZE];
char inputbuf[BUF_SIZE];
char recvbuf[BUF_SIZE];
int Ret;
//全域函式
// 接收執行緒的設定是死回圈不斷得提交recv申請,如果有反饋,就輸出,
DWORD WINAPI Client_Receive_Thread(LPVOID lp) {
SOCKET *s = (SOCKET*)lp;
int nrecv;
while (true)
{
// 監聽服務器端訊息
char recvBuf[SEND_SIZE]; //注意使用的是 B
// recv 的第一個引數是當前socket
int res = recv(sockCli, recvBuf, SEND_SIZE, 0); // 最后引數設定成0,表示非阻塞
if (res > 0) // 由于socket默認的阻塞,因此recv會自動阻塞
{
printf("%s\n", recvBuf);
}
}
}
int main(){
//\
加載socket函式,載入socket庫
WSADATA WSAData;
if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0){
//setColor(COLOR_ERROR);
cout << "吶吶吶,載入socket庫失敗!" << '\n';
system("pause");
return 0;
}
char ip[] = "192.168.81.90";
cout << "吶吶吶,本地IP是" << ip << "該電腦已經開啟!\n";
//setColor(COLOR_NORMAL);
//初始化服務器地址
addrSer.sin_family = AF_INET;
addrSer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSer.sin_port = htons(5000);
//創建socket(地址描述(AF_INET格式 - ipv4),指定socket型別(使用的是流式套接字即TCP協議), 指定協議
sockSer = socket(AF_INET, SOCK_STREAM, 0);
//建立連接
if (connect(sockSer, (SOCKADDR *)&addrSer, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
cout << "CONNECT_ERROR : " << SOCKET_ERROR << endl;
return 0;
}
else
{
cout << "二次元世界,連接成功!" << endl;
}
//讀取用戶名
char username[50];
printf("請輸入您的用戶名: ");
scanf("%s", username);
const int max_connet_cnt = 20; //最大嘗試連接次數
int cnt = 0;
while (true){
//發送資料
cout << '\n' << username << "說:";
cin >> sendbuf;
if (strcmp(sendbuf, "quit") == 0)
{
break;
}
if (send(sockSer, sendbuf, BUF_SIZE, 0) == SOCKET_ERROR)
{
cout << "訊息發送失敗" << endl;
break;
}
//接收資料
Ret = recv(sockSer, recvbuf, BUF_SIZE, 0);
if (Ret < 0)
{
cout << "RECV_ERROR" << SOCKET_ERROR << endl;
break;
}
else if (Ret == 0)
{
cout << "對方退出聊天程式,聊天結束" << endl;
break;
}
else
{
cout << "Server對你說:" << recvbuf << endl;
}
}
//關閉socket庫
closesocket(sockSer);
closesocket(sockCli);
WSACleanup(); //清空加載項
return 0;
}
運行截圖1

獲取本地IP的函式
void getLocalIP(char localIp[], int n){
gethostname(localIp, n);
HOSTENT *host = gethostbyname(localIp);
in_addr PcAddr;
int i = 0;
while (true){
char * p = host->h_addr_list[i];
if (p == NULL){
break;
}
memcpy(&(PcAddr, S_un.S_addr), p, host->h_length);
strcpy(localIp, inet_ntoa(PcAddr));
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/241831.html
標籤:其他
上一篇:Uncaught TypeError: Cannot read property 'checked' of undefined錯誤解決
