udp编程_udp编程实例golang - CSDN
精华内容
参与话题
  • UDP socket 编程示例

    千次阅读 2018-09-09 19:14:11
    一、UDP socket 编程模型   二、使用UDP协议实现一个简单的通讯程序 1.服务器端 #include<stdlib.h> #include<stdio.h> #include<sys/socket.h> #include<...

    本代码根据老师提供的tcp示例改写

    一、UDP socket 编程模型

        

    二、使用UDP协议实现一个简单的通讯程序

    1.服务器端

    #include<stdlib.h>
    #include<stdio.h>
    #include<sys/socket.h>
    #include<netdb.h>
    #include<string.h>
    #include<unistd.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    
    
    
    void print_usage(char * cmd)
    {
    	fprintf(stderr," %s usage:\n",cmd);
    	fprintf(stderr,"%s  [port]\n",cmd);
    
    }
    
    int main(int argc,char** argv)
    {
    	struct sockaddr_in server;
    	struct sockaddr_in client;
    	int sockfd;
    	int len;
    	int port;
    	int sendnum;
    	int recvnum;
    	int opt;
    	char send_buf[2048];
    	char recv_buf[2048];
    	int addr_len = sizeof(struct sockaddr_in);
    	if ((1>argc)|| (argc >2))
    	{
    		print_usage(argv[0]);
    		exit(1);
    
    	}
    	if (2==argc) 
    	{
    		port = atoi(argv[1]);
    	}
    	memset(send_buf,0,2048);
    	memset(recv_buf,0,2048);
    	opt=SO_REUSEADDR;
    	if (-1==(sockfd=socket(AF_INET,SOCK_DGRAM,0)))
    	{
    		perror("create socket error\n");
    		exit(1);
    	}
    	memset(&server,0,sizeof(struct sockaddr_in));
    	server.sin_family = AF_INET;
       	server.sin_addr.s_addr = htonl(INADDR_ANY);
    	server.sin_port = htons(port);
    	if (-1==bind(sockfd,(struct sockaddr *)&server,sizeof(struct sockaddr)))
    	{
    		perror("bind error\n");
    		exit(1);
    	}
    	while (1)
    	{
    
            	if (0>(recvnum = recvfrom(sockfd,recv_buf,sizeof(recv_buf),0,(struct sockaddr *)&client,&addr_len)))
    		{
    			perror("recv error\n");
    			//close(sockfd);
    			continue;
    		}
    		recv_buf[recvnum]='\0';
    
    		printf ("the message from the client is: %s\n",recv_buf);
    	
    		if (0==strcmp(recv_buf,"quit"))
            	{
    			perror("the client break the server process\n");
    			close(sockfd);
    			break;
    		}
    
    		sendnum = sprintf(send_buf,"The message from client is %s\n",recv_buf);
      		sendto(sockfd,send_buf,sizeof(send_buf),0,(struct sockaddr *)&client,sizeof(client));
    		continue;
    
    	}
    	close(sockfd);
    	return 0;
    }

    2.客户端

    #include<stdlib.h>
    #include<stdio.h>
    #include<sys/socket.h>
    #include<netdb.h>
    #include<string.h>
    #include<unistd.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    
    #define PORT 8900
    
    
    void print_usage(char * cmd)
    {
    	fprintf(stderr," %s usage:\n",cmd);
    	fprintf(stderr,"%s IP_Addr [port]\n",cmd);
    
    }
    
    
    int main(int argc,char** argv)
    {
    	struct sockaddr_in server;
    	int ret;
    	int len;
    	int port;
    	int sockfd;
    	int sendnum;
    	int recvnum;
    	char send_buf[2048];
    	char recv_buf[2048];
    	int addr_len = sizeof(struct sockaddr_in);
    	if ((2>argc)|| (argc >3))
    	{
    		print_usage(argv[0]);
    		exit(1);
    
    	}
    	if (3==argc) 
    	{
    		port = atoi(argv[2]);
    	}
    	if (-1==(sockfd=socket(AF_INET,SOCK_DGRAM,0)))
    	{
    		perror("can not create socket\n");
    		exit(1);
    	}
    
    	memset(&server,0,sizeof(struct sockaddr_in));
    	server.sin_family = AF_INET;
    	server.sin_addr.s_addr = inet_addr(argv[1]);
    	server.sin_port = htons(port);
    
    	if (0>(ret=connect(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr))))
    	{
    		perror("connect error");
    		close(sockfd);
    		exit(1);
    
    	}
    
    	//memset(send_buf,0,2048);
    	//memset(recv_buf,0,2048);
    	while(1)
    	{
    		printf("what words do  you want to tell to server:\n");
    		//gets(send_buf);
    		fgets(send_buf,2048,stdin);
    		//sprintf(send_buf,"i am lg,thank for your servering\n");
    		if (0>(len=sendto(sockfd,send_buf,sizeof(send_buf),0,(struct sockaddr *)&server,sizeof(server))))
    		{
    			perror("send data error\n");
    			close(sockfd);
    			exit(1);
    		}
    		if (0>(len=recvfrom(sockfd,recv_buf,sizeof(recv_buf),0,(struct sockaddr *)&server,&addr_len)))
    		{
    			perror("recv data error\n");
    			close(sockfd);
    			exit(1);
    		}
    		recv_buf[len]='\0';
    		printf("the message from the server is:%s\n",recv_buf);		
    	}
    	close(sockfd);
    }

    三、运行结果

        

    展开全文
  • TCP/UDP的Socket编程接口详解

    万次阅读 2018-06-23 16:08:14
    基于TCP(面向连接)的socket编程,分为客户端和服务器端。服务器端的流程如下:(1)创建套接字(socket)(2)将套接字绑定到一个本地地址和端口上(bind)(3)将套接字设为监听模式,准备接收客户端请求(listen...

    ---- socket概述:

    socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

    socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

    ---- 接口简介:

    socket():创建socket

    bind():绑定socket到本地地址和端口,通常由服务端调用

    listen():TCP专用,开启监听模式

    accept():TCP专用,服务器等待客户端连接,一般是阻塞态

    connect():TCP专用,客户端主动连接服务器

    send():TCP专用,发送数据

    recv():TCP专用,接收数据

    sendto():UDP专用,发送数据到指定的IP地址和端口

    recvfrom():UDP专用,接收数据,返回数据远端的IP地址和端口

    closesocket():关闭socket

    ---- 流程如下:

            

    接口详解,常用的系统调用如下:

    >> socket() : creating  a socket 

    A socket is an abstraction of a communication endpoint. Just as they would use file descriptors to access files, applications use socket descriptors to access sockets. To create a socket, we call the socket() function.

    原型:int socket(int domain, int type, int protocol);

    返回值: returns file (socket) descriptor if OK, -1 on error.

    domain:AF_INET, AF_INET6, AF_UNIX, AF_UNSPEC (address format)

    type:SOCK_DGRAM, SOCK_RAW, SOCK_STREAM, SOCK_SEQPACKET

    protocol:IPPROTO_IP,  IPPROTO_IPV6,  IPPROTO_TCP, IPPROTO_UDP

    The protocol argument is usually zero, to select the default protocol for the given domain and socket type. The default protocol for a SOCK_STREAM socket in the AF_INET communication domain is TCP(Transmission Control Protocol). The default protocol for a SOCK_DGRAM socket in the AF_INET communication domain is UDP(User Datagram Protocol).

    NOTE: UDP -- 数据报(datagram),无连接的,no logical connection exist between peers for them to communicate. A datagram is a self-contained(独立的) message. 类似于(analogous)发邮件,你可以发送多封邮件,但是不能保证邮件是否到达和邮件到达的顺序。每一封邮件都包含了接收者的地址。

    TCP -- 字节流 A byte stream(SOCK_STREAM), in contrast, 在传输数据之前,需要建立连接,面向连接的通信类似于打电话。点到点的连接里包含了source and destination。

    Communication on a socket is bidirectional. We can disable I/O on a socket with the shutdown function.

    >> shutdown()   

    原型:int shutdown(int sockfd, int how);

    返回值: returns 0 if OK, -1 on error.

    The shutdown() system call closes one or both channels of the socket sockfd, depending on the value of how, which is specified as one of the following:

    howSHUT_RD, then reading from the socket is disabled.  SHUT_WR, then we can't use the socket for transmitting data. We can use SHUT_RDWR to disable both data transmission and reception.

    shutdown() differs from close() in another important respect: it closes the socket channels regardless of whether there are other file descriptors referring to the socket. For example, sockfd refers to a connected stream socket. If we make the following calls, then the connection remains open, and we can still perform I/O on the connection via the file descriptor fd2:

        fd = dup(sockfd);
        close(sockfd);

    However, if we make the following sequence of calls, then both channels of the connection are closed, and I/O can no longer be performed via fd2:

        fd2 = dup(sockfd);
        shutdown(sockfd,SHUT_RDWR);

    Note that shutdown() doesn't close the file descriptor, even if how is specified as SHUT_RDWR. To close the file descriptor, we must additionally call close().

    >> bind() : binding a socket to an address    

    The bind() system call binds a socket to an address.

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

    返回值:returns 0 on success, or -1 on error.

    The sockfd argument is a file descriptor obtained from a previous call to socket(). The addr argument is a pointer to a structure specifying the address to which this socket is to be bound. The type of structure passed in this argument depends on the socket domain. The addrlen argument specifies the size of the address structure.

    Typically, we bind a server's socket to a well-known address - that is, a fixed address that is known in advance to client applications that need to communicate with that server.

    >> listen() : listening for incoming connections    

    原型:int listen(int sockfd, int backlog); // returns 0 on success, or -1 on error.

    The listen() system call marks the stream socket referred to by the file descriptor sockfd as passive. The socket will subsequently be used to accept connections from other(active) sockets.

    The client may call connect() before the server calls accept(). This could happen, for example, because the server is busy handling some other clients. This results in a pending connection, as illustrated in Figure 56-2.

                 

    The kernel must record some information about each pending connection request so that a subsequent accept() can be processed. The backlog argument allows us to limit the number of such pending connections. Further connection requests block until a pending connection is accepted(via accept()), and thus removed from the queue of pending connections.

    >> accept() : accepting a connection   

    The accept() system call accepts an incoming connection on the listening stream socket referred to by the file descriptor sockfd. If there are no pending connections when accept() is called, the call blocks until a connection request arrives when the sockfd in block mode. If sockfd is in nonblocking mode, accept() will return -1 and set errno to either EAGAIN or EWOULDBLOCK.

    原型:int accept(int sockfd, struct sockaddr * restrict addr, socklen_t * restrict len);

    返回值:return file(socket) descriptor if OK, -1 on error.

    本函数从s的等待连接队列中抽取第一个连接,创建一个与s同类的新的套接口并返回句柄。如果队列中无等待连接,且套接口为阻塞方式,则accept()阻塞调用进程直至新的连接出现。如果套接口为非阻塞方式且队列中无等待连接,则accept()返回一错误代码WSAEWOULDBLOCK。已接受连接的套接口不能用于接受新的连接,原监听套接口仍保持开放。

    The key point to understand about accept() is that it creates a new socket, and this new socket that is connected to the peer socket that performed the connect(). This new socket descriptor has the same socket type and address family as the  original socket(sockfd). A file descriptor for the connected socket is returned as the function result of the accept() call. The listening socket(sockfd) remains open, and can be used to accept further connections. A typical server application creates one listening socket, binds it to a well-known address, and then handles all client requests by accepting connections via that socket.

    The remaining(剩余的) arguments to accept() return the address of the peer socket.(客户端)

    If we don't care about the client's identity, we can set the addr and len parameters to NULL. Otherwise, before calling accept, we need to set the addr (指向一个buffer) parameter to a buffer large enough to hold the address and set the integer pointed to by len to the size of the buffer in bytes. On return, accept will fill in the client's address in the buffer and update the integer pointed to by len to reflect the size of the address.

    >> connect() : connecting to a peer socket  

    原型:int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);

    返回值: returns 0 on success, or -1 on error.

    The connect() system call connects the active socket referred to by the file descriptor sockfd to the listening socket whose address is specified by addr and addrlen.

    >> send() : TCP类型的数据发送 

    原型:int send(int sockfd, const void * msg, int len, int flags);

    每个TCP套接口都有一个发送缓冲区,它的大小可以用SO_SNDBUF这个选项来改变。调用send函数的过程实际是内核将用户数据(msg)拷贝至TCP套接口的发送缓冲区的过程。若len大于发送缓冲区的大小,则返回-1. 否则,查看缓冲区剩余空间是否容纳得下要发送的len长度,若不够,则拷贝一部分,并返回拷贝长度(指的是非阻塞send,若为阻塞send,则一定等待所有数据拷贝至缓冲区才返回,因此阻塞send返回值必定与len相等)。若缓冲区满,则等待发送,有剩余空间后拷贝至缓冲区,若在拷贝过程中出现错误,则返回-1.关于错误的原因,查看errno的值。

    注意:send成功返回并不代表对方已接收到数据,如果后续的协议传输过程中出现网络错误,下一个send便会返回-1发送错误。TCP给对方的数据必须在对方给予确认时,方可删除发送缓冲区的数据。否则,会一直缓存到缓冲区直至发送成功。

    参数解释:

    sockfd -- 发送端套接字描述符 (非监听描述符)

    msg -- 待发送数据的缓冲区 (将其内容的len长度拷贝到socket的发送缓冲区)

    len -- 待发送数据的字节长度。

    flags -- 一般情况下置为0.


    >> recv() : TCP类型的数据接收 

    原型:int recv(int sockfd, void *buf, int len, unsigned int flags);

    recv()从接收缓冲区拷贝数据。成功时,返回拷贝的字节数,失败时,返回-1。阻塞模式下,recv()将会阻塞直到缓冲区中至少有一个字节才返回,没有数据时处于休眠状态。若非阻塞,则立即返回,有数据则返回拷贝的数据大小,否则返回错误-1.

    参数解释:

    sockfd -- 接收端套接字描述符(非监听描述符)

    buf -- 接收数据的基地址(将socket的接收缓冲区中的内容拷贝至buf中)

    len -- 接收到数据的字节数

    flags -- 一般情况下置为0.

    >> sendto() : UDP类型的数据发送 

    原型:int sendto(int sockfd, const void * msg, int len, unsigned int flags, const struct sockaddr * dst_addr, int addrlen);

    用于非可靠连接(UDP)的数据发送,因为UDP方式未建立连接socket,因此需要指定目的socket的address。

    可使用同一个UDP套接口描述符sockfd和不同的目的地址通信。而TCP要预先建立连接,每个连接都会产生不同的套接口描述符,体现在:客户端要使用不同的fd进行connect,服务端每次accept产生不同的fd。

    UDP没有真正的发送缓冲区,因为是不可靠连接,不必保存应用进程的数据拷贝,应用进程的数据在沿协议栈向下传递时,以某种形式拷贝到内核缓冲区,当数据链路层把数据传出后就把内核缓冲区中数据拷贝删除。因此它不需要一个发送缓冲区。

    For sendto(), the dest_addr and addrlen arguments specify the socket to which the datagram is to be sent. These arguments are employed in the same manner as the corresponding arguments to connect(). The dest_addr argument is an address structure suitable for this communication domain. It is initialized with the address of the destination socket. The addrlen argument specifies the size of addr.


    >> recvfrom() : UDP类型的数据接收 

    原型:int recvfrom(int sockfd, void * buf, size_t len, int flags, struct sockaddr * src_addr, int * addrlen);

    参数解释:

    sockfd -- 接收端套接字描述;

    buf -- 用于接收数据的应用缓冲区地址;

    len -- 指明缓冲区大小;

    flags -- 通常为0;

    src_addr -- 数据来源端的地址(IP address,Port number).

    fromlen -- 作为输入时,fromlen常常设置为sizeof(struct sockaddr).

    For recvfrom(), the src_addr and addrlen arguments return the address of the remote socket used to send the datagram. (These arguments are analogous to the addr and addrlen arguments of accept(), which return the address of a connecting peer socket.) Prior to the call(在调用之前), addrlen should be initialized to the size of the structure pointed to by src_addr(结构的大小); upon return(在返回时), it contains the number of bytes actually written to this structure.

    展开全文
  • UDP编程步骤

    2018-11-19 18:45:53
    UDP提供的是无连接、不可靠的数据报服务。 在传输过程中数据可能会丢失。我们只有通过在应用层进行正确的控制才能修复在传输层上存在的缺陷。因此,需要我们编写可靠的UDP应用程序。 UDP客户端与服务器交互的...

    1. 概述

    UDP提供的是无连接、不可靠的数据报服务。

    在传输过程中数据可能会丢失。我们只有通过在应用层进行正确的控制才能修复在传输层上存在的缺陷。因此,需要我们编写可靠的UDP应用程序。

    UDP客户端与服务器交互的步骤如图:

    标题UDP客户端与服务器交互步骤及涉及到的系统调用

            使用UDP协议进行通信时,客户端并不需要与服务器建立连接,只需要通过sendto系统调用给指定的服务器发送数据报。相同地,服务器也不需要监听或接收客户端的连接,只需要通过recvfrom系统调用等待客户端发送数据报。

            sendto函数与recvfrom函数都是阻塞的!

    2. UDP应用程序的编写步骤

    基本步骤如下:

    • 创建套接字
    • 命名套接字(服务器端)
    • 服务器端,等待客户端发来数据
    • 客户端,向服务器端发送数据报
    • 关闭套接字

    步骤一:创建socket

    使用系统调用socket创建一个套接字并返回这个套接字的文件描述符sockfd

    #include<sys/socket.h>
    int socket(int domain, int type, int protocol);

    一个套接字为一条通信线路的一个端点。

    domain 
    domain参数指定哪种协议族,常见的协议族有  AF_INET、AF_INET6 和 AF_UNIX 。AF_UNIX 用于通过文件系统实现的本地套接字,AF_INET 用于网络套接字。

    type 
    type参数指定服务类型,即 SOCK_STREAM 和 SOCK_DGRAM。 
    SOCK_STREAM 为流套接字,基于 TCP,提供可靠,有序的服务。 
    SOCK_DGRAM 为数据报套接字,基于 UDP,提供不可靠,无序的服务。

    protocol 

    prorocol参数指定具体的协议,通常前两个参数就可以确定具体的协议,所以我们把这个参数默认设置为0。

    步骤二:命名socket

    要想让创建的套接字可以被其他进程使用,那必须给该套接字命名。对套接字命名的意思是指将该套接字关联一个IP地址和端口号,使用系统调用bind来实现。

    #include <sys/socket.h>
    int bind(int socket, const struct sockaddr *address, size_t address_len);

    bind系统调用把参数address中的地址分配给与文件描述符socket关联的套接字,address_len为address地址结构的长度。

     

    每种套接字都有自己的地址格式,对于AF_INET地址族,套接字地址由socket_in来指定。

    
    struct sockaddr_in
    {
        sa_family_t sin_family; //协议族
        u_int16_t sin_port; //端口号
        struct in_addr sin_addr; //IP地址
    };
     
    struct in_addr
    {
        u_int32_t s_addr;
    };

    对于客户套接字,我们一般不需要指定套接字的端口号,而对于服务器套接字,我们需要指定套接字的端口号以便让客户正确向服务器发送数据。

    步骤三:服务器接收客户端消息

    UDP 是无连接不可靠的数据报协议。服务器不接收来自客户的连接,而只管调用recvfrom系统调用,等待客户的数据到达。recvfrom的声明如下:

    #include <sys/socket.h>
    int recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *src_addr, socklen_t *src_len);

    recvfrom的参数说明:

    - socket:创建的套接字描述符 
    - buffer:指向输入缓冲区的指针 
    - length:缓冲区大小 
    - flags:在本文中,可以将 flags 置为0即可 
    - src_addr:指向客户套接字地址的指针 
    - src_len:地址长度 

    recvfrom的返回值为读入数据的长度。

    步骤四:客户端向服务器发送消息

    UDP是无连接的,客户端可以直接向服务器发送消息而不需要建立连接。使用sendto系统调用向服务器发送消息。函数定义如下:

    #include <sys/socket.h>
    int sendto(int socket, const void *buffer, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
    

    sendto函数的参数说明:

    -socket:创建的套接字描述符

    -buffer:输出缓冲区的指针

    -length:缓冲区大小

    -flags:正常应用中,flags一般设置为0

    -dest_addr:指向服务器套接字地址的指针

    -dest_len:地址长度

    步骤五:关闭socket

    操作系统为每个套接字分配了一个文件描述符,使用完后需要关闭使操作系统回收该文件描述符,使用 close 系统调用。

    #include<sys/socket.h>
    int close(int sockfd);

    3. 代码段

    服务器端:

    int main()
    {
    	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    	assert(sockfd != 0);
    	struct sockaddr_in ser, cli;
    	memset(&ser, 0, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(6500);
    	ser.sin_addr.s_addr = inet_addr("127.0.0.1"); //回环地址
    	int res = bind(sockfd, (struct sockaddr*)&ser, sizeof(ser));
    	assert(res != -1);
    	while(1)
    	{
    		char recvbuf[128] = {0};
    		int len = sizeof(cli);
    		int n = recvfrom(sockfd, recvbuf, 127, 0, (struct sockaddr*)&cli, &len);
    		if(n <= 0)
    		{
    			printf("recvfrom error\n");
    			continue;
    		}
    		printf("recvbuff: %s\n", recvbuf);
    		sendto(sockfd, "OK", 2, 0, (struct sockaddr*)&cli, sizeof(cli));
    	}
    	close(sockfd);
    }

    客户端:

    int main(int argc, char *argv[])
    {
    	if(argc <3)
    	{
    		printf("please choose server's IP && port\n");
    		exit(0);
    	}
    	int port = 0;
    	sscanf(argv[2], "%d", &port);
    	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    	assert(sockfd != -1);
    	struct sockaddr_in ser, cli;
    	memset(&ser, 0, sizeof(ser));	
    	ser.sin_family = AF_INET;
    	ser.sin_addr.s_addr = inet_addr(argv[1]);
    	ser.sin_port = htons(port);
    	while(1)
    	{
    		printf("please input data: ");
    		fflush(stdout);
    		char buff[128] = {0};
    		fgets(buff, 128, stdin);
    		buff[strlen(buff) - 1] = 0;
    		if(strncmp(buff, "end", 3) == 0)
    		{
    			close(sockfd);
    			break;
    		}
    		sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr*)&ser, sizeof(ser));
    		memset(buff, 0, 128);
    		recvfrom(sockfd, buff, 127, 0, NULL, NULL);
    		printf("%s\n", buff);
    	}
    	close(sockfd);
    }

    4.运行结果

     

    展开全文
  • UDP编程

    千次阅读 2019-06-17 14:32:39
    测试命令 #windows查找udp是否启动端口 > netstat -anp udp | find "9999" ...UDP服务端编程 创建socket对象。socket.SOCK_DGRAM 绑定IP和Port,bind()方法 传输数据 接收数据,socket.recvfrom(bufsize[,...
    测试命令
    #windows查找udp是否启动端口
    > netstat -anp udp | find "9999"
    > netstat -anbp udp | findstr 9999
    

    UDP服务端编程

    在这里插入图片描述

    • 创建socket对象。socket.SOCK_DGRAM

    • 绑定IP和Port,bind()方法

    • 传输数据

      • 接收数据,socket.recvfrom(bufsize[,flags]),获得一个二元组(string,address)
      • 发送数据,socket.sendto(string,address)发给某地址某信息
    • 释放资源

    import socket
    server=socket.socket(type=socket.SOCK_DGRAM)
    server.bind(('172.22.141.176',9999))
    data=server.recv(1024)
    print(data)
    data=server.recvfrom(1024)
    print(data)
    server.sendto(b'110110',('172.22.141.176',10000))
    server.close()
    

    在这里插入图片描述
    在这里插入图片描述

    UDP客户端编程流程

    • 创建socket对象,socket.SOCK_DGRAM
    • 发送数据,socket.sendto(string,address)发送给某地址某信息
    • 接收数据,socket.recvfrom(bufsize[,flags]),获得一个二元组(string,address)
    • 释放资源
    import socket
    client=socket.socket(type=socket.SOCK_DGRAM)
    raddr=('172.22.141.176',11111)
    
    client.connect(raddr)
    client.sendto(b'1008611',raddr)
    client.send(b'132132323')
    data=client.recvfrom(1024)
    print(data)
    data=client.recv(1024)
    print(data)
    
    client.close()
    

    在这里插入图片描述
    注意:UDP是无连接协议,所以可以只有任何一端,例如客户端数据发往服务端,服务端存在与否无所谓。

    UDP编程bind、connect、send、sendto、recv、recvfrom方法使用
    UDP的socket对象创建后,是没有占用本地地址和端口的。

    方法 说明
    bind方法 可以指定本地地址和端口laddr,会立即占用
    connect方法 可以立即占用本地地址和端口laddr,填充远端地址和端口raddr
    sendto方法 可以立即占用本地地址和端口laddr,并发数据发往指定远端,只有有了本地绑定端口,sendto就可以向任何远端发送数据
    send方法 需要和connect方法配合,可以使用已经从本地端口把数据发往raddr指定的远端
    recv方法 要求一定要在占用了本地端口后,返回接收的数据
    recvfrom方法 要求一定要占用本地端口后,返回接收的数据和对端地址的二元组
    • UDP群聊服务端
    import socket
    import threading
    class ChatUdpServer:
        def __init__(self,ip='0.0.0.0',port=11112):
            self.ipaddr=ip,port
            self.sock=socket.socket(type=socket.SOCK_DGRAM)
            self.clients=set()
            self.event=threading.Event()
            self.lock=threading.Lock()
        def start(self):
            self.sock.bind(self.ipaddr)
            threading.Thread(target=self.recv).start()
        def recv(self):
            while not self.event.is_set():
                try:
                    data,other=self.sock.recvfrom(1024)
                    print(data)
                    if data==b'quit':
                        with self.lock:
                            self.clients.remove(other)
                        continue
                    with self.lock:
                        self.clients.add(other)
                    with self.lock:
                        for i in self.clients:
                            self.sock.sendto(data,i)
                except:
                    print('error')
        def stop(self):
            self.event.set()
            self.clients.clear()
            try:
                self.sock.close()
            except:
                print('over')
            finally:
                print('over')
    chat=ChatUdpServer()
    chat.start()
    while True:
        cmd=input(">>>").strip()
        if cmd=='quit':
            chat.stop()
            break
        print(threading.enumerate())
    
    • UDP客户端
    import socket
    import threading
    import time
    class ChatUdpClient:
        def __init__(self,ip='172.22.141.176',port=11113):
            self.sock=socket.socket(type=socket.SOCK_DGRAM)
            self.ipadd=ip,port
            self.event=threading.Event()
            self.lock=threading.Lock()
        def start(self):
            self.sock.connect(self.ipadd)
            threading.Thread(target=self.send).start()
            threading.Thread(target=self.recv).start()
        def send(self):
            while not self.event.is_set():
                with self.lock:
                    data=input("你说话呀:").strip().encode()
                    self.sock.sendto(data,self.ipadd)
        def recv(self):
            while not self.event.is_set():
                message=self.sock.recvfrom(1024)
                print(message)
        def stop(self):
            self.event.set()
            self.sock.close()
    chat=ChatUdpClient()
    chat.start()
    while True:
        time.sleep(10)
        cmd=input('>>>').strip()
        if cmd=='quit':
            chat.stop()
        print(threading.enumerate())
    

    上面的例子并不完善,如果客户端断开了,服务端不知道,每一个服务端还需要对所有客户端发送数据,包括已经断开的客户端

    服务端代码改进

    增加心跳机制heartbeat机制或ack机制,这些机制同样用在TCP通信的时候。
    心跳,就是一端定时发往另一端的信息。
    ack即相应,一端收到另一端的信息后返回的确认信息。
    心跳机制:
    1、一般来说是客户端定时发往服务端的,服务端并不需要ack回复客户端,只需要记录该客户端活着就行了。
    2、如果是服务端定时发往客户端的,一般需要客户端ack相应来表示活着,如果没有收到ack的客户端,服务端移除其信息,这种实现较为复杂,用的较少
    3、双向发心跳

    • 服务器端增加心跳机制
    import socket
    import threading
    import datetime
    class ChatUdpServer:
        def __init__(self,ip='172.22.141.176',port=11113,interval=10):
            self.ipaddr=ip,port
            self.sock=socket.socket(type=socket.SOCK_DGRAM)
            self.clients=dict()
            self.event=threading.Event()
            self.lock=threading.Lock()
            self.interval=interval
        def start(self):
            self.sock.bind(self.ipaddr)
            threading.Thread(target=self.recv).start()
        def recv(self):
            while not self.event.is_set():
                start = datetime.datetime.now().timestamp()
                try:
                    data,other=self.sock.recvfrom(1024)
                    if data.strip()==b'^bbb^':
                        self.clients[other]=start
                        continue
                    elif data==b'quit':
                        with self.lock:
                            self.clients.pop(other)
                        continue
                    with self.lock:
                        self.clients[other]=start
                    with self.lock:
                        fail=set()
                        print(fail)
                        for i,k in self.clients.items():
                            print(self.clients)
                            print(k)
                            print(start)
                            if start-k>self.interval:
                                fail.add(i)
                            else:
                                self.sock.sendto(data,i)
                        for c in  fail:
                            self.clients.pop(c)
                except:
                    print('error')
        def stop(self):
            self.event.set()
            self.clients.clear()
            try:
                self.sock.close()
            except:
                print('over')
            finally:
                print('over')
    chat=ChatUdpServer()
    chat.start()
    while True:
        cmd=input(">>>").strip()
        if cmd=='quit':
            chat.stop()
            break
        print(threading.enumerate())
    
    • 客户端增加心跳机制
    import socket
    import threading
    import time
    class ChatUdpClient:
        def __init__(self,ip='172.22.141.176',port=11113):
            self.sock=socket.socket(type=socket.SOCK_DGRAM)
            self.ipadd=ip,port
            self.event=threading.Event()
            self.lock=threading.Lock()
        def start(self):
            self.sock.connect(self.ipadd)
            threading.Thread(target=self.send).start()
            threading.Thread(target=self.recv).start()
            threading.Thread(target=self.heartbeat).start()
        def heartbeat(self):
            while not self.event.wait(5):
                self.sock.send(b'^^bb^^')
        def send(self):
            while not self.event.is_set():
                with self.lock:
                    data=input("你说话呀:").strip().encode()
                    self.sock.sendto(data,self.ipadd)
        def recv(self):
            while not self.event.is_set():
                message=self.sock.recvfrom(1024)
                print(message)
        def stop(self):
            self.event.set()
            self.sock.close()
    chat=ChatUdpClient()
    chat.start()
    while True:
        time.sleep(10)
        cmd=input('>>>').strip()
        if cmd=='quit':
            chat.stop()
        print(threading.enumerate())
    
    展开全文
  • (一)UDP基本编程步骤

    万次阅读 2017-03-14 16:16:29
    UDP服务端编程分5步: 1.创建 UDP socket套接字,用socket函数。 2.设置socket的属性,用setsockopt()函数,(可选) 3.往socket绑定包含 IP信息,地址信息的 struct sockaddr_in(IPv4)结构体。用bind()函数 4...
  • UDP编程示例

    2019-07-30 17:35:28
    客户端: public class SocketSend { public static void main(String[] args) throws Exception { DatagramSocket ds = null; try { ds = new DatagramSocket();... String data = "UDP测试"...
  • 37 - 网络编程-UDP编程

    2019-03-10 15:54:12
    文章目录1 UDP协议2 UDP通信流程3 UDP编程3.1 构建服务端3.2 构建客户端3.3 常用方法4 聊天室5 UDP协议应用 1 UDP协议 UDP是面向无连接的协议,使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就...
  • UDP编程流程

    千次阅读 2018-11-15 19:46:25
    UDP编程流程: 服务器端:socket(), bind(), recvfrom()/sendto(), close(); 客户端:socket(), sendto()/recvfrom(), close(); 以下是各个函数的具体介绍: 首先提一句:linux下一切皆文件,socket也不...
  • UDP服务器端和客户端程序设计

    万次阅读 多人点赞 2012-02-16 15:55:29
    实验三 UDP服务器端程序设计 一、实验目的 学习和掌握Linux下的UDP服务器基本原理和基本编程方法,体会与TCP的区别,TCP编程:http://blog.csdn.net/yueguanghaidao/article/details/7035248 二、实验平台 ...
  • 1、sendto和recvfrom函数介绍 sendto(经socket传送数据) 相关函数 send , sendmsg,recv , recvfrom , socket 表头文件 #include #include ...int sendto ( int s , const void * msg, int l
  • 基于TCP/UDP的socket编程流程

    千次阅读 2019-01-23 09:09:26
    其实Socket编程也对应于TCP和UDP的通信机制: 这里附上TCP/UDP通信机制的图例,对应他们建立通信的过程,就很容易理解Socket是如何进行通信的了: 图片参考博客:图片博客地址 基于TCP(面向连接)的socket编程的...
  • 基于TCP和UDP的Socket编程的步骤

    千次阅读 2019-07-30 11:10:51
    java为TCP和UDP两种通信协议提供了相应的Socket编程类,这些类存放在java.net包中。与TCP对应的是服务器端的ServerSocket和客户端的Socket;与UDP对应的是DatagramSocket. 基于TCP协议的Socket编程的主要步骤: ...
  • C/C++实现基于UDP的网络编程

    万次阅读 2012-09-12 18:33:45
    在网络编程中,我们一般用UDP或者TCP这两种协议来进行编写网络应用程序,那叫UDP呢? UDPUDP的简称是User Datagram Protocol,中文名是用户数据报协议,是OSI 参考模型中一种无连接的传输层协议,提供面向事务的...
  • Linux C Socket UDP编程介绍及实例

    万次阅读 多人点赞 2016-11-25 15:48:55
    1、UDP网络编程主要流程 UDP协议的程序设计框架,客户端和服务器之间的差别在于服务器必须使用bind()函数来绑定侦听的本地UDP端口,而客户端则可以不进行绑定,直接发送到服务器地址的某个端口地址。框图如图1.3所示...
  • TCP和UDP的最完整的区别

    万次阅读 多人点赞 2016-08-04 11:30:30
    TCP和UDP两种协议的比较汇总
  • 从Linux音频,视频采集,到TCP/IP UDP Socket基础概念,网络编程接口介绍,POSIX线程封装,私有协议定义,开发,服务器模型,客户端编程等详细实战讲解,整个过程,涵盖iOS,Android ,Mac OS嵌入式Linux网络编程核心的...
  • C++UDP组播编程中,组播地址(组播号)与组播地址的关系是什么?和TCP中IP和端口的关系一样吗?另外,一台计算机上(不经过路由器或交换机)只能有一个组播吗?急求!!!
  • C++ UDP socket编程

    千次阅读 2018-10-07 20:13:13
    客户端: //#include "stdafx.h" #include&lt;stdio.h&gt; #include&lt;tchar.h&gt; #include &lt;iostream&gt; #include &lt;...#pragma comment(lib...
  • Windows网络编程UDP通信

    千次阅读 2017-04-09 13:17:42
    实验二 UDP 通信实验通信模型: 1 实验类型 验证型实验 2 实验目的 1. 进一步理解Winsock API 的调用方法 2. 了解UDP 协议的工作原理 3. 掌握UDP 服务端程序和客户端程序的编写流程 4. 熟悉程序的调试方法。 ...
  • (JAVA)基于Socket的TCP和UDP编程

    千次阅读 2016-07-12 18:30:08
    (JAVA)基于Socket的TCP和UDP编程 最近空闲下来了,顺便学习了下Java的网络编程,例子都是来源网络,整理了下,先记录下来,以便后用! 一. TCP 、UDP、Scoket TCP: Transmission Control Protocol UDP: User ...
1 2 3 4 5 ... 20
收藏数 109,871
精华内容 43,948
关键字:

udp编程