当前位置: 首页 > 编程笔记 >

如何基于C语言socket编程实现TCP通信

杨凌
2023-03-14
本文向大家介绍如何基于C语言socket编程实现TCP通信,包括了如何基于C语言socket编程实现TCP通信的使用技巧和注意事项,需要的朋友参考一下

TCP/IP协议(Transmission Control Protocol/Internet Protocol)叫做传输控制/网际协议,又叫网络通信协议。实际上,它包含上百个功能的协议,如ICMP(互联网控制信息协议)、FTP(文件传输协议)、UDP(用户数据包协议)、ARP(地址解析协议)等。TCP负责发现传输的问题,一旦有问题就会发出重传信号,直到所有数据安全正确的传输到目的地。

套接字(socket):在网络中用来描述计算机中不同程序与其他计算机程序的通信方式。socket其实是一种特殊的IO借口,也是一种文件描述符。

套接字分为三类:

流式socket(SOCK_STREAM):流式套接字提供可靠、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性。

数据报socket(SOCK_DGRAM):数据报套接字定义了一种无连接的服务,数据通过相互独立的保温进行传输,是无序的,并且不保证是可靠、无差错的。它使用的数据报协议是UDP。

原始socket:原始套接字允许对底层协议如IP或ICMP进行直接访问,它功能强大但使用复杂,主要用于一些协议的开发。

套接字由三个参数构成:IP地址,端口号,传输层协议。

这三个参数用以区分不同应用程序进程间的网络通信与连接。

套接字的数据结构:C语言进行套接字编程时,常会使用到sockaddr数据类型和sockaddr_in数据类型,用于保存套接字信息。

两种结构体分别表示如下:

struct sockaddr
{
 //地址族,2字节
 unsigned short sa_family;
 //存放地址和端口,14字节
 char sa_data[14];
}
 
struct sockaddr_in
{
 //地址族
 short int sin_family;
 //端口号(使用网络字节序)
 unsigned short int sin_port;
 //地址
 struct in_addr sin_addr;
 //8字节数组,全为0,该字节数组的作用只是为了让两种数据结构大小相同而保留的空字节
 unsigned char sin_zero[8]
}

对于sockaddr,大部分的情况下只是用于bind,connect,recvfrom,sendto等函数的参数,指明地址信息,在一般编程中,并不对此结构体直接操作。而是用sockaddr_in来代替。

两种数据结构中,地址族都占2个字节,常见的地址族有:AF_INET,AF_INET6,AF_LOCAL。

这里要注意字节序的问题,最好使用以下函数来对端口和地址进行处理:

uint16_t htons(uint16_t host16bit) uint32_t htonl(uint32_t host32bit)
uint16_t ntohs(uint16_t net16bit) uint32_t ntohs(uint32_t net32bit)

将主机字节序改成网络字节序。

使用socket进行TCP通信时,经常使用的函数有:

下面是TCP通信的demo:

/*socket tcp服务器端*/
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
 
#define SERVER_PORT 5555
 
/*
 监听后,一直处于accept阻塞状态,
 直到有客户端连接,
 当客户端如数quit后,断开与客户端的连接
 */
 
