• 07socket编程


    TCP客户/服务器模型:
    从图中就可以看出基本的过程来。
    回射客户/服务器:
    这个是回射的图示,客户端发给服务端,服务端在发回给客户端。
     
    socket函数:
     
    包含头文件<sys/socket.h>
    功能:创建一个套接字用于通信
    原型
    int socket(int domain, int type, int protocol);
    参数
    domain :指定通信协议族(protocol family)
    type:指定socket类型,流式套接字SOCK_STREAM,数据报套接字SOCK_DGRAM,原始套接字SOCK_RAW
    protocol :协议类型
    返回值:成功返回非负整数, 它与文件描述符类似,我们把它称为套接口描述字,简称套接字。失败返回-1
     
    bind函数:
     
    包含头文件<sys/socket.h>
    功能:绑定一个本地地址到套接字
    原型
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    参数
    sockfd:socket函数返回的套接字
    addr:要绑定的地址
    addrlen:地址长度
    返回值:成功返回0,失败返回-1
     
    listen函数:
     
    包含头文件<sys/socket.h>
    功能:将套接字用于监听进入的连接
    原型
    int listen(int sockfd, int backlog);
    参数
    sockfd:socket函数返回的套接字
    backlog:规定内核为此套接字排队的最大连接个数
    返回值:成功返回0,失败返回-1
     
    listen函数:
     
    一般来说,listen函数应该在调用socket和bind函数之后,调用函数accept之前调用。
    对于给定的监听套接口,内核要维护两个队列:
    1、已由客户发出并到达服务器,服务器正在等待完成相应的TCP三路握手过程
    2、已完成连接的队列
     
    accept函数:
     
    包含头文件<sys/socket.h>
    功能:从已完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞。
    原型
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    参数
    sockfd:服务器套接字
    addr:将返回对等方的套接字地址
    addrlen:返回对等方的套接字地址长度
    返回值:成功返回非负整数,失败返回-1
     
    connect函数:
     
    包含头文件<sys/socket.h>
    功能:建立一个连接至addr所指定的套接字
    原型
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    参数
    sockfd:未连接套接字
    addr:要连接的套接字地址
    addrlen:第二个参数addr长度
    返回值:成功返回0,失败返回-1
     
    下面是回射的小程序,其中有很多点需要注意:
    服务器端:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    int main(void)
    {
    	int listenfd;
    	if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)//第三个参数可以指定,也可以为0,第一个参数可以AF_INET也可以用PF这个,建议用PF
    /*	if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
    		ERR_EXIT("socket");
    
    	struct sockaddr_in servaddr;//注意一下这个结构,这个和bind中的通用的结构不一样,用的时候需要转换成通用结构
    	memset(&servaddr, 0, sizeof(servaddr));
    	servaddr.sin_family = AF_INET;
    	servaddr.sin_port = htons(5188);
    	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//可以是任意,一般用这种
    	/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/  //也可以指定
    	/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/
    
    	if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    		ERR_EXIT("bind");
    	if (listen(listenfd, SOMAXCONN) < 0)
    		ERR_EXIT("listen");
    
    	struct sockaddr_in peeraddr;
    	socklen_t peerlen = sizeof(peeraddr);
    	int conn;
    	if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)//注意这里的后两个参数是返回客服端的信息的,并且返回的是客户端的套接字
    		ERR_EXIT("accept");
    
    	char recvbuf[1024];
    	while (1)           //再就是注意缓冲区的清空,不然数据会不正常
    	{
    		memset(recvbuf, 0, sizeof(recvbuf));
    		int ret = read(conn, recvbuf, sizeof(recvbuf));
    		fputs(recvbuf, stdout);
    		write(conn, recvbuf, ret);
    	}
    	close(conn);
    	close(listenfd);
    	
    	return 0;
    }
    

    客户端:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    int main(void)
    {
    	int sock;
    	if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    		ERR_EXIT("socket");
    
    	struct sockaddr_in servaddr;
    	memset(&servaddr, 0, sizeof(servaddr));
    	servaddr.sin_family = AF_INET;
    	servaddr.sin_port = htons(5188);
    	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");   //客户端这里需要指定,不能跟服务器端的那样
    
    	if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    		ERR_EXIT("connect");
    
    	char sendbuf[1024] = {0};
    	char recvbuf[1024] ={0};
    	while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    	{
    		write(sock, sendbuf, strlen(sendbuf));
    		read(sock, recvbuf, sizeof(recvbuf));
    
    		fputs(recvbuf, stdout);
    		memset(sendbuf, 0, sizeof(sendbuf));
    		memset(recvbuf, 0, sizeof(recvbuf));
    	}
    
    	close(sock);
    	
    	return 0;
    }
    

    大家体会一下吧。

  • 相关阅读:
    2017 ACM-ICPC西安网赛B-Coin
    Codeforces389D(SummerTrainingDay01-J)
    Codeforces672D(SummerTrainingDay01-I)
    VS2017.滚动条选项
    VS.自动调试
    ffmpeg.mp4.格式资料
    vs2017.unresolved external symbol __imp__fprintf&__imp____iob_func
    vs2017."const char *"的实参与"char *"的形参不兼容_goto跳过类型声明
    vue项目开发时怎么解决跨域
    vue奇怪的知识点又增加了
  • 原文地址:https://www.cnblogs.com/DamonBlog/p/4420040.html
Copyright © 2020-2023  润新知