linux下socket編程

  • Socket

   soket接口是TCP/IP網絡的API。網絡的socket數據傳輸是一種特別的I/O,socket也是一種文檔描述符。利用socket()函數打開,返回一個整型的socket描述符,而後創建鏈接,數據傳輸等等。經常使用的socket類型有:流式socket(SOCK_STREAM)、數據報socket(SOCK_DGRAM). 其中流式socket是採用面向鏈接的TCP服務,而數據報socket則是無鏈接的UDP服務
服務器


  • Socket創建

    調用: int socket(int domain, int type, int prococol)來建立socket網絡

    domain:指明所使用的協議族,經常使用PF_INET, 表示互聯網協議族(TCP/IP)數據結構

       說明:dom

                        在綁定本地地址或鏈接遠程地址時須要初始化sockaddr_in結構,其中指定address family時通常設置爲AF_INET,即便用IP。socket

                        相關頭文件中的定義:AF = Address Family
                    PF = Protocol Family
                    AF_INET = PF_INET
tcp

            所以,通常規範的用法是在socket中用PF_INET指定協議族,在設置address中時,使用AF_NET,固然二者是同樣的。函數

    type:指定socket的類型,分爲:SOCK_STREAM(TCP)、SOCK_DGRAM(UDP),SOCK_RAW(原始socket,容許socket調用底層協議)
ui

    protocol:一般賦值爲0
spa

    socket描述符是一個指向內部數據結構的指針,執行描述符表的入口
指針

    兩個網絡程式之間的一個網絡鏈接包括:通訊協議、本地協議地址、本地主機端口、遠端主機地址、遠端協議端口

    

  • socket配置

    面向鏈接的socket客戶端經過調用connet函數在socket數據接口中保存本地和遠端信息,無鏈接socket的客戶端和服務端聯通面向鏈接socket的服務端經過調用bind函數來配置本地信息

    1. int bind(int sockefd, struct sockaddr *my_addr, int addrlen);

    

struct sockaddr
{    
    unsigned short sa_family; // 地址族, AF_INET...
    char sa_data[14]; // 14字節的協議地址
}

說明,其中sa_family通常爲AF_INET,表明tcp/ip協議族,sa_data則包含該socket的IP地址和端口號

struct sockaddr_in
{
    short int sin_family; // 地址族
    unsigned short int sin_port; // 端口號
    struct in_addr sin_addr; // ip地址
    unsigned char sin_zero[8]; // 填充0以保證和struct sockaddr一樣大小
}
第二個結構體更經常使用,用bzero()或memset()函數將其該結構體置爲零
指向sockaddr_in的指針和指向sockaddr的指針可以相互轉換
my_addr.sin_port = 0; // 表示隨機獲取一個沒有被佔用的端口號
my_addr.sin_addr.s_addr = INADDR_ANY; // 自動填入本機IP地址
my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 客戶端的填充IP方式

    bind函數須要將sin_port和sin_addr轉換成爲網絡字節有限順序,而sin_addr則無需轉換

    internet上數據以高位優先 順序傳輸,故須要實現進行轉換

htonl(); 把32位值從主機字節序轉換爲網絡字節序
htons(); 把16位值從主機字節序轉換爲網絡字節序
ntohl(); 把32位值從網絡字節序轉換爲主機字節序
ntohs(); 把16位值從網絡字節序轉換爲主機字節序

    bind函數在成功調用後,返回0,出錯返回-1並將errno設置爲響應的錯誤號

  • 創建鏈接

    面向鏈接的客戶程式使用connet函數來配置socket並和遠端服務器創建一個TCP鏈接

    int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)

    sockfd是socket函數返回的socket描述符

    serv_addr: 包含遠端主機ip地址和端口號的指針   

    addrlen: 爲遠端地址結構的長度, sizeof(sockaddr)

    connect函數只用於面向鏈接的客戶端程式,無鏈接和麪向鏈接的服務器不須要,成功則返回0,失敗返回-1

    

    listen函數使socket處於被動的監聽模式,併爲該socket模式創建一個輸入數據隊列,將到達的服務請求保存在隊列中

    int listen(int sockfd,int backlog)

    sockfd:爲socket描述符

    backlog: 自定在請求隊列中容許的最大請求數

    

    accept函數讓服務器接受客戶的鏈接請求,在創建好輸入隊列後,服務器調用accept函數,而後睡眠並等待客戶端的鏈接喚醒

    int accept(int sockfd, void *addr, int *addrlen)

    sockfd:是被監聽的socked秒速富

    addr:一般是個指向sockaddr_in變量的指針,該變量用來存放提出鏈接請求服務的主機信息;

    addrlen:一般是一個指向值爲sizeof(struct sockaddr_in)的整型指針變量

    accept函數監控的 socket收到鏈接請求時,socket執行體將創建一個新的socket,執行體將這個新socket和請求鏈接進程的地址聯繫起來,收到服務請求的初始socket仍可以繼續在之前的 socket上監聽,同時可以在新的socket描述符上進行數據傳輸操做


  • 數據傳輸

   send和recv用於面向鏈接的socket上進行數據傳輸     

    int send(int sockfd, const void *msg, int len, int flags)

    sockfd: 是想用來傳輸數據的socket描述符

    msg: 指向要發送數據的指針

    len:以直接爲單位的數據長度

    flags:通常設置爲0

    返回實際上發送出的字節數,可能會少於但願發送的數據;在程序中應該將send發送的數據和len進行比較,若不匹配時,應該進行處理

