本文實現一個簡單的UDP小例子,來說明Linux下socket程式設計之UDP的簡單實現。本文主要包括三個部分:伺服器端的實現,客服端的實現和通訊測試。實現的功能:客服端傳送一條訊息給伺服器端,伺服器端把客服端發來的訊息給顯示出來。

一、伺服器端的實現

1、開啟一個socket使用者伺服器

/* 1、開啟一個socket */
iSocketServerFd = socket(AF_INET, SOCK_DGRAM, 0);	// 網路型別為ipv4,連結型別為udp
2、將上面開啟的socket與伺服器進行繫結
/* 2、繫結 */
/* 2.1 設定要繫結的伺服器端 */
tSocketServerAddr.sin_family      = AF_INET;			// 網路型別為ipv4
tSocketServerAddr.sin_port        = htons(SOCKET_PORT);	// 設定伺服器埠
tSocketServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);	
memset(tSocketServerAddr.sin_zero, 0, 8);

iSockAddrLen = sizeof(struct sockaddr);

/* 2.2 對伺服器端進行繫結 */
iRet = bind(iSocketServerFd, (const struct sockaddr *)&tSocketServerAddr, iSockAddrLen);
3、接收客服端傳送過來的資料
/* 3、接收資料 */
iRecvLen = recvfrom(iSocketServerFd, ucRecvBuf, ARRAY_LENGTH, 0,
                    (struct sockaddr *)&tSocketClientAddr, &iSockAddrLen);
伺服器端實現的完整程式碼如下所示:
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>


#define SOCKET_PORT		1234		// 定義socket繫結的埠
#define LISTEN_BACKLOG	10			// 設定伺服器最大的監聽數量
#define ARRAY_LENGTH	256			// 定義緩衝區的大小

/* 
 * 接收客服端傳送過來的資料,並把它們打印出來
 */
int main(void)
{
	int iSocketServerFd;	// 定義伺服器端的socket檔案描述符
	int iSocketClientFd;	// 定義客服端的socket檔案描述符

	struct sockaddr_in tSocketServerAddr;	
	struct sockaddr_in tSocketClientAddr;

	int iSockAddrLen;	
	int iRet;

	unsigned char ucRecvBuf[ARRAY_LENGTH];	// 定義接收緩衝區
	int iRecvLen;

	signal(SIGCHLD,SIG_IGN); 

	/* 1、開啟一個socket */
	iSocketServerFd = socket(AF_INET, SOCK_DGRAM, 0);	// 網路型別為ipv4,連結型別為udp
	if(iSocketServerFd == -1)
	{
		printf("socket error!\n");
		return -1;
	}

	/* 2、繫結 */
	/* 2.1 設定要繫結的伺服器端 */
	tSocketServerAddr.sin_family      = AF_INET;			// 網路型別為ipv4
	tSocketServerAddr.sin_port        = htons(SOCKET_PORT);	// 設定伺服器埠
	tSocketServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);	
	memset(tSocketServerAddr.sin_zero, 0, 8);

	iSockAddrLen = sizeof(struct sockaddr);

	/* 2.2 對伺服器端進行繫結 */
	iRet = bind(iSocketServerFd, (const struct sockaddr *)&tSocketServerAddr, iSockAddrLen);
	if(iRet == -1)
	{
		printf("bind error!\n");
		return -1;
	}

	while(1)
	{
		/* 3、接收資料 */
		iRecvLen = recvfrom(iSocketServerFd, ucRecvBuf, ARRAY_LENGTH, 0,
                        (struct sockaddr *)&tSocketClientAddr, &iSockAddrLen);
		if(iRecvLen <= 0)	// 失敗
		{
			printf("recvfrom error!\n");
			close(iSocketServerFd);
			return -1;
		}
		else	// 成功,將接收到的資料打印出來
		{
			ucRecvBuf[iRecvLen] = '\0';
			printf("Receive a message from : %s\n", inet_ntoa(tSocketClientAddr.sin_addr));
			printf("The message is : %s\n", ucRecvBuf);
		}
	}

	close(iSocketServerFd);
	return 0;
}
二、客服端的實現
客服端的實現總體上來說分為兩種方式:一種是先和伺服器端進行連結然後在進行通訊,另一種是直接進行通訊。

2.1 先連線再通訊

a、開啟一個socket用於客服端

/* 1、開啟一個socket用於客服端 */
iSocketClientFd = socket(AF_INET, SOCK_DGRAM, 0);
b、連線
/* 2、connect */
/* 2.1 設定連結的伺服器端的基本資訊 */
tSocketServerAddr.sin_family      = AF_INET;			// 網路型別為ipv4
tSocketServerAddr.sin_port        = htons(SOCKET_PORT);	// 伺服器端的網路埠
iRet = inet_aton(argv[1], &tSocketServerAddr.sin_addr);	// 客服端的ip地址
if(iRet == 0)
{
	printf("server ip is not valid!\n");
	return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8);

iSockAddrLen = sizeof(struct sockaddr);

/* 2.2 將伺服器端和客服端連結起來 */
iRet = connect(iSocketClientFd, (const struct sockaddr *)&tSocketServerAddr, iSockAddrLen);
c、傳送資料
/* 3、將使用者的輸入傳送給伺服器端 */
iSendLen = send(iSocketClientFd, ucSendBuf, strlen(ucSendBuf), 0);
完整的程式碼如下:
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SOCKET_PORT		1234	// 設定伺服器端的埠
#define ARRAY_LENGTH	256		// 設定緩衝區的大小

