TCP的编程之socket套接字实现两台主机的无线通信


前言


一、TCP编程是什么?

TCP编程是基于TCP协议的客户端和服务器程序之间的数据传输。TCP编程通常使用socket编程接口实现,因此也称为socket编程。

二、TCP编程分几步?

首先TCP编程分为两个部分
TCP服务器

	1>建立sock套接字  	     	
	2>绑定IP和端口号  		
	3>监听												
	4>等待客户端连接  
	5>接收/发送

TCP客户端

	1>建立sock套接字 
	2>主动连接服务器  
	3>接收/发送

服务端部分

1.建立sock套接字---->创建一个具有网络属性的文件描述符

socket函数原型如下:

#include <sys/types.h>          /* 头文件*/
#include <sys/socket.h>
int socket(int domain, int type, int protocol);//函数原型

函数功能: 建立sock套接字 (具有网络通信功能的文件标识符)

参数: domain:地址族
AF_UNIX, AF_LOCAL   Local communication              unix(7)    本地连接协议
AF_INET             IPv4 Internet protocols          ip(7)		IPv4协议 
AF_INET6            IPv6 Internet protocols          ipv6(7)	IPv6协议

参数:type:协议

SOCK_STREAM:流式套接字(TCP) 
SOCK_DGRAM: 数据报文套接字(UDP)
SOCK_RAW:	原始套接字  不选用任何协议

参数:protocol:参数生效值

默认为:0   --->前两个参数生效
返回值: 
	成功返回 套接字文件描述符
	失败返回-1,并设置错误码

2.绑定IP和端口号

bind函数原型如下:

#include <sys/types.h>          /* 头文件 */
#include <sys/socket.h>
    int bind(int sockfd, const struct sockaddr *addr,\
    socklen_t addrlen);

参数:

sockfd:套接字文件描述符
addr:struct sockaddr *   赋值的结构体指针
addrlen:addr的长度 
 返回值: 
	成功返回0 
	失败返回-1,并设置错误码

bind第二个参数一般会使用sockaddr_in 结构体

struct sockaddr_in {
	short sin_family;         		 // 地址族 
	 __be16    sin_port;   			//  端口号 
	struct in_addr sin_addr;       //   IP地址
	s_addr      				  //   专门给IPv4地址赋值的
	unsigned char sin_zero[8];   //    填充位
};

网络编程的难点:需要进行小端序和大端序的转换

__be16    sin_port;             /* Port number */端口号 
struct in_addr sin_addr;       /* Internet address */IP地址

这时候就需要引入一个函数htons(uint16_t hostshort)

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);     针对32uint16_t htons(uint16_t hostshort);	   针对16位
前两个:小转大 

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);
后两个:大转小

由于系统是不会自动帮你把点分十进制转化为网络二进制的 所以,我们需要自行获取IP地址的网络二进制

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);
功能: 
	将点分十进制转化为网络二进制 
参数:
	cp:字符串IP地址返回值: 
返回值:
	成功返回二进制地址
	失败转化无效,返回INADDR_NONE

3.监听

为了防止一次性太多客户端接入的情况发生,我们需要限制一下服务器 同一时刻接入客户端的最大值
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);

功能: 监听 :限制服务器同一时刻被连接的最大值

参数: 
	SOCKFD: socket套接字
	backlog:同一时刻被连接的最大值			
返回值: 
	成功返回0 
	失败返回-1,并设置错误码		

4.等待客户端连接

#include <sys/types.h>          
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数: 
	sockfd:socket套接字
	addr:客户端的IP 
	addrlen:addr的长度:  需要指针 			
返回值: 
	成功返回客户端的fd 
	失败返回-1,并设置错误码

客户端部分

1.建立sock套接字

请看上面的服务端部分的介绍

2.声明服务器的IP和端口号(给地址结构体赋值)

跟服务端部分的操作无异,少了一步bind函数的调用

3.主动连接服务器

#include <sys/types.h>          
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);
功能: 主动连接服务器 
参数: 
		sockfd: socket套接字
		addr:服务器的IP和端口号 
		addrlen:结构体的大小 
			
返回值: 
		成功返回0 
		失败返回-1,并设置错误码

4.收/发

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能: 接收数据 
参数: 
		sockfd:数据来源
		buf:数据存放的地方
		len:buf的长度
		flags:0  阻塞 
			
返回值: 
		成功返回接收的字节数
		失败返回-1,并设置错误码
		如果断开连接返回0
 #include <sys/types.h>
 #include <sys/socket.h>
 ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能: 
		发送数据 
参数: 
		sockfd:给谁发
		buf:数据存放的地方
		len:buf的长度
		flags:0  阻塞 			
返回值: 
		成功返回发送的字节数
		失败返回-1,并设置错误码

总结

TCP编程本质上就是操作socket套接字(具有网络通信功能的文件标识符)来实现主机一对一的连接通信。其所有函数都是为了逐渐完善通信的功能。而接下来是我编写的一个通过socket套接字实现的服务器和客户端的双向通信例程,其核心原理就是开辟2个独立的进程来进行数据的收发,互不干扰阻塞。
服务端

