Socket初体验 C/S模式开发模型

初步理解Socket

Socket 也叫 套接字

网络编程中 如果有两个进程需要进行网络通信

两个进程各获得一个 Socket 来进行标识

我们通过 IP+端口号 唯一确定一台主机上需要通信的一个进程

每一组 IP+端口号 拥有一个Socket

每两组 IP+端口号 也就是两个Socket 形成一个 Socket Pair

一个 Socket Pair 唯一标识一个连接 也就意味着两个Socket 一定成对出现

Socket 也可以理解为一种文件 

相较于本地磁盘的本地读写

Socket 是系统内核借助缓冲区形成的特殊文件

可以在不同主机之间进行网络读写 实现数据传递

一个Socket由 发送缓冲区接收缓冲区 组成

一端的发送缓冲区对应另一端的接收缓冲区

当主机A给主机B发送信息时

A主机的数据会写入B主机的“缓冲区文件” 也就是B主机上的一个Socket

Socket在建立连接的两个主机中分别有两个文件描述符(int类型)

一个主机上 拥有Socket的进程 会从Socket文件中读取来自另一个主机的数据

客户端服务端的运行效果

Linux客户端开发代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<iostream>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 666
using namespace std;

//客户端开发
int main(int argc, char *argv[])
{
	//echo_client "This is a test."
	//argc == 2
	//argv[0] 和 argv[1] 分别指向 echo_cilent 和 "This is a test."
	int sockfd;
	char *message;

    //服务器地址标签
    //sockaddr_in 是用于IPV4通信的结构体
    //这个结构体里存了协议设定 IP地址 端口号等信息
	struct sockaddr_in server_addr;
	int len;
	char buf[256];
	if(argc != 2)
	{
		fputs("Usage: ./echo_client message \n", stderr);
		exit(1);
	}

	message = argv[1];
	printf("message:%s\n", message);

	//客户端连接服务端 TCP协议的固定写法
    //AF_INET 和 SOCK_STREAM 是TCP协议的特有参数
	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	//设置服务器地址标签
	memset(&server_addr, 0, sizeof(struct sockaddr_in));
	server_addr.sin_family = AF_INET;//设置协议

	//用到一些调整字节序的函数
    //inet_pton 将"x.x.x.x"字符串形式的IP地址转化为4字节整形
    //inet_pton 自动转化为大端字节序(网络上的字节序)的4字节整形
	inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    //htons 本地字节序变网络通信的大端字节序(host to net)
    //htons 最后的 s 指的是短整型 对应两个字节 这里是对端口号的转换
	server_addr.sin_port = htons(SERVER_PORT);

	//建立C/S连接
    //第一个参数是socket的文件描述符
    //第二个参数是结构体地址 因历史兼容原因要强制类型转换
    //connect函数封装了填写客户端IP地址以及端口号信息的功能
	connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

	//往服务端写入数据
	write(sockfd, message, strlen(message));
	len = read(sockfd, buf, sizeof(buf)-1);
	if(len>0)
	{
		//成功从服务器接收到数据
		buf[len] = '\0';
		printf("receive:%s\n", buf);	
	}	
	else
	{
		perror("error");
	}

	printf("finished.\n");

	close(sockfd);//关闭socket	
	
	return 0; 	
}

Linux服务端开发代码

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<iostream>
#define SERVER_PORT 666
using namespace std;

//服务端开发
int main(void)
{
	//准备信箱 sock变量待会接收文件描述符
	int sock;

    //sockaddr_in 是IPV4协议下存储通信地址的结构体
	struct sockaddr_in server_addr;

	//创建信箱 获取文件描述符
	sock = socket(AF_INET, SOCK_STREAM, 0);

	//初始化结构体标签 写上地址和端口号
	bzero(&server_addr,sizeof(server_addr));

    //指定协议家族IPV4
	server_addr.sin_family = AF_INET;

    //htons 是 host to net short 
    //转换为大端字节序 并且支持短整型 端口号就是两个字节
    //一个服务器可能有多个网卡多个IP
    //INADDR_ANY 这个宏定义量表示监听所有IP
	server_addr.sin_addr.s_addr = htons(INADDR_ANY);
	server_addr.sin_port = htons(SERVER_PORT);

	//用结构体标签初始化信箱
    //考虑历史兼容性 强制类型转换(struct sockaddr *)
	bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));

	//启动两条连接队列
    //队列中有已完成TCP连接的和未完成TCP连接的
    //第二个参数限制总共最多128个请求
	listen(sock, 128);

	//等待客户端连接
	cout<<"等待客户端连接"<<endl;

	while(1)
	{
		//客户端的标签 寄信者的信息
		struct sockaddr_in client;
		socklen_t client_addr_len;
		client_addr_len = sizeof(client);
		char client_ip[64];
		char buf[256];

		//创建一个用于数据交换的的socket
        //accept函数从已完成TCP连接的队列中获取连接并开始通信
        //accept函数返回一个新的socket 有新的文件描述符
        //注意 这个新的socket 和 之前用于连接的socket 不是一个文件描述符
		int client_sock = accept(sock, (struct sockaddr*)&client, &client_addr_len);

		//打印客户端信息
        //inet_ntop 讲4字节整形数转化为"x.x.x.x"类型的IP地址
        //ntohs net to host short 从网络字节序转为主机字节序
        //short 对应占据2字节大小的整形数据 用于表示端口
		printf("client ip:%s\tport:%d\n",
			inet_ntop(AF_INET,&client.sin_addr.s_addr,
			client_ip,sizeof(client_ip)),ntohs(client.sin_port));

		//读入客户端发送的数据
		int len = read(client_sock, buf, sizeof(buf)-1);//最大读取到255个字符 留一个给\0
		buf[len] = '\0';//要手动加上字符串结束符
		printf("receive len:%d\tbuf:%s\n", len, buf);	

		//写回客户端
		len = write(client_sock, buf, len);
		printf("write finished. len:%d\n", len);

		close(client_sock);//关闭socket
        close(sock);//两个socket都应该关闭
	}

	return 0;
}

支持并发客户端通信的版本在这里!!! 

本篇CSDN 同一时刻仅支持一个客户端使用服务器

如果希望在同一时刻有多个客户端使用服务器 看下面链接

多进程实现并发服务器通信_没伞的男孩的博客-CSDN博客

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没伞的男孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值