socket实现简单的echo应答服务器和客户端
Posted 小喽啰A
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket实现简单的echo应答服务器和客户端相关的知识,希望对你有一定的参考价值。
</pre></h6><h6><strong>实现思路</strong></h6><p><span style="white-space:pre"></span>1、使用socket()创建tcp套接字。</p><p><span style="white-space:pre"></span>2、利用bind()给套接字分配端口号。</p><p><span style="white-space:pre"></span>3、使用listen()告诉系统允许对该端口建立连接。</p><p><span style="white-space:pre"></span>4、调用accept()为每个客户连接获取新的套接字。</p><p><span style="white-space:pre"></span>5、使用send()和recv()通过新的套接字与客户端通信。</p><p><span style="white-space:pre"></span>6、使用close()关闭客户连接。</p><p></p><h6>函数和结构体说明:</h6><p><span style="white-space:pre"></span>1、主机字节序和网络字节序的转换</p><p><span style="white-space:pre"><span style="white-space:pre"></span>函数名中,h代表host,就是主机;n代表net 就是网络;l代表long是32位整数;s代表short是16位整数。</span></p><pre name="code" class="cpp">#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
2、socket结构体
</pre></h6><h6><strong>实现思路</strong></h6><p><span style="white-space:pre"></span>1、使用socket()创建tcp套接字。</p><p><span style="white-space:pre"></span>2、利用bind()给套接字分配端口号。</p><p><span style="white-space:pre"></span>3、使用listen()告诉系统允许对该端口建立连接。</p><p><span style="white-space:pre"></span>4、调用accept()为每个客户连接获取新的套接字。</p><p><span style="white-space:pre"></span>5、使用send()和recv()通过新的套接字与客户端通信。</p><p><span style="white-space:pre"></span>6、使用close()关闭客户连接。</p><p></p><h6>函数和结构体说明:</h6><p><span style="white-space:pre"></span>1、主机字节序和网络字节序的转换</p><p><span style="white-space:pre"><span style="white-space:pre"></span>函数名中,h代表host,就是主机;n代表net 就是网络;l代表long是32位整数;s代表short是16位整数。</span></p><pre name="code" class="cpp">#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
IPv4和IPv6的地址格式定义在 netinet.h 中。在IPv4地址中使用sockaddr_in结构体,包括16位的端口号和32位的IP地址。IPv6地址使用sockaddr_in6结构体,包括16位的端口号和128位的ip地址和一些控制字段。
地址结构的第一部分定义了地址族——地址属于的空间。AF_INET和AF_INET6分别代表IPv6和IPv4的Internet地址族。
sockaddr 定义了一种泛型的数据类型,用于指定与套接字关联的地址。
struct sockaddr
sa_family_t sa_family;
char sa_data[14];
;
struct in_addr
uint32_t s_addr;
;
struct sockaddr_in
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
struct in6_addr
uint32_t s_addr[16];
;
struct sockaddr_in6
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
;
3、二进制/字符串地址转换
// pton = printable to numeric
// addressFamily: 要转换地址的地址族
// src:转换的地址,字符串以null终止
// des:存放结果的地址空间
// 转换成功,返回1;未指定地址族,返回-1;des未格式化成指定类型,返回0。
int inet_pton(int addressFamily, const char *src, void *dst)
// addressFamily:要转换的地址类型。
// src:指向包含要转换的数字地址的内存块
// des:指向在调用这的空间中分配的缓冲区
const char *inet_ntop(int addressFamily, const void *src, char *dst, socklen_t desBytes)
// socket:是我们想要获得的地址信息的套接字描述符
// remoteAddress和localAddress :指向把地址信息存放在其中的地址结构
int getpeername(int socket, struct sockaddr *remoteAddress, socklen_t *addressLength)
int getsockname(int socket, struct sockaddr *localAddress, socklen_t *addressLength)
5、创建销毁套接字
// domain:通信领域,IPv4(AF_INET), IPv6(AF_INET6)
// type:SOCK_STREAM表示利用可靠的自截留语义指定一个套接字。SOCK_DGRAM则指定一种"尽力而为"的数据报套接字
// protocal:指定要使用的指定的端到端协议;TCP(IPPROTO_TCP)、UCP(IPPROTO_UCP)
// 返回:-1表示失败;
int socket(int domain, int type, int protocal)
// 释放套接字。
// 返回:0代表成功,-1表示失败
int close(int socket)
int connect(int socket, const struct sockaddr *foreignAddress, socklen_t addressLength)
7、绑定到地址
int bind(int socket, struct sockaddr *localAddress, socklen_t addressSize)
int listen(int socket, int queueLimit)
int accept(int socket, struct sockaddr *clientAddress, socklen_t *addressLength)
ssize_t send(int socket, const void *msg, size_t msgLength, int flags)
ssize_t recv(int socket, void *rcvBuffer, size_t bufferLength, int flags)
echo服务器代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
static const int MAXPENDING = 5;
static const int BUFSIZE = 1024;
void HandleTCPClient(int clntSocket);
void DieWithUserMessage(const char *msg, const char *detail);
void DieWithSystemMessage(const char *msg);
int main(int argc, char *argv[])
if(argc != 2)
DieWithUserMessage("Parameter(s)", "<Server Port>");
in_port_t servPort = atoi(argv[1]);
// Create socket for incoming connections
int servSock;
if((servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 )
DieWithSystemMessage("Socket() failed");
// Construct local address structure
struct sockaddr_in servAddr; // Local address
memset(&servAddr, 0, sizeof(servAddr)); // Zero out structure
servAddr.sin_family = AF_INET; // IPv4 address family
servAddr.sin_addr.s_addr = htonl(INADDR_ANY); // Any incoming interface.
servAddr.sin_port = htons(servPort);
//Bind to the local address
if (bind(servSock, (struct sockaddr*) &servAddr, sizeof(servAddr)) < 0 )
DieWithSystemMessage("bind() failed");
// Mark the socket so it will listen for incoming connections
if (listen(servSock, MAXPENDING) < 0 )
DieWithSystemMessage("listen() failed");
for (;;) // Run forever
struct sockaddr_in clntAddr; // Client address
// Set length of client address structure (in-out parameter)
socklen_t clntAddrLen = sizeof(clntAddr);
// Wait for a client to connect
int clntSock = accept(servSock, (struct sockaddr*) &clntAddr, &clntAddrLen);
if ( clntSock < 0 )
DieWithSystemMessage("accept() failed");
// clntSock is connected to a client!
char clntName[INET_ADDRSTRLEN]; // String to contain client address
if (inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName, sizeof(clntName)) != NULL )
printf("Handling client %s/%d\\n", clntName, ntohs(clntAddr.sin_port));
else
puts("Unable to get client address");
HandleTCPClient(clntSock);
void HandleTCPClient(int clntSocket)
char buffer[BUFSIZE]; // Buffer for echo string
// Receive message from client.
ssize_t numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0);
if ( numBytesRcvd < 0 )
DieWithSystemMessage("recv() failed");
// Send received string and receive again until end of stream.
while ( numBytesRcvd > 0 ) //0 indicates end of stream.
// Echo message back to client.
ssize_t numBytesSent = send(clntSocket, buffer, numBytesRcvd, 0);
if ( numBytesSent < 0 )
DieWithSystemMessage("send() failed");
else if ( numBytesSent != numBytesRcvd )
DieWithUserMessage("send()", "send unexpected number of bytes");
// See if there is more data to receive.
numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0);
if ( numBytesRcvd < 0 )
DieWithSystemMessage("recv() failed.");
close(clntSocket); // Close client socket.
void DieWithUserMessage(const char *msg, const char *detail)
fputs(msg, stderr);
fputs(": ", stderr);
fputs(detail, stderr);
fputc('\\n', stderr);
exit(1);
void DieWithSystemMessage(const char *msg)
perror(msg);
exit(1);
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> // IPPROTO_TCP
#include "DieMessage.h"
int main(int argc, char **argv)
if ( argc < 3)
DieWithUserMessage("Parameter(s)", "<IP><PORT>[<MESSAGE>]");
// Create local socket
int sock;
if ( ( sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) < 0 )
DieWithSystemMessage("socket() failed.");
int servPort = atoi(argv[2]);
// Create server address
struct sockaddr_in servAddr;
memset( &servAddr, 0, sizeof(servAddr) );
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(servPort);
inet_pton(AF_INET, argv[1], &servAddr.sin_addr.s_addr);
// Connect to server
if ( (connect( sock, (struct sockaddr *) &servAddr, sizeof(servAddr))) < 0 )
DieWithSystemMessage("connect() failed.");
// Send Message to server
ssize_t sentByteLen = send( sock, argv[3], sizeof(argv[3]), 0);
if ( sentByteLen > 0 )
printf("send: %s\\tbytes:%d\\n", argv[3], sentByteLen);
else
DieWithSystemMessage("send() failed");
// Receive Message from server
char recvBuf[sentByteLen];
ssize_t recvByteLen = recv( sock, recvBuf, sentByteLen, 0);
if ( recvByteLen > 0 )
printf("recv: %s\\tbytes:%d\\n", recvBuf, recvByteLen);
else
DieWithSystemMessage("recv() failed");
close(sock);
printf("end!\\n");
以上是关于socket实现简单的echo应答服务器和客户端的主要内容,如果未能解决你的问题,请参考以下文章
Quasar Vue Socket.io Laravel-Echo 实现
针对ECHO服务的TCP客户软件的实现( Linux + Socket + C + Code + Annotation)
针对ECHO服务的TCP客户软件的实现( Linux + Socket + C + Code + Annotation)