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;
}