Socket套接字和TCP通信的简单实现

Socket是实现不同主机间进程通信的方式,常用于网络服务。API包括socket、bind、listen、accept和connect等。通信过程涉及服务器的bind、listen、accept和客户端的connect。示例代码展示了服务器接收客户端消息的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Socket简介:

       socket(简称 套接字) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的

Socket常用API:

头文件:

#include <sys/types.h>

#include <sys/socket.h>

API:

/*创建套接字*/

int socket(int domain, int type, int protocol);  

参数:

-domain: 表示指定通信域(通信地址族)一般使用如下三种:

AF_UNIX      使用本地域套接字的地址结构,用于本地通信

AF_INET       使用IPv4的通信地址结构

AF_INET6     使用IPv6的通信地址结构

-type: 指定套接字类型,一般使用如下三种:

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

SOCK_DGEAM:数据报套接字,定义了一种不可靠、面向无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。使用了此套接字数据报协议默认为UDP

SOCK_RAM:原始套接字,允许直接访问底层协议,如IP或ICMP,它功能强大但使用较为不便,主要用于协议开发。

-protocol: 指定协议:当时用流式套接字SOCK_STREAM和数据报套接字SOCK_DGEAM时可以写为0,因为他们两个有默认的协议,当时用其他的套接字时在具体做定义。

/*绑定通信结构体*/

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

-sockfd:socket函数生成的套接字,也就是socket函数的返回值

-addr:通信结构体,注意此处的结构体类型为通用地址族结构体,他的结构为:

struct sockaddr {    

        sa_family_t sa_family;    

        char  sa_data[14];

}

我们在编程时使用的结构体一般为对应的通信域的结构体,如选择AF_INET(IPv4通信地址结构),他的结构体定义为

struct sockaddr_in {    

        sa_family_t   sin_family; /* 地址族: AF_INET */

        in_port_t    sin_port;   /* 网络字节序的端口号 */

        struct in_addr sin_addr;   /*IP地址结构体 */

};

struct sockaddr {  

       uint32_t s_addr;  /* 网络字节序的IP地址 */

};

他们之间是有区别的,所以在使用时应该进行强制类型转换,实例见最后的代码。

-addrlen:通信结构体的长度

/*监听套接字*/

int listen(int sockfd, int backlog);

 sockfd:socket函数生成的套接字

/*处理客户端发起的连接,生成新的套接字*/

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);此函数的返回值为用来进行通信的新的套接字,读写操作都使用新的套接字作为参数。

参数:

-sockfd: 函数socket生成的套接字

-addr:客户端的地址族信息

-addrlen:地址族结构体的长度

/*向服务器发起连接请求*/

int connect(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);参数同上

通信过程:

1.通信之前,客户和服务器先创建套接字,使用socket函数来创建且服务器端和客户端都需要此操作。

2.服务器端调用 bind,把端口号和本地 IP 地址填写到已创建的套接字中。要注意区分好服务器端和客户端,服务器端才需要调用此函数,客户端只需要创建好套接字后发送连接请求即可。

3.服务器端调用 listen(收听),把套接字设置为被动方式,以便随时接受客户的服务请求。UDP 服务器不使用 listen 系统调用。

4.服务器端调用 accept(接受),以便把远地客户进程发来的连接请求提取出来。UDP 服务器不使用 accept 系统调用。

5.客户进程调用 connect,以便和远地服务器建立连接(这就是主动打开)。

6.客户和服务器在 TCP 连接上使用 write传送数据,使用 read接收数据,客户端使用的是最开始socket出来的套接字作为write和read的参数fd,而服务器端则是使用accept函数的返回值作为其参数。

7.客户或服务器通信结束,调用 close 释放连接和撤销套接字。服务器端需要close两次 ,分别是socket函数返回的套接字和accept函数返回的套接字。

代码实例:

目的:实现在本机上互相通信

服务器端接收客户端发送

服务器端:

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

int main(int argc, const char *argv[])
{

	int fd,newfd,ret;
    //使用传参的方式来传入IP地址和端口号,第一个参数为Ip地址
    //参数个数判断;
	if(argc < 3){
		printf("too few arguments\n");
		return 0;
	}
	char buf[BUFSIZ];//BUFSIZ为一个常量,数值为8192
	fd = socket(AF_INET,SOCK_STREAM,0);//第一步:创建套接字
	if( fd < 0 ){
		perror("socket");
		return 0;
	}
    //初始化bind函数的第二个参数-通信结构体
    //因为我们是用的IPv4的互联网通信协议,所以family的参数为AF_INET
	struct sockaddr_in soc;
    //htons 用来将主机字节顺序转换为网络字节顺序
	soc.sin_family = AF_INET;
	soc.sin_port = htons(atoi(argv[2]));
	if (inet_aton(argv[1], &soc.sin_addr) == 0) {
		fprintf(stderr, "Invalid address\n");
		exit(EXIT_FAILURE);
	}
    //绑定通信结构体
	ret = bind(fd,&soc,sizeof(soc));
	if(ret < 0){
		perror("bind");
		return 0;

	}
    //监听套接字
	ret = listen(fd,5);
	if(ret < 0){
		perror("listen");
		return 0;
	}
	newfd = accept(fd,NULL,NULL);
	if(newfd < 0){
		perror("newfd");
		return 0;
	//循环的读
	while(1){
	
	memset(buf,0,BUFSIZ);
	ret = read(newfd,buf,BUFSIZ);
	if(ret == 0)
		break;
	printf("%s",buf);
	}
	close(newfd);
	close(fd);
	return 0;
}

客户端:

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

int main(int argc, const char *argv[])
{
	if(argc < 3){
		printf("too few arguments\n");
		return 0;
	}
	int fd;
	char buf[BUFSIZ];
	fd = socket(AF_INET,SOCK_STREAM,0);
	if( fd < 0 ){
		perror("socket");
		return 0;
	}
	struct sockaddr_in soc;
	soc.sin_family = AF_INET;
	soc.sin_port = htons(atoi(argv[2]));
	if (inet_aton(argv[1], &soc.sin_addr) == 0) {
		fprintf(stderr, "Invalid address\n");
		exit(EXIT_FAILURE);
	}

	if(connect(fd,(struct sockaddr *)&soc,sizeof(soc)) == -1){
		perror("connect");
		return 0;

	}
	while(1){
		printf("Please Input->");
		fgets(buf,BUFSIZ,stdin);
		write(fd,buf,strlen(buf));
	}

	close(fd);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值