Socket: TCP echo Server

玩socket第一篇,简单的TCP echo服务器。

Socket

socket在我的认识中就是用于不同进程间的通讯,甚至包括了不同主机上的不同进程,通讯的两端就是两个socket。

根据Linux下“一切皆文件”的原则,用socket进行通讯就是对创建并连接好的socket进行读(read)写(write)操作,客户端对连接到服务器的socket进行write就是发送信息,进行read就是接收信息;服务器则是对连接到客户端的socket进行读写操作。

TCP和UDP

通过socket传输信息的方式主要有两种:TCP和UDP。

两者相比,TCP的可靠性明显比UDP要高,但UDP的传输速度则要比TCP高很多。在网络状况比较差时,TCP的可靠连接难以建立,反而不如使用UDP不停地发送信息效果好。TCP的速度虽然难以超过UDP,但在单次传输的数据量越来越大时,TCP的速度也会越来越接近UDP。

步骤

在使用C语言中的socket进行通信时,服务器进行需要以下几步:

而客户端则需要:

代码

使用时直接使用g++编译,先运行server,再运行client,然后在client中输入内容发送后,就可以接收到server返回的信息,当在client中发送“end”后结束server和client。

服务端:

/*************************************************************************
    > File Name: server.cpp
    > Author: nian
    > Blog: https://whoisnian.com
    > Mail: [email protected]
    > Created Time: 2017年08月23日 星期三 22时29分46秒
 ************************************************************************/
#include<cstdio>
#include<cstring>

//引入头文件
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>

//设置服务器监听端口号
const unsigned long port = 7637;

int main(void)
{
	//创建服务器端socket,地址族为AF_INET(IPv4),传输方式为TCP
	int server_socket;
	server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//初始化IP为本地127.0.0.1,端口为已设置的port
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	//绑定socket与IP和端口
	bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));

	//使socket进入监听模式
	listen(server_socket, 10);

	//等待接收客户端请求
	int client_socket;
	struct sockaddr_in client_addr;
	socklen_t client_addr_len;
	client_addr_len = sizeof(client_addr);
	client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);

	//一直获取内容,直到获取到的内容为“end”时停止
	int flag = 1;
	while(flag)
	{
		char buf[1000];
		read(client_socket, buf, sizeof(buf));

		//如果内容是“end”就停止,否则返回给客户端输入的内容
		if(buf[0] == 'e'&&buf[1] == 'n'&&buf[2] == 'd'&&buf[3] == '\n')
			flag = 0;
		else
		{
			write(client_socket, buf, sizeof(buf));
		}
	}
	close(client_socket);
	close(server_socket);
	return 0;
}

客户端:

/*************************************************************************
    > File Name: client.cpp
    > Author: nian
    > Blog: https://whoisnian.com
    > Mail: [email protected]
    > Created Time: 2017年08月23日 星期三 23时39分54秒
 ************************************************************************/
#include<cstdio>
#include<cstring>

//引入头文件
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>

//设置服务器监听端口号
const unsigned long port = 7637;

int main(void)
{
	//创建服务器socket,地址族为AF_INET(IPv4),传输方式为TCP
	int server_socket;
	server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//初始化IP为服务器127.0.0.1,端口为已设置的port
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	//客户端连接服务器
	connect(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));

	//一直发送请求,直到发送的内容为“end”时停止
	int flag = 1;
	while(flag)
	{
		//发送内容
		char buf[1000];
		memset(buf, 0, sizeof(buf));
		printf("put: ");
		fgets(buf, sizeof(buf)-1, stdin);
		write(server_socket, buf, sizeof(buf));

		//如果内容是“end”就停止,否则读取并输出服务器返回的内容
		if(buf[0] == 'e'&&buf[1] == 'n'&&buf[2] == 'd'&&buf[3] == '\n')
			flag = 0;
		else
		{
			read(server_socket, buf, sizeof(buf));
			printf("get: %s\n", buf);
		}
	}
	close(server_socket);
	return 0;
}

函数

注意

  struct sockaddr_in{
    sa_family_t     sin_family;   //地址族,即地址类型,与创建套接字时保持一致
    uint16_t        sin_port;     //16位的端口号,要用htons()转换
    struct in_addr  sin_addr;     //32位IP地址
    char            sin_zero[8];  //不使用,一般用0填充
  };

  struct in_addr{
    in_addr_t  s_addr;  //32位的IP地址,要用inet_addr()将字符串类型的IP地址转换为数字
  };

  struct sockaddr{
    sa_family_t  sin_family;   //地址族(Address Family),也就是地址类型
    char         sa_data[14];  //IP地址和端口号
  };