#include <stdio.h>
#include <sys/types.h>          
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
/*
    int socket(int domain, int type, int protocol);
*/
int main(void)
{
    //1>建立socket连接
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0){
        perror("socket");
        return -1;
    }
    printf("sockfd = %d\n",sockfd);
    //绑定IP和端口号
    struct sockaddr_in server;//声明结构体,并即将给里面的成员赋值
    server.sin_family = AF_INET;//表示使用IPv4地址协议
    server.sin_port = htons(12345);//端口号 将12345转成大端序
    server.sin_addr.s_addr = inet_addr("192.168.60.119");//ip赋值
    if(bind(sockfd,(struct sockaddr *)&server,sizeof(server))){
        perror("bind");
        return -1;
    }
    //3>监听--->服务器的保护机制
    listen(sockfd,8);//当前服务器,同一时刻连接客户端的最大值为8

    //等待客户端连接
    struct sockaddr_in client;//用来保存客户端的IP和端口号
    int len = sizeof(client);
    int fd;
    fd = accept(sockfd,(struct sockaddr *)&client,&len);
    if(fd < 0){
        perror("accept");
        return -1;
    }
    printf("客户%d上线了\n",fd);
    pid_t pid = fork();
    if(pid < 0){
        perror("pid");
        return -1;
    }
	char r_buf[50];
	char w_buf[50];
    if(pid > 0){
        while(1){
            printf("我是父进程\n");
		    bzero(w_buf,sizeof(w_buf));
		    scanf("%s",w_buf);
		    send(fd,w_buf,strlen(w_buf),0);
        }
    }
    if(0 == pid){
        while(1){
            printf("我是子进程\n");
		    bzero(r_buf,sizeof(r_buf));
		    recv(fd,r_buf,sizeof(r_buf),0);
	        printf("%s\n",r_buf);
        }
    }

    return 0;
}

客户端

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <stdlib.h>
/*建立sock套接字
 * 声明服务器的IP端口号
 * 主动连接服务器
 * 收/发
 * */
int main(int argc,char *argv[])
{
    if(argc != 3){
        printf("User:%s <IP><PORT>\n",argv[0]);
        return -1;
    }
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0){
        perror("socket");
        return -1;
    }
    printf("sockfd = %d\n",sockfd);
    //声明IP和端口号
    struct sockaddr_in server;//声明结构体,并即将给里面的成员赋值
    server.sin_family = AF_INET;//表示使用IPv4地址协议
    server.sin_addr.s_addr = inet_addr(argv[1]);//ip赋值
    server.sin_port = htons(atoi(argv[2]));//端口号 将12345转成大端序

    	//3>主动连接服务器
	if(connect(sockfd,(struct sockaddr *)&server,sizeof(server))){
		perror("connect");
		return -1;
	}
	printf("连接成功\n");
    //4>发
    pid_t pid = fork();//开辟2个进程
	char r_buf[50];
	char w_buf[50];
        if(pid > 0){
            while(1){
                printf("我是父进程\n");
		        bzero(w_buf,sizeof(w_buf));
		        scanf("%s",w_buf);
		        send(sockfd,w_buf,strlen(w_buf),0);
            }
        }
        if(pid ==  0){
            while(1){
		        bzero(r_buf,sizeof(r_buf));
                printf("我是子进程\n");
		        recv(sockfd,r_buf,sizeof(r_buf),0);
	    	    printf("%s\n",r_buf);
            }
        }
    return 0;
}
  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来为您详细介绍一下如何使用TCP协议进行网络编程实现两台计算机之间的通信。 TCP协议是一种面向连接的协议,它可以保证数据传输的可靠性。在进行TCP协议网络编程时,我们需要使用socket编程接口来实现。下面是一份示例代码,您可以参考其中的注释来理解代码的实现过程。 ```python import socket # 创建socket对象 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 获取本地主机名和端口号 host = socket.gethostname() port = 9999 # 绑定端口号 server_socket.bind((host, port)) # 监听客户端连接 server_socket.listen(5) while True: # 等待客户端连接 client_socket, addr = server_socket.accept() print('连接地址:', addr) # 向客户端发送消息 msg = '欢迎访问菜鸟教程!' + "\r\n" client_socket.send(msg.encode('utf-8')) # 关闭客户端连接 client_socket.close() ``` 在上述代码中,我们首先创建了一个socket对象,指定使用IPv4协议和TCP协议。然后我们获取本地主机名和端口号,并将其绑定到socket对象上。接着我们使用listen()函数监听客户端连接。在循环中,我们使用accept()函数等待客户端连接,一旦有客户端连接,就向客户端发送一条欢迎消息并关闭连接。 下面是客户端代码的示例,同样附有注释: ```python import socket # 创建socket对象 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 获取本地主机名和端口号 host = socket.gethostname() port = 9999 # 连接服务器 client_socket.connect((host, port)) # 接收服务器消息 msg = client_socket.recv(1024) client_socket.close() print(msg.decode('utf-8')) ``` 在客户端代码中,我们同样使用socket对象来创建一个TCP连接。指定连接主机名和端口号后,使用connect()函数连接服务器。接着我们使用recv()函数接收服务器发送的消息,并关闭连接。最后将接收到的消息解码后输出。 以上就是使用TCP协议进行网络编程实现两台计算机之间的通信的示例。希望能对您有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值