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结构体

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) 


4、获取套接字关联的地址

// 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)


6、连接套接字

int connect(int socket, const struct sockaddr *foreignAddress, socklen_t addressLength)

7、绑定到地址

int bind(int socket, struct sockaddr *localAddress, socklen_t addressSize)


8、处理进入连接
int listen(int socket, int queueLimit)
int accept(int socket, struct sockaddr *clientAddress, socklen_t *addressLength)


9、通信

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应答服务器和客户端的主要内容,如果未能解决你的问题,请参考以下文章

C/S程序的一般流程和基本socket函数

简单socket服务器的基本理解

Quasar Vue Socket.io Laravel-Echo 实现

针对ECHO服务的TCP客户软件的实现( Linux + Socket + C + Code + Annotation)

针对ECHO服务的TCP客户软件的实现( Linux + Socket + C + Code + Annotation)

socket 和 SocketServer 模块