C语言socket编程实例

文章目录

      • 一、实验说明
      • 二、面向连接的流式套接字 C/S 例子
      • 三、非阻塞的多人聊天服务器端例子
      • 四、简单的 IPv6 UDP socket编程
      • 四、参考书籍和

一、实验说明

  • 服务端:树莓派(可以使用putty、xshell、vnc远程操作树莓派,这里使用vnc)
  • 客户端:Ubuntu
  • 所用语言:C语言
  • 树莓派和Ubuntu应处于同一个局域网下(可以用手机热点连接)

二、面向连接的流式套接字 C/S 例子

  1. 树莓派下,新建一个 Server1.c,命令 nano Server1.c,然后写入如下内容

    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    #define PORT "9090" // the port users will be connecting to 
    #define BACKLOG 10 
    // how many pending connections queue will hold 
    void sigchld_handler(int s) 
    { 
    	while(waitpid(-1, NULL, WNOHANG) > 0); 
    }
    // get sockaddr, IPv4 or IPv6: 
    void *get_in_addr(struct sockaddr *sa) 
    { 
        if (sa->sa_family == AF_INET) { 
        	return &(((struct sockaddr_in*)sa)->sin_addr); 
        }
    	return &(((struct sockaddr_in6*)sa)->sin6_addr); 
    }
    int main(void) 
    { 
        int sockfd, new_fd; // listen on sock_fd, new connection on new_fd 
        struct addrinfo hints, *servinfo, *p; 
        struct sockaddr_storage their_addr; // connector's address information 
        socklen_t sin_size; 
        struct sigaction sa; 
        int yes=1;
        char s[INET6_ADDRSTRLEN]; 
        int rv; 
        memset(&hints, 0, sizeof hints); 
        hints.ai_family = AF_UNSPEC; 
        hints.ai_socktype = SOCK_STREAM; 
        hints.ai_flags = AI_PASSIVE; // use my IP 
        if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) { 
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); 
            return 1; 
    	}
    // loop through all the results and bind to the first we can 
        for(p = servinfo; p != NULL; p = p->ai_next) { 
        if ((sockfd = socket(p->ai_family, p->ai_socktype, 
        p->ai_protocol)) == -1) { 
            perror("server: socket"); 
            continue; 
        }
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, 
        sizeof(int)) == -1) { 
            perror("setsockopt"); 
            exit(1); 
        }
        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { 
            close(sockfd); 
            perror("server: bind"); 
            continue; 
        }
        break; 
    }
        if (p == NULL) { 
            fprintf(stderr, "server: failed to bind\n"); 
            return 2; 
        }
        freeaddrinfo(servinfo); // all done with this structure 
        if (listen(sockfd, BACKLOG) == -1) { 
            perror("listen"); 
            exit(1); 
        }
        sa.sa_handler = sigchld_handler; // reap all dead processes 
        sigemptyset(&sa.sa_mask); 
        sa.sa_flags = SA_RESTART; 
        if (sigaction(SIGCHLD, &sa, NULL) == -1) { 
            perror("sigaction"); 
            exit(1); 
        }
        printf("server: waiting for connections...\n"); 
        while(1) { // main accept() loop 
            sin_size = sizeof their_addr; 
            new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); 
            if (new_fd == -1) { 
                perror("accept"); 
                continue; 
            }
            inet_ntop(their_addr.ss_family, 
            get_in_addr((struct sockaddr *)&their_addr), 
            s, sizeof s); 
            printf("server: got connection from %s\n", s); 
            if (!fork()) { // this is the child process 
                close(sockfd); // child doesn't need the listener 
                if (send(new_fd, "Hello, world!", 13, 0) == -1) 
                perror("send"); 
                close(new_fd); 
                exit(0); 
            }
            close(new_fd); // parent doesn't need this 
        }
        return 0; 
    }
    
  2. 然后编译并运行

    gcc Server1.c -o Server1
    ./Server1
    
  3. Ubuntu系统下,新建一个Client1.c文件,命令gedit Client1.c,然后写入如下内容

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define PORT "9090"  //the port client will be connecting to
    #define MAXDATASIZE 100  //max number of bytes we can get at once
    
    //get sockaddr, IPv4 or IPv6
    void *get_in_addr(struct sockaddr *sa)
    {
    	if(sa->sa_family == AF_INET)
    	{
    		return &(((struct sockaddr_in*)sa)->sin_addr);
    	}
    	return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    
    int main(int argc, char *argv[])
    {
    	int sockfd, numbytes;
    	char buf[MAXDATASIZE];
    	struct addrinfo hints, *servinfo, *p;
    	int rv;
    	char s[INET6_ADDRSTRLEN];
    	if(argc != 2)
    	{
    		fprintf(stderr, "usage:client hostname\n");
    		exit(1);
    	}
    	memset(&hints, 0, sizeof hints);
    	hints.ai_family = AF_UNSPEC;
    	hints.ai_socktype = SOCK_STREAM;
    	if((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0)
    	{
    		fprintf(stderr, "getaddrinfo:%s\n",gai_strerror(rv));
    		return 1;
    	}
        // loop through all the results and connect to the first we can 
    	for(p = servinfo; p != NULL; p = p->ai_next)
    	{
    		if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
    		{
    			perror("client:socket");
    			continue;
    		}
    		if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
    		{
    			close(sockfd);
    			perror("client:connect");
    			continue;
    		}
    		break;
    	}
    	if(p == NULL)
    	{
    		fprintf(stderr, "client:failed to connect\n");
    		return 2;
    	}
    	inet_ntop(p->ai_family, get_in_addr((struct sockaddr*)p->ai_addr), s, sizeof s);
    	printf("client:connecting to %s\n",s);
    	freeaddrinfo(servinfo);// all done with this structure 
    	if((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1)
    	{
    		perror("recv");
    		exit(1);
    	}
    	buf[numbytes] = '\0';
    	printf("client:received %s\n",buf);
    	close(sockfd);
    	return 0;
    }
    
  4. 然后编译并运行

    gcc Client1.c -o Client1
    ./Client1 192.168.43.161
    

    注意:这里后面跟的IP是自己服务端的IP,即,自己树莓派的IP

  5. 运行结果如下

    C语言socket编程实例_第1张图片

三、非阻塞的多人聊天服务器端例子

  1. 树莓派下,新建一个Server2.c文件,命令nano Server2.c,然后写入如下内容

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define PORT "9090"  //port we're listening on
    
    //get sockaddr,IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa)
    {
        if(sa->sa_family == AF_INET){
            return &(((struct sockaddr_in*)sa)->sin_addr);
        }
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    
    int main(void)
    {
        fd_set master; 
        // master file descriptor list 
        fd_set read_fds; // temp file descriptor list for select() 
        int fdmax; 
        // maximum file descriptor number
        int listener; 
        // listening socket descriptor 
        int newfd; 
        // newly accept()ed socket descriptor 
        struct sockaddr_storage remoteaddr; // client address 
        socklen_t addrlen; 
        char buf[256]; 
        // buffer for client data 
        int nbytes; 
        char remoteIP[INET6_ADDRSTRLEN]; 
        int yes=1; 
        // for setsockopt() SO_REUSEADDR, below 
        int i, j, rv; 
        struct addrinfo hints, *ai, *p; 
        FD_ZERO(&master); 
        // clear the master and temp sets 
        FD_ZERO(&read_fds); 
        // get us a socket and bind it 
        memset(&hints, 0, sizeof hints); 
        hints.ai_family = AF_UNSPEC; 
        hints.ai_socktype = SOCK_STREAM; 
        hints.ai_flags = AI_PASSIVE; 
        if((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0)
        {
            fprintf(stderr, "selectserver:%s\n", gai_strerror(rv));
            exit(1);
        }
    
        for(p = ai; p != NULL; p = p->ai_next)
        {
            listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
            if(listener < 0)
            {
                continue;
            }
            // lose the pesky "address already in use" error message 
            setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
            if(bind(listener, p->ai_addr, p->ai_addrlen) < 0)
            {
                close(listener);
                continue;
            }
            break;
        }
    	// if we got here, it means we didn't get bound 
        if(p == NULL)
        {
            fprintf(stderr, "selectserver:failed to bind\n");
            exit(2);
        }
    
        freeaddrinfo(ai);  // all done with this 
        // listen
        if(listen(listener, 10) == -1)
        {
            perror("listen");
            exit(3);
        }
        // add the listener to the master set 
        FD_SET(listener, &master);
    	// keep track of the biggest file descriptor 
        fdmax = listener;  // so far, it's this one 
    	// main loop 
        for(;;)
        {
            read_fds = master; // copy it 
            if(select(fdmax + 1, &read_fds, NULL, NULL, NULL) == -1)
            {
                perror("select");
                exit(4);
            }
    		// run through the existing connections looking for data to read 
            for(i = 0; i <= fdmax; i++)
            {
                if(FD_ISSET(i, &read_fds))// we got one!!
                {
                    if(i == listener)
                    {
                        // handle new connections
                        addrlen = sizeof remoteaddr;
                        newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen);
    
                        if(newfd == -1)
                        {
                            perror("accept");
                        }
                        else
                        {
                            FD_SET(newfd, &master); // add to master set 
                            if(newfd > fdmax)
                            {
                                // keep track of the max 
                                fdmax = newfd;
                            }
                            printf("selectserver: new connection from %s on " 
                            "socket %d\n", 
                            inet_ntop(remoteaddr.ss_family, 
                            get_in_addr((struct sockaddr*)&remoteaddr), 
                            remoteIP, INET6_ADDRSTRLEN), 
                            newfd); 
                        }
                    }
                    else
                    {
                        // handle data from a client 
                        if((nbytes = recv(i, buf, sizeof buf, 0)) <= 0)
                        {
                            // got error or connection closed by client 
                            if(nbytes == 0)
                            {
                                // connection closed 
                                printf("selectserver:socket %d hung up\n", i);
                            }
                            else
                            {
                                perror("recv");
                            }
                            close(i);// bye! 
                            FD_CLR(i, &master);// remove from master set 
                        }
                        else
                        {
                            // we got some data from a client 
                            for(j =0; j <= fdmax; j++)
                            {
    			    			// send to everyone! 
                                if(FD_ISSET(j, &master))
                                {
                                     // except the listener and ourselves 
                                    if(j != listener && j != i)
                                    {
                                        if(send(j, buf, nbytes, 0) == -1)
                                        {
                                            perror("send");
                                        }
                                    }
                                }
                            }
                        }
                    }  //END handle from client
                }  //END got new incoming connection
            }  //END looping through file descriptors
        }  //END for(;;)--and you thought it would never end!
        return 0;
    }
    
  2. 编译并运行

    gcc Server2.c -o Server2
    ./Server2
    
  3. Ubuntu下新建一个Client2.c文件,命令gedit Client2.c,然后写入如下内容

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define PORT "9090"  //the port client will be connecting to
    #define MAXDATASIZE 100  //max number of bytes we can get at once
    
    int sockfd, numbytes;
    char buf[MAXDATASIZE];
    
    //get sockaddr, IPv4 or IPv6
    void *get_in_addr(struct sockaddr *sa)
    {
    	if(sa->sa_family == AF_INET)
    	{
    		return &(((struct sockaddr_in*)sa)->sin_addr);
    	}
    	return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    
    void *recvMag()
    {
        while(1)
        {
            if((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1)
    	{
    		perror("recv");
    		exit(1);
    	}
    	if(numbytes == 1)
    		continue;
    	buf[numbytes] = '\0';
    	printf("\nreceived:%s\n",buf);
        }
    }
    
    int main(int argc, char *argv[])
    {
    	//int sockfd, numbytes;
    	//char buf[MAXDATASIZE];
    	struct addrinfo hints, *servinfo, *p;
    	int rv;
    	char s[INET6_ADDRSTRLEN];
    	pthread_t t1;
        char mag[MAXDATASIZE];
        
    	if(argc != 2)
    	{
    		fprintf(stderr, "usage:client hostname\n");
    		exit(1);
    	}
    	memset(&hints, 0, sizeof hints);
    	hints.ai_family = AF_UNSPEC;
    	hints.ai_socktype = SOCK_STREAM;
    	if((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0)
    	{
    		fprintf(stderr, "getaddrinfo:%s\n",gai_strerror(rv));
    		return 1;
    	}
        // loop through all the results and connect to the first we can 
    	for(p = servinfo; p != NULL; p = p->ai_next)
    	{
    		if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
    		{
    			perror("client:socket");
    			continue;
    		}
    		if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
    		{
    			close(sockfd);
    			perror("client:connect");
    			continue;
    		}
    		break;
    	}
    	if(p == NULL)
    	{
    		fprintf(stderr, "client:failed to connect\n");
    		return 2;
    	}
    	inet_ntop(p->ai_family, get_in_addr((struct sockaddr*)p->ai_addr), s, sizeof s);
    	printf("client:connecting to %s\n",s);
    	freeaddrinfo(servinfo);// all done with this structure 
    	
    	int err = pthread_create(&t1, NULL, recvMag, NULL);
    	if(err != 0)
    	{
    		printf("receive failed");
    		exit(1);
    	}
        
    	while(1)
    	{
            scanf("%s", mag);
            if(send(sockfd, mag, sizeof mag, 0) == -1)
            {
                printf("send failed!\n");
            }
    	}
    	return 0;
    }
    

    注意:因为是一个多人聊天程序,所以这里至少应该有两个客户端,本人是重新再开个Ubuntu的虚拟机,重复客户端的操作

  4. 编译并运行

    gcc -pthread Client2.c -o Client2
    ./Client2 192.168.43.161
    
  5. 运行结果如下

    ① 客户端

    C语言socket编程实例_第2张图片

    ② 服务端

    C语言socket编程实例_第3张图片

四、简单的 IPv6 UDP socket编程

  1. 树莓派Ubuntu下查看自己的IPv6地址地址,ifconfig,然后再看双方能不能ping

    C语言socket编程实例_第4张图片
  2. 树莓派下新建一个Server4.c文件,命令nano Server4.c,然后写入如下内容

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
     
    int main(int argc, char **argv)
    {
    	struct sockaddr_in6 s_addr;
    	struct sockaddr_in6 c_addr;
    	int sock;
    	socklen_t addr_len;
    	int len;
    	char buff[128];
    	char buf_ip[128];
     
    	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
    		perror("socket");
    		exit(errno);
    	} else
    		printf("create socket.\n\r");
     
    	memset(&s_addr, 0, sizeof(struct sockaddr_in6));
    	s_addr.sin6_family = AF_INET6;
     
    	if (argv[2])
    		s_addr.sin6_port = htons(atoi(argv[2]));
    	else
    		s_addr.sin6_port = htons(4444);
     
    	if (argv[1])
    		inet_pton(AF_INET6, argv[1], &s_addr.sin6_addr);
    	else
    		s_addr.sin6_addr = in6addr_any;
     
    	if ((bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr))) == -1) {
    		perror("bind");
    		exit(errno);
    	} else
    		printf("bind address to socket.\n\r");
     
    	addr_len = sizeof(c_addr);
    	while (1) {
    		len = recvfrom(sock, buff, sizeof(buff) - 1, 0,
    					   (struct sockaddr *) &c_addr, &addr_len);
    		if (len < 0) {
    			perror("recvfrom");
    			exit(errno);
    		}
     
    		buff[len] = '\0';
    		printf("receive from %s: buffer:%s\n\r",
    				inet_ntop(AF_INET6, &c_addr.sin6_addr, buf_ip, sizeof(buf_ip)), 
    				buff);
    	}
    	return 0;
    }
    
  3. 编译运行

    gcc Server4.c -o server4
    ./server4 2409:8960:1e48:f1a:b6a4:81d5:7021:4873 9090
    
  4. Ubuntu下,新建一个Client4.c文件,然后写入如下内容

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
     
    int main(int argc, char **argv)
    {
    	struct sockaddr_in6 s_addr;
    	int sock;
    	int addr_len;
    	int len;
    	char buff[128];
     
    	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
    		perror("socket");
    		exit(errno);
    	} else
    		printf("create socket.\n\r");
     
    	s_addr.sin6_family = AF_INET6;
    	if (argv[2])
    		s_addr.sin6_port = htons(atoi(argv[2]));
    	else
    		s_addr.sin6_port = htons(4444);
     
    	if (argv[1])
    		inet_pton(AF_INET6, argv[1], &s_addr.sin6_addr);
    	else {
    		printf("usage:./command ip port\n");
    		exit(0);
    	}
     
    	addr_len = sizeof(s_addr);
    	strcpy(buff, "hello i'm here");
    	len = sendto(sock, buff, strlen(buff), 0,
    				 (struct sockaddr *) &s_addr, addr_len);
    	if (len < 0) {
    		printf("\n\rsend error.\n\r");
    		return 3;
    	}
     
    	printf("send success.\n\r");
    	return 0;
    }
    
  5. 编译运行

    gcc Client4.c -o Client4
    ./Client4 2409:8960:1e48:f1a:b6a4:81d5:7021:4873 9090
    
  6. 运行结果如下

    ① 客户端

    ② 服务端

  7. wireshark抓包

    C语言socket编程实例_第5张图片

四、参考书籍和

  • Ubuntu 与树莓派之间的两个 socket 应用实例:C/S 和多人聊天
  • 《网络编程技术》
  • 简单的IPv6 UDP/TCP socket编程 – 两台Linux实现简单的ipv6通信

你可能感兴趣的:(网络,socket,c语言)