C、C++網絡編程(雙電腦串口通訊)
在閱讀本文前。請確保先查看這篇文章,學習有關TCP網編與單電腦信息傳輸實踐。
文章目錄
本文爲華中科技大學 人工智能與自動化學院 自動化1801 魏靖旻編寫整理,如有任何錯誤請及時指正。
在第一篇的基礎上,我總結併成功實現了雙電腦的即時通訊方式。
其實和單電腦最大的差別只在於兩個方面,一個是IPV4地址的確定,一個是Port活動端口號的綁定。在單電腦信息傳輸基礎上,實現雙電腦串口通訊其實只需要根據以下的操作 2,3 修改兩個參數。
1.讓兩臺電腦連接同一個局域網
2.查詢IP地址
a.使用cmd命令提示符,在要運行server端的電腦上查詢
- netstat -ano:
- ipconfig:
我們注意到此時的IPv4的地址是192.168.43.181 (IPv6和IPv4地址的區別自己百度)
b.然後我們在netstat -ano活動連接中選擇本機IPv4的地址對應的端口號
然後我們選擇其中的任何一個端口號(:後面的部分,比如說192.168.43.181:139,這個的端口號就是139)
- TIPS:
至於選擇什麼狀態的PORT,爲了減小偶然誤差,我在ESTABLISHED、CLOSE_WAIT、TIME_WAIT都成功過,所以自己多試一些端口就出來了。
然後很神奇的是,只要你的這兩個程序成功通訊了一次,之後成功率就會大大提高。然後你會發現你選擇的端口號和電腦給你配置的端口號可能會不一樣,個人覺得應該是電腦自動配置端口,這裏影響不大。
同時也說明一下,大部分電腦都有自己的本地端口,如果僅僅在一臺電腦上進行通訊則用“127.0.0.1”+任意選擇的port即可輕鬆實現。
3.配置端口
這也是最重要的一步,對應程序源碼中的:
unsigned short Port = 49911; //端口號,根據用戶實際修改
以及
ServerAddr.sin_addr.s_addr = inet_addr("192.168.43.53"); //具體的IP地址,根據用戶實際修改
這兩步非常重要,後面的參數根據第二部查詢IP地址得到的結果進行修改。配置好一個好的端口號,纔是成功的前提。
-
在C/C++程序中,我們將兩個源文件配置的端口號PORT和IP_address改成上述所說對應的即可。
-
客服端(clinet.cpp)和服務器端(server.cpp)的IP地址以及端口號要保持一致。
-
在上述我的實踐過程中,IPv4就是“192.168.43.181”,端口號我選擇的是57618。
-
注意:重新連接新的局域網或者發現程序有問題的時候:
a.檢查本機防火牆是否阻止公用網絡訪問
b.要重新在cmd終端輸入“netstat -ano”和“ipconfig”查看最新的IPv4的地址的活動接口
c.之後不斷的換IP地址對應的端口,不斷地嘗試(前文提到過server.cpp和client.cpp兩個端要保證IP地址和端口號一致)
4.運行程序
5.程序源碼©
二者同時連接了這輛AE86,你敢上嗎的熱點。
其中我標了註釋“//根據用戶實際更改”,則按照上述第二步和第三步提到的方式更改
server端
/*
* 服務器端 Server.c
*
*/
#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#define BUFFSIZE 1024
int main(int argc, char**argv)
{
int Ret;
WSADATA wsaData;
SOCKET ListeningSocket;
SOCKET NewConnection;
SOCKADDR_IN ServerAddr;
SOCKADDR_IN ClientAddr;
int ClientAddrLen = sizeof(ClientAddr);
unsigned short Port = 49911; //端口號,根據用戶實際修改
char sendData[BUFFSIZE];
char recvData[BUFFSIZE];
if((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
{
printf("WSASTARTUP_ERROR: %d\n", Ret);
return 0;
}
//創建一個套接字來監聽客戶機連接
if((ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
printf("SOCKET_ERROR: %d\n", INVALID_SOCKET);
return 0;
}
/*
* 填充SOCKADDR_IN結構,這個結構將告知bind我們想要在5150端口監聽所有接口上的連接
*/
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(Port); //將端口變量從主機字節順序轉換位網絡字節順序
ServerAddr.sin_addr.s_addr = inet_addr("192.168.43.53"); //具體的IP地址,根據用戶實際修改
//ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//使用bind將這個地址信息和套接字綁定起來
if(bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
{
printf("BIND_ERROR: %d\n", SOCKET_ERROR);
return 0;
}
//監聽客戶機連接。這裏使用5個backlog
if(listen(ListeningSocket, 5) == SOCKET_ERROR)
{
printf("LISTEN_ERROR: %d\n", SOCKET_ERROR);
return 0;
}
//連接到達時,接受連接
printf("正在接受連接...");
if((NewConnection = accept(ListeningSocket, (SOCKADDR *)&ClientAddr, &ClientAddrLen)) == INVALID_SOCKET)
{
printf("ACCPET_ERROR: %d\n", INVALID_SOCKET);
closesocket(ListeningSocket);
return 0;
}
printf("檢測到一個連接: %s 端口:%d\n", inet_ntoa(ClientAddr.sin_addr), ntohs(ClientAddr.sin_port));
//聊天
while(1)
{
//接收數據
Ret = recv(NewConnection, recvData, BUFFSIZE, 0);
if(Ret > 0)
printf("小民: %s\n", recvData);
else if(Ret < 0)
printf("RECV_ERROR: %d\n", SOCKET_ERROR);
else
{
printf("對方退出程序,聊天結束!");
break;
}
//發送數據
printf("\n小魏:");
scanf("%s", sendData);
if(strcmp(sendData, "quit") == 0) //退出
break;
if(send(NewConnection, sendData, BUFFSIZE, 0) == SOCKET_ERROR)
{
printf("消息發送失敗!\n");
break;
}
}
//從容關閉
shutdown(NewConnection, SD_BOTH);
//完成新接受的連接後,用closesocket API關閉這些套接字
closesocket(NewConnection);
closesocket(ListeningSocket);
//應用程序完成對接的處理後,調用WSACleanup
if(WSACleanup() == SOCKET_ERROR)
{
printf("WSACLEANUP_ERROR: %d\n", WSAGetLastError());
return 0;
}
system("pause");
return 0;
}
client端
/*
* 客戶端 Client.c
*
*/
#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#define BUFFSIZE 1024
int main(int argc, char**argv)
{
int Ret;
WSADATA wsaData;
SOCKET s;
SOCKADDR_IN ServerAddr;
unsigned short Port = 49911; //端口號,根據用戶實際修改
char sendData[BUFFSIZE];
char recvData[BUFFSIZE];
if((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
{
printf("WSASTARTUP_ERROR: %d\n", Ret);
return 0;
}
if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
printf("SOCKET_ERROR: %d\n", INVALID_SOCKET);
return 0;
}
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(Port);
ServerAddr.sin_addr.s_addr = inet_addr("192.168.43.53"); //具體的IP地址,根據用戶實際修改
//ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.101");// 這裏S_un.S_addr在不同的IDE中可能不一樣,然後IPv4地址使用該程序所運行在的PC上的IPv4地址
if((connect(s, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr))) == SOCKET_ERROR)
{
printf("CONNECT_ERROR: %d\n", SOCKET_ERROR);
closesocket(s);
return 0;
}
//Chat
while(1)
{
printf("\n小民:");
scanf("%s", sendData);
if(strcmp(sendData, "quit") == 0) //quit
break;
if(send(s, sendData, BUFFSIZE, 0) == SOCKET_ERROR)
{
printf("消息發送失敗!\n");
break;
}
Ret = recv(s, recvData, BUFFSIZE, 0);
if(Ret > 0)
printf("小魏: %s\n", recvData);
else if(Ret < 0)
printf("RECV_ERROR: %d\n", SOCKET_ERROR);
else
{
printf("對方退出程序,聊天結束!");
break;
}
}
shutdown(s, SD_BOTH);
closesocket(s);
if(WSACleanup() == SOCKET_ERROR)
{
printf("WSACLEANUP_ERROR: %d\n", WSAGetLastError());
return 0;
}
system("pause");
return 0;
}
6.程序源碼(C++)
其中我標了註釋“//根據用戶實際更改”,則按照上述第二步和第三步提到的方式更改
server端
#include<iostream>
#include<string.h>
#include<winsock2.h>
#pragma comment(lib,"ws2_32.lib")//加載 ws2_32.dll
#define BUFFSIZE 1024
using namespace std;
class Socket
{
private:
SOCKET sock;//套接字
sockaddr_in sockAddr;//特定的IP地址
unsigned short Port;//端口號
public:
Socket();//構造servSock
Socket(int n,Socket servSock);//構造clntSock
void usebind2listen();
void chat();
void close();
//~Socket();
};
Socket::Socket()
{
this->Port=57618; //這裏根據用戶實際更改
this->sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
memset(&(this->sockAddr),0,sizeof(this->sockAddr));
this->sockAddr.sin_family = PF_INET; //使用IPv4地址
this->sockAddr.sin_addr.s_addr = inet_addr("192.168.43.181"); //具體的IP地址,根據用戶實際更改
this->sockAddr.sin_port = htons(Port); //端口
}
Socket::Socket(int n,Socket servSock)
{
this->sock=accept(servSock.sock,(SOCKADDR*)&(this->sockAddr),&n);
//將server的servSock與客戶端的sock相連接(二者地址配置相同),並賦給新的套接字clntSock
cout<<"檢測到一個連接:"<<inet_ntoa(this->sockAddr.sin_addr)<<"端口:"<<ntohs(this->sockAddr.sin_port)<<endl;
}
void Socket::usebind2listen()
{
bind(this->sock,(SOCKADDR*)&sockAddr,sizeof(SOCKADDR));//將servsock與特定地址綁定
listen(this->sock,5);//監聽,主程序運行到此處中斷
cout<<"正在接收連接...";
}
void Socket::chat()
{
int ret;
char sendData[BUFFSIZE];
char recvData[BUFFSIZE];
while(true)
{
//接收數據
ret=recv(this->sock,recvData,BUFFSIZE,0);
if(ret>0)
cout<<"小民:"<<recvData<<endl;
else if(ret<0)
cout<<"recv_error"<<SOCKET_ERROR<<endl;
else
{
cout<<"對方退出程序,聊天結束"<<endl;
break;
}
//發送數據
cout<<endl<<"小魏:";
cin>>sendData;
cin.get();//吃掉回車
if(strcmp(sendData,"quit")==0)
{
break;
}
if(send(this->sock,sendData,BUFFSIZE,0)==SOCKET_ERROR)
{
cout<<"發送失敗";
break;
}
}
shutdown(this->sock, SD_BOTH);
}
void Socket::close()
{
closesocket(this->sock);//關閉套接字
}
int main(int argc,char**argv)
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
Socket servSock;//構造套接字對象 PF_INET:IPv4
servSock.usebind2listen();//servSock與設定的sockAddr綁定
Socket clntSock(sizeof(sockaddr_in),servSock);//重載構造套接字對象,接收客戶端請求
clntSock.chat();//聊天
servSock.close();
clntSock.close();
WSACleanup();//終止 DLL 的使用
system("pause");
return 0;
}
client端
#include<iostream>
#include<string.h>
#include<winsock2.h>
#pragma comment(lib,"ws2_32.lib")//加載 ws2_32.dll
#define BUFFSIZE 1024
using namespace std;
class CSocket
{
private:
SOCKET sock;//套接字
sockaddr_in sockAddr;//特定的IP地址
unsigned short Port;//端口號
public:
CSocket();
void useconnect();
void userecieve(char *str);
void chat();
void close();
//~CSocket();
};
CSocket::CSocket()
{
this->Port=57618; //這裏根據用戶實際更改
this->sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
memset(&(this->sockAddr),0,sizeof(this->sockAddr));
this->sockAddr.sin_family = PF_INET; //使用IPv4地址
this->sockAddr.sin_addr.s_addr = inet_addr("192.168.43.181"); //具體的IP地址,根據用戶實際更改
this->sockAddr.sin_port = htons(Port); //端口
}
void CSocket::useconnect()
{
connect(sock,(SOCKADDR*)&(this->sockAddr),sizeof(SOCKADDR));
//套接字與地址建立連接,與服務器的accept相對應
}
void CSocket::chat()
{
int ret;
char sendData[BUFFSIZE];
char recvData[BUFFSIZE];
while(true)
{
//發送數據
cout<<endl<<"小民:";
cin>>sendData;
cin.get();//吃掉回車
if(strcmp(sendData,"quit")==0)
{
break;
}
if(send(this->sock,sendData,BUFFSIZE,0)==SOCKET_ERROR)
{
cout<<"發送失敗";
break;
}
//接收數據
ret=recv(this->sock,recvData,BUFFSIZE,0);
if(ret>0)
cout<<"小魏:"<<recvData<<endl;
else if(ret<0)
cout<<"recv_error"<<SOCKET_ERROR<<endl;
else
{
cout<<"對方退出程序,聊天結束"<<endl;
break;
}
}
shutdown(this->sock, SD_BOTH);
}
void CSocket::close()
{
closesocket(this->sock);
}
int main(int argc,char**argv)
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
CSocket sock;
sock.useconnect();
sock.chat();
sock.close();
WSACleanup();
system("pause");
return 0;
}