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

 更新时间:2019年05月20日 14:58:51   作者:lovekun1989  
本文介绍了如何基于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
 
/*
 连接到服务器后,会不停循环,等待输入,
 输入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;
}

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

相关文章

  • 基于C++内存分配、函数调用与返回值的深入分析

    基于C++内存分配、函数调用与返回值的深入分析

    本篇文章是对C++中的内存分配、函数调用与返回值进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 全排列算法的原理和实现代码

    全排列算法的原理和实现代码

    这篇文章主要介绍了全排列算法的原理和实现代码,全排列是将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个,需要的朋友可以参考下
    2014-08-08
  • 常用的STL查找算法

    常用的STL查找算法

    这篇文章主要介绍了常用的STL查找算法的相关资料,十分的详细,需要的朋友可以参考下
    2015-07-07
  • C语言实现井字棋(三子棋)

    C语言实现井字棋(三子棋)

    这篇文章主要为大家详细介绍了C语言实现井字棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • c/c++小游戏源代码

    c/c++小游戏源代码

    这篇文章主要介绍了c/c++小游戏源代码,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • C++标准模板库vector的常用操作

    C++标准模板库vector的常用操作

    今天小编就为大家分享一篇关于C++标准模板库vector的常用操作,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • C++  数据结构链表的实现代码

    C++ 数据结构链表的实现代码

    这篇文章主要介绍了C++ 数据结构链表的实现代码的相关资料,需要的朋友可以参考下
    2017-01-01
  • C语言实现图的搜索算法示例

    C语言实现图的搜索算法示例

    这篇文章主要介绍了C语言实现图的搜索算法,结合具体实例形式分析了C语言实现图的定义及搜索相关操作技巧,需要的朋友可以参考下
    2017-06-06
  • C语言实现双向链表

    C语言实现双向链表

    本文给大家分享的是一段使用C语言实现双向链表的代码,完全是根据自己的理解和认识来编写的,希望大家能够喜欢,文章的最后附上了一个网友写的对于双向链表删除节点、插入节点、双向输出等操作的代码,也非常不错,推荐给大家
    2015-03-03
  • 详解C++编程中的主表达式与后缀表达式编写基础

    详解C++编程中的主表达式与后缀表达式编写基础

    这篇文章主要介绍了C++编程中的主表达式与后缀表达式编写基础,是C++入门学习中的基础知识,需要的朋友可以参考下
    2016-01-01

最新评论