TCP的套接字的socket通信

TCP的套接字的socket通信

本篇主要通过实现TCP的套接字通信例程介绍套接字基础知识。

一、概念

套接字:TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点,就叫做套接字(socket)或插口。套接字用(IP地址:端口号)表示。

       生成套接字的三个主要参数:通信的目的IP地址,使用传输层协议(TCP或UDP)和使用的端口。socket原意为插座,通过这三个参数与另一个插座(socket)绑定,应用层和传输层就可以通过套接字接口,区分来自不同应用程序或网络通信,实现数据传输的并发服务。

       任务与任务之间的通信节点。要通过套接字进行通信,至少需要一对套接字,一个运行于客户机端,称之为ClinetSocket,另一个运行于服务器端,称之为ServerSocket。

简单的来说,我们需要套接字这个桥梁来实现客户端和服务器端的通信。

 

二、TCP套接字通信流程

服务器端:

1.创建套接字(socket

2.将套接字绑定到本地地址和端口上(bind

3.将套接字的状态设置为监听状态(listen

4.接受连接请求,并且得到用于通信的套接字(accept

5.使用接受连接请求后得到的套接字进行通信(send/recv

6.通信完毕,释放套接字(close

客户端:

1.创建套接字(socket

2.向服务器端发起连接请求(connect

3.连接成功后,开始通信操作(send/recv

在这里出现的红色字体则是我们需要使用的函数。

 

三、相关实现函数

1.socket函数

函数原型函数介绍
int socket(int domain, int type, int protocol);创建一个套接字,成功返回一个套接字,失败返回-1
domain:域名,man手册上有具体介绍,这里我们是基于TCP进行通信,所以选择AF_INET
type:表示套接字类型,选择流式套接字,SOCK_STREAM
protocol:套接口所用协议

2.bind函数

函数原型函数介绍
int bind(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);将套接字绑定到指定端口和地址上,成功返回0,失败返回-1
sockfd:套接字
addr:关于绑定端口的具体信息
addrlen:addr的长度

可在 /usr/include/netinet/in.h文件中查到该结构体的介绍

struct sockaddr_in
{
    __uint8_t        sin_len;        //表示该结构体的长度
    sa_family_t      sin_family;     //地址家族,也就是域名
    in_port_t        sin_port;       //端口号
    struct in_addr   sin_addr;       //地址号
    char             sin_zero[8];                    
}

3.listen函数

函数原型函数介绍
int listen(int sockfd, int backlog);将套接字设置为监听状态,成功返回0,失败返回-1
sockfd:套接字
backlog:表示等待连接的最大队列长度。代表该端口同时最多可连接多少个客户端,设置为5,则最多同时连接5个客户端

4.accept函数

函数原型函数介绍
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);接受客户端的连接请求,成功返回一个新的套接字,失败返回-1
sockfd:套接字
addr:接受连接端的地址信息
addrlen:addr结构体指针的长度
若addr 和 addrlen都选择0的话,则代表选择了套接字绑定的端口

5.close函数

函数原型函数介绍
int close(int fd);关闭套接字
sockfd:套接字

6.connect函数

函数原型函数介绍
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);通过套接字发送数据,成功返回0,失败返回-1
sockfd:套接字
addr:连接服务端的地址信息
addrlen:addr结构体指针的长度

7.send函数

函数原型函数介绍
ssize_t send(int sockfd, const void *buf, size_t len, int flags);往套接字中发送数据,成功返回发送的数据个数,失败返回-1
sockfd:套接字
buf:被发送数据的缓冲区
len:发送数据的长度
flags:函数的调用方式,一般填0,具体信息查看man手册

8.recv函数

函数原型函数介绍
ssize_t recv(int sockfd, void *buf, size_t len, int flags);从套接字中接收数据,成功返回接收到的数据个数,失败返回-1
sockfd:套接字
buf:接收数据的缓冲区
len:接收到数据的长度
flags:函数的调用方式,一般填0,具体信息查看man手册

 

四、具体实现代码

服务端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>


int main(int argc, const char *argv[])
{
	int sockfd = 0;
	int acceptfd = 0;
	struct sockaddr_in server_addr;                     //结构体定义在 netinetin.h  当中
	char str[200] = {'\0'};

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) //Socket operation on non-socket
	{
		perror("套接字创建失败");
		return -1;
	}

	printf("套接字创建成功;\n");
	memset(&server_addr, 0, sizeof(server_addr));

	
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));

	
	if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
	{
		perror("套接字绑定失败 ");
		return -1;
	}
	printf("套接字绑定成功\n");

	if (listen(sockfd, 5) < 0)
	{
		perror("套接字监听失败 ");
		return -1;
	}
	
	printf("套接字监听成功\n");

	if ((acceptfd = accept(sockfd, 0, 0)) < 0)
	{
		printf("连接到发送端;\n");
	}

	while(1)
	{

		if (recv(acceptfd, str, 200, 0) < 0)
		{
			perror("fail to recv ");
			close(acceptfd);
			return -1;
		}

		else 
		{
			printf("接收到数据: %s;\n", str);

			if (send(acceptfd, str, 200, 0) < 0)
			{
				printf("数据发送失败;\n");
			}
			else
			{
				printf("数据发送成功: %s \n", str);
			}
		}
		
	}

	return 0;
}

用户端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>


int main(int argc, const char *argv[])
{
	int sockfd = 0;
	int acceptfd = 0;
	struct sockaddr_in server_addr;                     //结构体定义在 netinetin.h  当中
	char str[200] = {'\0'};

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) //Socket operation on non-socket
	{
		perror("套接字创建失败");
		return -1;
	}

	memset(&server_addr, 0, sizeof(server_addr));

	printf("套接字创建成功;\n");
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));

	if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
	{
		perror("连接失败 ");
		return -1;
	}
	
	printf("连接到接收端\n");
	while(1)
	{

		printf("请输入需要发送的数据: ");
		scanf("%s", str);

		send(sockfd, str, 200, 0);

		if (recv(sockfd, str, 200, 0) < 0 )
		{
			printf("接收数据错误;\n");
		}
		else
		{
			printf("接收到数据:%s ;\n", str);
		}

	}

	return 0;
}

在程序中的注释中有一句,Socket operation on non-socket,这个是没注意赋值运算和比较运算导致编译程序报错的错误信息,假如成程序改成

if (sockfd = socket(AF_INET, SOCK_STREAM, 0) < 0) //Socket operation on non-socket

此时编译程序则会报错Socket operation on non-socket

在运行程序之前,我们可以使用:netstat  -tnlp命令获取本地地址。

此处的127.0.0.1 即是地址号。

下面运行程序,我们看看结果。

在这里我们需要注意的是,我们需要先运行服务端的程序,在上面的图片我们能清楚的看到用户端和服务端连接成功了。

在这里,我们则可以看到,客户端发送的数据,服务端能正常接收并且返回,服务端发送的数据,接收端也能正常返回。

程序里面还有个别函数没介绍,这里我们简单的说一下,

atoi(),将字符串转换成整型数

htons()作用是将端口号有主机字节序转换为网络字节序的整数值。(host  to net)

       inet_addr()将点分十进制的字符串转换为网络地址。得到一个二进制数据。

 

 仓促成文,不当之处,尚祈方家和读者批评指正。联系邮箱1772348223@qq.com

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCP编程是一种网络编程技术,用于在计算机网络中建立TCP并进行数据传输。下面是一个简单的示例,展示了如何使用Python的socket库进行TCP编程: ```python import socket # 创建一个TCP sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 绑定套到特定的IP地址和端口号 server_address = ('localhost', 8000) sock.bind(server_address) # 监听连请求 sock.listen(1) while True: print('等待连...') # 受客户端的连请求 client_sock, client_address = sock.accept() try: print('受来自', client_address, '的连') # 收客户端发送的数据 data = client_sock.recv(1024) print('收到的数据:', data.decode()) # 向客户端发送响应数据 response = 'Hello, client!' client_sock.sendall(response.encode()) finally: # 关闭客户端套 client_sock.close() ``` 上述示例代码中,首先创建了一个TCP,并通过bind()方法绑定到一个特定的IP地址和端口号。然后使用listen()方法开始监听连请求。下来使用accept()方法受客户端的连请求,并创建一个新的套用于与客户端通信。 在循环中,可以通过recv()方法收客户端发送的数据,并使用sendall()方法向客户端发送响应数据。最后使用close()方法关闭客户端套。 这只是一个简单的示例,TCP编程还可以实现更复杂的功能,如多线程处理多个客户端连、错误处理等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值