char *msg = "hello world";
int len = strlen(msg), bytes_sent;

bytes_sent = send(sockfd, msg, len, 0);
if(bytes_sent != len)
{
    ....
}


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

    sockfd:接受數據的socket描述符

    buf:爲存放接受數據的緩衝區

    len:緩衝區的長度

    flags:通常也被設置爲0

    返回實際接受的數據字節數


面向無鏈接的數據socket以sendto()和recvfrom()來進行數據傳輸

    int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)

    to表示目地機的IP地址和端口號信息,而tolen常爲sizeof(to)

    int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from , int *fromlen);

    from是sockaddr結構的變量,保存源機的IP地址和端口號,fromlen指向sizeof (struct sockaddr)的變量


結束傳輸

    close(sockfd);


實例代碼:

server.c
/*
 * server.c
 *
 *  Created on: 2014-8-29
 *      Author: wzb
 */
 
#include "server.h"

#define temp_pathname "temp.d"
void server_process(int sockfd)
{
	char buffer[1024];
	int data_len = 0;
	FILE *f;
	if((f = fopen(temp_pathname, "w")) == NULL)
	{
		fprintf(stderr, "crete temp file %s error\n", temp_pathname);
		exit(1);
	}
	
	while((data_len = recv(sockfd, buffer, 1024, 0)) >0)
	{
		if(data_len < 1024)
			buffer[data_len] = '\0';
		fprintf(f, "%s", buffer);
	}
	fclose(f);
}

int main()
{
	int sockfd;
	int clientfd;
	uint16_t port = 10033;
	
	struct sockaddr_in server_addr, client_addr;
	bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr  = htonl(INADDR_ANY);
	server_addr.sin_port = htons(port);
	
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		fprintf(stderr, "open data stream socket failed!\n");
		exit(1);
	}
	
	if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
	{
		fprintf(stderr, "bind data socket failed!\n");
		exit(1);
	}
	
	if(listen(sockfd, SOMAXCONN) < 0)
	{
		fprintf(stderr, "listen data stream failed\n");
		exit(1);
	}
	
	while(1)
	{
		socklen_t len = sizeof(client_addr);
		clientfd = accept(sockfd, (struct sockaddr *)&client_addr, &len);
		if(clientfd < 0)
		{
			fprintf(stderr, "accept error\n");
			continue;
		}
		
		int pid = fork();
		if(pid == 0)
		{
			server_process(clientfd);
			exit(0);
		}
		close(clientfd);
	}
	return 0;	 
}


client.c
/*
 * client.c
 *
 *  Created on: 2014-8-29
 *      Author: wzb
 */
 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

#define filename "file.d"

int main()
{
	int sockfd;
	struct sockaddr_in server_addr;
	uint16_t port = 10033;
	
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		fprintf(stderr, "socket eerror\n");
		exit(1);
	}
	
	bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");;
	
	if(connect(sockfd, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr))<0)
	{
		fprintf(stderr, "connect eerror\n");
		return -1;
	}	
	
	FILE *f = NULL;
	if((f = fopen(filename, "r")) == NULL)
	{
		fprintf(stderr, "%s file error\n", filename);
		close(sockfd);
		exit(1);
	}
	
	char buf[LINE_MAX];
	while(fgets(buf, LINE_MAX, f))
	{
		send(sockfd, buf, strlen(buf), 0);
	}
	fclose(f);
	printf("-----------send over------------\n");
	close(sockfd);
	return 0;
}
相關文章
相關標籤/搜索