int main()
{
 //调用socket函数返回的文件描述符
	int serverSocket;
 //声明两个套接字sockaddr_in结构体变量,分别表示客户端和服务器
	struct sockaddr_in server_addr;
	struct sockaddr_in clientAddr;
	int addr_len = sizeof(clientAddr);
	int client;
	char buffer[200];
	int iDataNum;
 
 //socket函数,失败返回-1
 //int socket(int domain, int type, int protocol);
 //第一个参数表示使用的地址类型,一般都是ipv4,AF_INET
 //第二个参数表示套接字类型:tcp:面向连接的稳定数据传输SOCK_STREAM
 //第三个参数设置为0
	if((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("socket");
		return 1;
	}
 
	bzero(&server_addr, sizeof(server_addr));
 //初始化服务器端的套接字,并用htons和htonl将端口和地址转成网络字节序
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(SERVER_PORT);
 //ip可是是本服务器的ip,也可以用宏INADDR_ANY代替,代表0.0.0.0,表明所有地址
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 //对于bind,accept之类的函数,里面套接字参数都是需要强制转换成(struct sockaddr *)
 //bind三个参数:服务器端的套接字的文件描述符,
 if(bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
	{
		perror("connect");
		return 1;
	}
 //设置服务器上的socket为监听状态
	if(listen(serverSocket, 5) < 0) 
	{
		perror("listen");
		return 1;
	}
 
	while(1)
	{
		printf("Listening on port: %d\n", SERVER_PORT);
 //调用accept函数后,会进入阻塞状态
 //accept返回一个套接字的文件描述符,这样服务器端便有两个套接字的文件描述符,
 //serverSocket和client。
 //serverSocket仍然继续在监听状态,client则负责接收和发送数据
 //clientAddr是一个传出参数,accept返回时,传出客户端的地址和端口号
 //addr_len是一个传入-传出参数,传入的是调用者提供的缓冲区的clientAddr的长度,以避免缓冲区溢出。
 //传出的是客户端地址结构体的实际长度。
 //出错返回-1
		client = accept(serverSocket, (struct sockaddr*)&clientAddr, (socklen_t*)&addr_len);
		if(client < 0)
		{
			perror("accept");
			continue;
		}
		printf("\nrecv client data...n");
 //inet_ntoa ip地址转换函数,将网络字节序IP转换为点分十进制IP
 //表达式:char *inet_ntoa (struct in_addr);
		printf("IP is %s\n", inet_ntoa(clientAddr.sin_addr));
		printf("Port is %d\n", htons(clientAddr.sin_port));
		while(1)
		{
			iDataNum = recv(client, buffer, 1024, 0);
			if(iDataNum < 0)
			{
				perror("recv");
				continue;
			}
			buffer[iDataNum] = '\0';
			if(strcmp(buffer, "quit") == 0)
				break;
			printf("%drecv data is %s\n", iDataNum, buffer);
			send(client, buffer, iDataNum, 0);
		}
	}
	return 0;
}

/*socket tcp客户端*/
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
 
#define SERVER_PORT 5555
 
/*
 连接到服务器后,会不停html" target="_blank">循环,等待输入,
 输入quit后,断开与服务器的连接
 */
 
int main()
{
 //客户端只需要一个套接字文件描述符,用于和服务器通信
	int clientSocket;
 //描述服务器的socket
	struct sockaddr_in serverAddr;
	char sendbuf[200];
	char recvbuf[200];
	int iDataNum;
	if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("socket");
		return 1;
	}
 
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(SERVER_PORT);
 //指定服务器端的ip,本地测试:127.0.0.1
 //inet_addr()函数,将点分十进制IP转换成网络字节序IP
	serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	if(connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
	{
		perror("connect");
		return 1;
	}
 
	printf("connect with destination host...\n");
 
	while(1)
	{
		printf("Input your world:>");
		scanf("%s", sendbuf);
		printf("\n");
 
		send(clientSocket, sendbuf, strlen(sendbuf), 0);
		if(strcmp(sendbuf, "quit") == 0)
			break;
		iDataNum = recv(clientSocket, recvbuf, 200, 0);
		recvbuf[iDataNum] = '\0';
		printf("recv data of my world is: %s\n", recvbuf);
	}
	close(clientSocket);
	return 0;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 本文向大家介绍基于Java语言实现Socket通信的实例,包括了基于Java语言实现Socket通信的实例的使用技巧和注意事项,需要的朋友参考一下 基于Java语言实现Socket通信 由于近日项目需求,需要在服务器中增加Socket通信的功能,接收硬件设备发送的心跳包和相关数据,因此又重新对Java的网络编程进行了复习,根据项目的实际情况做了简化的编程,实现了简单的通信过程。 1. Socket

  • 本文向大家介绍基于C#的socket编程的TCP异步的实现代码,包括了基于C#的socket编程的TCP异步的实现代码的使用技巧和注意事项,需要的朋友参考一下 一、摘要 本篇阐述基于TCP通信协议的异步实现。 二、实验平台 Visual Studio 2010 三、异步通信实现原理及常用方法 3.1 建立连接  在同步模式中,在服务器上使用Accept方法接入连接请求,而在客户端则使用Connec

  • 本文向大家介绍基于C语言实现点餐系统,包括了基于C语言实现点餐系统的使用技巧和注意事项,需要的朋友参考一下 这是一个很简单的点餐系统,用到的知识也不复杂,所以对新手学习还是很有作用,贴上来给大家参考下。 完成了如下功能:  1.显示餐厅提示语;  2.打印菜单(输出菜品分类:主食、甜点、饮料、主菜、开胃菜);  3.选择ID进入菜品分类子目录;  4.进行具体选择(可重复选择同一样菜);  5.返

  • 主要内容:本节引言:,1.运行效果图:,2.实现流程图:,3.代码示例:,4.代码下载:,5.本节小结:本节引言: 上节中我们给大家接触了Socket的一些基本概念以及使用方法,然后写了一个小猪简易聊天室的 Demo,相信大家对Socket有了初步的掌握,本节我们来学习下使用Socket来实现大文件的断点续传! 这里讲解的是别人写好的一个Socket上传大文件的例子,不要求我们自己可以写出来,需要的时候会用 就好! 1.运行效果图: 1.先把我们编写好的Socket服务端运行起来: 2.将一个音

  • 主要内容:本节引言:,1.什么是Socket?,2.Socket通信模型:,3.Socket服务端的编写:,4.Socket客户端的编写:,5.增强版案例:小猪简易聊天室,本节小结:本节引言: 上一节的概念课枯燥无味是吧,不过总有点收获是吧,本节开始我们来研究基于TCP协议的Socket 通信,先来了解下Socket的概念,以及Socket通信的模型,实现Socket的步骤,以及作为Socket服务 端与客户端的两位各做要做什么事情!好的,我们由浅入深来扣这个Socket吧! 1.什么是Sock

  • 本文向大家介绍python基于socket实现的UDP及TCP通讯功能示例,包括了python基于socket实现的UDP及TCP通讯功能示例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了python基于socket实现的UDP及TCP通讯功能。分享给大家供大家参考,具体如下: Server: Client: 运行结果: server [work@db-testing python]$

  • 本文向大家介绍Go语言基于Socket编写服务器端与客户端通信的实例,包括了Go语言基于Socket编写服务器端与客户端通信的实例的使用技巧和注意事项,需要的朋友参考一下 在golang中,网络协议已经被封装的非常完好了,想要写一个Socket的Server,我们并不用像其他语言那样需要为socket、bind、listen、receive等一系列操作头疼,只要使用Golang中自带的net包即可

  • Block 基础 Block 语法 Block 可以认为是一种匿名函数,使用如下语法声明一个 Block 类型: return_type (^block_name)(parameters) 例如: double (^multiplyTwoValues)(double, double); Block 字面值的写法如下: ^ (double firstValue, double secondValue