WinSock2 客户端/服务器通信:发送和接收字符串
WinSock2 client/server communication: send & receive strings
我有一点困难试图编码这个,因为我不知道很多。我有两台可以互相通信的电脑。它可以正常工作,但它只能互相发送单个字符。如果命令执行时没有IP地址参数,那么一台PC就像服务器一样,而另一台如果给定服务器IP地址,就像客户端一样连接到服务器。
代码都在这里:
// Quick and dirty - error checks omitted for brevity.
#include <iostream>
#include <string>
#include <conio.h>
#include <WinSock2.h>
#include <ws2tcpip.h>
using namespace std;
void chat (int socket_d)
{
while (true)
{
if (_kbhit ())
{
char ch;
ch = _getche();
int n;
n = send (socket_d, &ch, 1, 0);
if (ch == 'r')
{
cout << "n";
}
}
int n;
int count = 0;
char byte; // Read one byte at a time - is this efficient?
n = recv (socket_d, &byte, 1, 0);
if (n <= 0)
{
if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking.
{
cout << "Terminated " << WSAGetLastError() << "n";
return;
}
}
else
{
cout << (char)byte;
if ((char) byte == 'r')
cout << "n";
}
}
}
int main (int argc, char * argv [])
{
// Messy process with windows networking - "start" the networking API.
WSADATA wsaData;
int result = WSAStartup (MAKEWORD (2, 2), &wsaData);
unsigned short port = 25565;
// If argument is IP address - be a client and connect to it. Otherwise
// be a server.
if (argc > 1)
{
int socket_d;
socket_d = socket (AF_INET, SOCK_STREAM, 0);
// be a client.
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr (argv[1]); // Parse the string and create the 32 bit address.
server_addr.sin_port = htons (port); // Watch out for the endian conversion!
connect (socket_d, (sockaddr *) &server_addr, sizeof (server_addr));
u_long iMode=1;
ioctlsocket (socket_d, FIONBIO, &iMode); // put the socket into non-blocking mode.
chat (socket_d);
closesocket (socket_d);
}
else
{
// be a server
int listen_socket_d;
int connection_socket_d;
listen_socket_d = socket (AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // A placeholder that will be replaced with my own address.
server_addr.sin_port = htons (port); // Watch out for the endian conversion!
bind (listen_socket_d, (struct sockaddr *) &server_addr, sizeof (server_addr));
int backlog = 5;
listen (listen_socket_d, backlog);
// take only the first connection.
struct sockaddr_storage their_addr;
int their_addr_size = sizeof(their_addr);
connection_socket_d = accept (listen_socket_d, (struct sockaddr *) &their_addr, &their_addr_size);
u_long iMode=1;
ioctlsocket (connection_socket_d, FIONBIO, &iMode); // put the socket into non-blocking mode.
chat (connection_socket_d);
closesocket (connection_socket_d);
}
return 0;
}
我想要实现的是能够发送字符串而不是单个字符。我希望这个工作的方式是增加它发送的字节大小,而不是目前的单个字节。我假设它可以工作的方式,让我们假设一次发送和接收的总大小为64字节。
从这里并不清楚您的问题所在。
我的解决方案如下。基本思想是分配大小相等的发送和接收缓冲区,在键盘输入时向发送缓冲区添加字符,并在缓冲区满了或用户按回车键时传输缓冲区。
非阻塞send
函数可能不会一次发送整个缓冲区(参见文档)。我决定继续在这里阻塞,直到整个缓冲区被发送,这样我就不必跟踪单独的输入和传输缓冲区。我相信你可以在这方面做得更好。
接收部分只是回显接收到的任何字节。一般来说,没有办法知道其他用户是否"完成"发送数据,所以我们只打印第一次调用recv
后我们收到的数据。
我总是在每次发送和接收后将缓冲区memset
为所有零,以防止丢失null终止符的奇怪行为。简单地在当前字符串的末尾附加一个空字符会更优化,但我有时会偏执。
下面是我的代码:
#define BUFFER_SIZE 64
void chat (int socket_d)
{
char sendBuffer[BUFFER_SIZE] = { 0 };
char receiveBuffer[BUFFER_SIZE] = { 0 };
int bufferPosition = 0;
int charsSent = 0;
int charsReceived = 0;
while (true)
{
if (_kbhit ())
{
sendBuffer[bufferPosition++] = _getche();
if (sendBuffer[bufferPosition - 1] == 'r' || bufferPosition == BUFFER_SIZE - 1)
{
// This defeats the purpose of a non-blocking socket, I know.
// You can do better by keeping separate buffers for sending and
// collecting input.
while (charsSent < bufferPosition)
{
charsSent += send(
socket_d,
&sendBuffer[charsSent], // Treats the address of a character as a string.
bufferPosition - charsSent, // Only send the part that hasn't been sent yet.
0);
}
memset(sendBuffer, 0, bufferPosition); // Paranoid.
bufferPosition = charsSent = 0;
cout << "n";
}
}
charsReceived = recv (socket_d, receiveBuffer, BUFFER_SIZE, 0);
if (charsReceived <= 0)
{
if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking.
{
cout << "Terminated " << WSAGetLastError() << "n";
return;
}
}
else
{
cout << receiveBuffer;
if (receiveBuffer[charsReceived - 1] == 'r')
cout << "n";
memset(receiveBuffer, 0, charsReceived); // Super paranoid.
}
}
}
现在,在它支持UTF-8或至少wchar_t
之前,我不会真正认为这是"好的"。ASCII缺少很多人们期望能够在真正的聊天应用程序中使用的字符。
PS -根据Visual Studio 2013, inet_addr
函数已弃用。我用inet_ptons
代替。下面是Winsock实现的文档
相关文章:
- "unknown ca"自生成的 CA、证书和客户端/服务器
- 如何将函数集合传递给客户端类,以便将它们当作客户端类本身的成员使用
- 使用调试/崩溃报告将应用程序部署到客户端
- 如何在本地机器上运行c++和javascript客户端代码(hackerbank风格)
- 如何通过套接字将文本文件的内容从服务器发送到客户端
- 从服务器传输到客户端的消息不会出现
- OpenSSL TLS服务器-使用客户端证书白名单
- 如何使用c++在winsock中从客户端向服务器发送大字符串
- 服务器仅从客户端接收null字符串
- 为什么我的android客户端无法接收到来自winsock c++服务器的字符串消息
- 将未签名的字符串从本机客户端模块发送到浏览器
- 从Poco http客户端获取对字符串的响应
- 如何将可变长度字符串从 RPC 服务器传递到客户端
- C 服务器和 Java 客户端.如何读取完整字符串
- 由于PROTOBUF服务器中的字符串数据类型变量和客户端通过cpp中recv端的套接字进行通信,因此正在获取segfau
- 客户端到服务器发送字符串消息c++
- 客户端接收函数-字节到字符串
- WinSock2 客户端/服务器通信:发送和接收字符串
- 接收到的CSocket客户端-服务器字符串是中文字符
- 处理字符串的正确方法是什么:Java客户端和C++服务器