socket编程(1)

一、流程图

二、实现

1、服务器

#include<stdio.h>
#include<string.h>
#include<unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
//#include <sys/socket.h>
#include <netinet/in.h>
//#include <arpa/inet.h>

int main(void)
{
    /*
    #include <sys/types.h>
    #include <sys/socket.h>
    int socket(int domain, int type, int protocol);
    返回值:非负描述符 – 成功,-1 - 出错
    1) domain为地址族(Address Family),也就是 IP 地址类型,
    常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。
    AF_INET 表示 IPv4 地址,例如 127.0.0.1;
    AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
    127.0.0.1,它是一个特殊IP地址,表示本机地址。
    2) type 为数据传输方式/套接字类型,
    常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 
    和 SOCK_DGRAM(数据报套接字/无连接的套接字)
    3) protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,
    分别表示 TCP 传输协议和 UDP 传输协议。
    一般情况下有了 af 和 type 两个参数就可以创建套接字了,
    操作系统会自动推演出协议类型,除非遇到这样的情况:
    有两种不同的协议支持同一种地址类型和数据传输类型。
    如果我们不指明使用哪种协议,操作系统是没办法自动推演的。
    */
   //建立套接字
   int tcp_fd=socket(AF_INET,SOCK_STREAM,0);
   if(tcp_fd<0)
   {
       printf("socket fail\n");
       return -1;
   }
   else
   {
       printf("socket......\n");
       printf("socket success\n");
   }

   /*struct sockaddr和struct sockaddr_in这两个结构体用来处理网络通信的地址。
    一、sockaddr
    sockaddr在头文件#include <sys/socket.h>中定义,
    sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起
    struct sockaddr 
    {  
        sa_family_t sin_family;//地址族
      char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息               
   }; 
    二、sockaddr_in
    sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,
    该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中
    struct sockaddr_in
    {
        short sin_family;
        //Address family一般来说AF_INET(地址族)PF_INET(协议族
 
        unsigned short sin_port;
        //Port number(必须要采用网络数据格式,
        //普通数字可以用htons()函数转换成网络数据格式的数字)
 
        struct in_addr sin_addr;
        //IP address in network byte order(Internet address)
 
        unsigned char sin_zero[8];
        //Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐
 
    };

    //存放32IP地址
    typedef uint32_t in_addr_t;
    struct in_addr
    {
        in_addr_t s_addr;
    };
    */
   	struct sockaddr_in server_addr;
	server_addr.sin_family=AF_INET;
    /*htons 转换程网络字节序
    #include <arpa/inet.h> 
    uint16_t htons(uint16_t hostshort); 
    将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian),电脑存储小端序

    https://www.zhihu.com/question/54580649/answer/145500997
    端口的作用:
    我们知道一台主机(对应一个IP地址)可以提供很多服务,比如web服务,ftp服务等等。
    如果只有一个IP,无法却分不同的网络服务,所以我们采用”IP+端口号”来区分不同的网络服务。 
    端口的定义:
    端口号是标识主机内唯一的一个进程,IP+端口号就可以标识网络中的唯一进程。
    在我们通常用的Socket编程中,IP+端口号就是套接字 
    端口号是由16比特进行编号,范围是0-65535,按照道理来讲,这些端口你都可以随便用。
    但是你不是vip用户,所以有一些端口被vip用户占着。
    比如FTP 21 Ssh 22等等,所以给端口分了类,规定你可以使用端口的范围。 
    端口的分类
    分类的维度很多,这里我们按照服务端使用还是客户端使用分类 
    a.服务端使用的端口号 预留端口号        
    取值范围0-1023,这些端口我们编程的时候不能使用,是那些vip应用程序使用的,
    只有超级用户特权的应用才允许被分配一个预留端口号 
    登记端口号        
    取值范围1024-49151,就是我们平时编写服务器使用的端口号范围 ,
    服务器的端口号,只要在这个范围就好.
    客户端使用的端口号    
    取值范围49152-65535,这部分是客户端进程运行时动态选择的范围,又叫临时端口号*/
	server_addr.sin_port=htons(43281);
    /* #include <sys/socket.h>
       #include <netinet/in.h>
       #include <arpa/inet.h>
    将字符串转换成32位的网络字节序
    char *inet_ntoa(struct in_addr in);
    tcp_addr.sin_addr.s_addr=inet_addr(INADDR_ANY)
    宏INADDR_ANY转换过来就是0.0.0.0,泛指本机的意思,也就是表示本机的所有IP,
    因为有些机子不止一块网卡,多网卡的情况下,这个就表示所有网卡ip地址的意思。
    就比方说我这里本机的ip地址包括:
    192.168.150.136   ifconfig
    127.0.0.1
    0.0.0.0
    */
	server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");

    /*bind函数把一个本地协议地址赋予一个套接字。
    #include<sys/socket.h>
    int bind(int sockfd,  const struct sockaddr, socklen_t addrlen);
    第二个参数是一个指向特定协议的地址结构的指针,第三个参数是该地址结构的长度。*/
    bind(tcp_fd, (struct sockaddr *)&server_addr,sizeof(struct sockaddr));

    /*头文件:#include <sys/socket.h>
    定义函数:int listen(int s, int backlog);
    函数说明:listen()用来等待参数s 的socket 连线. 
    参数backlog 指定同时能处理的最大连接要求, 
    如果连接数目达此上限则client 端将收到ECONNREFUSED 的错误. 
    Listen()并未开始接收连线, 只是设置socket 为listen 模式, 
    真正接收client 端连线的是accept(). 
    通常listen()会在socket(), bind()之后调用, 接着才调用accept().
    */
	listen(tcp_fd,4);

    /*当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。
    int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);*/
	int con_fd=accept(tcp_fd,NULL,NULL);//建立通信套接字
	if(con_fd<0)
	{
		printf("accept failed\n");
		return -2;
	}
    else
    {
        printf("connect\n");
    }

	//聊天
	char buf[100];
	while(1)
	{
		//清空buffer
        bzero(buf,100);
		//读取客户端发送的数据
        read(con_fd,buf,100);
		printf("server:%s\n",buf);
        //退出条件
        if(!strncmp(buf,"quit",4))
		{
			break;
		}
	}

	close(con_fd);
	close(tcp_fd);

    return 0;
}