/*
 *	輸入一個字串,傳送給伺服器端
 */
int main(int argc, char **argv)
{
	int iSocketClientFd;	// 描述客服端socket的檔案描述符

	struct sockaddr_in tSocketServerAddr;

	int iSockAddrLen;
	int iSendLen;
	unsigned char ucSendBuf[ARRAY_LENGTH];	// 定義一個緩衝區
	int iRet;

	if(argc != 2)
	{
		printf("Usage :\n");
		printf("%s <server_ip>\n", argv[0]);
		return -1;
	}

	/* 1、開啟一個socket用於客服端 */
	iSocketClientFd = socket(AF_INET, SOCK_DGRAM, 0);
	if(iSocketClientFd == -1)
	{
		printf("socket error!\n");
		return -1;
	}

	/* 2、connect */
	/* 2.1 設定連結的伺服器端的基本資訊 */
	tSocketServerAddr.sin_family      = AF_INET;			// 網路型別為ipv4
	tSocketServerAddr.sin_port        = htons(SOCKET_PORT);	// 伺服器端的網路埠
	iRet = inet_aton(argv[1], &tSocketServerAddr.sin_addr);	// 客服端的ip地址
	if(iRet == 0)
	{
		printf("server ip is not valid!\n");
		return -1;
	}
	memset(tSocketServerAddr.sin_zero, 0, 8);

	iSockAddrLen = sizeof(struct sockaddr);

	/* 2.2 將伺服器端和客服端連結起來 */
	iRet = connect(iSocketClientFd, (const struct sockaddr *)&tSocketServerAddr, iSockAddrLen);
	if(iRet == -1)
	{
		printf("connect error!\n");
		return -1;
	}

	printf("Input a message to %s: \n", argv[1]);
	while(1)
	{
		if(fgets(ucSendBuf, ARRAY_LENGTH, stdin))	// 接收使用者的輸入
		{
			/* 3、將使用者的輸入傳送給伺服器端 */
			iSendLen = send(iSocketClientFd, ucSendBuf, strlen(ucSendBuf), 0);
			if(iSendLen <= 0)
			{
				printf("send error!\n");
				close(iSocketClientFd);
				return -1;
			}
		}
	}

	close(iSocketClientFd);
	return 0;
}

2.2 直接進行通訊
a、開啟socket
/* 1、開啟客服端的socket */
iSocketClientFd = socket(AF_INET, SOCK_DGRAM, 0);	// 網路型別為ipv4, 傳輸型別為udp
b、傳送資料
/* 2、強使用者端的輸入傳送給伺服器端 */
iSendLen = sendto(iSocketClientFd, ucSendBuf, strlen(ucSendBuf), 0,
               (const struct sockaddr *)&tSocketServerAddr, iSockAddrLen);
完整的程式碼實現如下:
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SOCKET_PORT		1234	// 設定伺服器端的網路埠
#define ARRAY_LENGTH	256		// 設定緩衝區的大小

/*
 *	從使用者端輸入一個字串,並把這個字串傳送給客服端
 */
int main(int argc, char **argv)
{
	int iSocketClientFd;	// 定義一個表示客服端socket的檔案描述符

	struct sockaddr_in tSocketServerAddr;

	int iSockAddrLen;
	int iSendLen;
	unsigned char ucSendBuf[ARRAY_LENGTH];	// 定義緩衝區
	int iRet;

	if(argc != 2)
	{
		printf("Usage :\n");
		printf("%s <server_ip>\n", argv[0]);
		return -1;
	}

	/* 1、開啟客服端的socket */
	iSocketClientFd = socket(AF_INET, SOCK_DGRAM, 0);	// 網路型別為ipv4, 傳輸型別為udp
	if(iSocketClientFd == -1)
	{
		printf("socket error!\n");
		return -1;
	}

	/* 設定要連結的伺服器端的基本資訊 */
	tSocketServerAddr.sin_family      = AF_INET;			// 網路型別為ipv4
	tSocketServerAddr.sin_port        = htons(SOCKET_PORT);	// 伺服器端的網路埠
	iRet = inet_aton(argv[1], &tSocketServerAddr.sin_addr);	// 要連線的客服端的ip地址
	if(iRet == 0)
	{
		printf("server ip is not valid!\n");
		return -1;
	}
	memset(tSocketServerAddr.sin_zero, 0, 8);

	iSockAddrLen = sizeof(struct sockaddr);

	printf("Input a message to %s: \n", argv[1]);
	while(1)
	{
		if(fgets(ucSendBuf, ARRAY_LENGTH, stdin))	// 接收使用者端的輸入
		{
			/* 2、強使用者端的輸入傳送給伺服器端 */
			iSendLen = sendto(iSocketClientFd, ucSendBuf, strlen(ucSendBuf), 0,
                      (const struct sockaddr *)&tSocketServerAddr, iSockAddrLen);
			if(iSendLen <= 0)
			{
				printf("sendto error!\n");
				close(iSocketClientFd);
				return -1;
			}
		}
	}

	close(iSocketClientFd);
	return 0;
}

三、測試
將上面兩種方式實現的客服端都和伺服器進行通訊測試。伺服器端和客服端可以在同一臺電腦上也可以在不同的電腦上(必須在同一區域網當中),本文是在同一臺電腦上對客服端和伺服器端進行測試,測試結果如下:

客服端一:



客服端二:



伺服器端:



從上面結果可以看出,伺服器和兩個客服端都通訊成功。