2、客户端

#include<stdio.h>
#include<string.h>
#include<unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
//#include <sys/socket.h>
#include <netinet/in.h>
//#include <arpa/inet.h>

int main(void)
{
	int tcp_fd=socket(AF_INET,SOCK_STREAM,0);//建立带连接套接字
   	if(tcp_fd<0)
   	{
       printf("socket fail\n");
       return -1;
   	}
   	else
   	{
       printf("socket......\n");
       printf("socket success\n");
   	}
	
	//拨号,服务器的ip+port
	struct sockaddr_in server_addr;
	server_addr.sin_family=AF_INET;
	server_addr.sin_port=htons(43281);//转换程网络字节序
	server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//将字符串转换成32位的网络字节序
	/*#include<sys/socket.h>
	int connect(int sockfd, const struct sockaddr *servaddr, int *addrlen);
    返回:若成功则返回0,失败则返回-1;
    sockfd是有socket函数返回的套接字描述符,
	第二个、第三个参数分别是一个指向套接字地址结构的指针和该结构的大小。
	套接字地址结构必须含有服务器的IP地址和端口号。*/
	int n=connect(tcp_fd, (struct sockaddr *)&server_addr,sizeof(struct sockaddr));
	if(n<0)
	{
		printf("connect failed\n");
		return -2;
	}
    else
    {
        printf("connect\n");
    }
	
	
	//聊天
	char buf[100];
	while(1)
	{
		bzero(buf,100);
		fgets(buf,100,stdin);
		write(tcp_fd,buf,strlen(buf));	//给服务器发送消息
		if(!strncmp(buf,"quit",4))
		{
			break;
		}
	}

	close(tcp_fd);
    return 0;
}

三、效果

服务器

客户端

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值