Linux套接字编程


套接字是两个进程序通信的一种方法,而且可以是两台不同的主机之间的进程通信。套接字通信类似向“文件”读写进行通信,而这个“文件”    

就是套接字,某个大神曾经说过:"Linux的哲学思想之一就是一切皆文件"。下图就是经典的客户端服务器端模型

一.sock的基本API函数

1.socket()

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
返回值:成功返回套接字描述符。
出错返回-1.


domain     协议族或协议域(domain)  取值:
AF_INET        IPv4协议                2
AF_INET6       IPv6协议                10
AF_LOCAL       Unix域协议              1
AF_ROUTE       路由器套接口            16
AF_KEY         密钥套接口              15

Type               指明套接口类型      取值

SOCK_STREAM          字节流套接口         1

SOCK_DGRAM           数据报套接口         2

SOCK_SEQPACKET       有序分组套接口       5

SOCK_RAW             原始套接口           3

SOCK_PACKET          数据链路套接口       10 (只有Linux支持)

 

Protocol              指定协议的类型,  取值:

IPPROTO_TCP               TCP传输协议         6

IPPROTO_UDP               UDP传输协议         7

IPPROTO_SCTP              SCTP传输协议       132

Protocol通常取0,让系统根据familytype的取值,选取合适的protocol的值.

 补充:返回的套接字描述符在Linux中本质是一个文件描述符(为了方便记作sockfd),大多数以文件描述符为参数的函数都可以接受使用套接字描述符


2.bind()绑定套接字

#include<sys/socket.h>

int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);


sockfd:socket函数的返回值

myaddr:指向已初始化的sockaddr_in指针,用(structsockaddr*)强制转换。

addrlen:套接口地址结构大小(sizeof(sockaddr)与sizeof(sockaddr_in)一样)

类型socklen_t:unsigned int

返回值:成功为0,出错为-1



(1)地址格式与初始化

 由于两台计算机通信时进程IP已无法区别两个进程,所以套接字通信用(ip:端口号)标识两个通信的进程。而Linux系统的IPV4(AF_INET)中,套接字地址结构用 

sockaddr_in表示,bind()任务就是绑定sockfd和初始化的sockaddr_in。

头文件<netinet/in.h>

structsockaddr_in

{

 unsigned short sin_family; //协议族(四个字节,不同的机型不一定是unsigned short

  unsigned shortsin_port;    //端口号(两个字节情况同上)

  unsignedintsin_addr.s_addr; //IP地址

  unsigned charsin_zero[8]; //填充字段

};

初始化的sockaddr_in

1)必须memset把它初始化为0,然后再填充内容。

2)sin_family:必须用htons(端口号)把端口号转化为网络字节序再赋值给sin_family。而且端口号必须大于1024,应为小于1024都给系统占用。

3)sin_addr.s_addr:客户端有多个网络接口(IP地址)时多使用XX.sin_addr.s_addr=htonl(INADDR_ANY)表示服务器可以在任意网络接口上接受客户连接。

 当服务器要求绑定指定接口,或客户端在初始化目的服务器的地址时,要用inet_pton指定IP地址。

             int inet_pton(intdomain,const char* str,  void *addr);

             参数

domain:AF_INET或AF_INET6与套接字 domain一致。
     str:IPv4或IPv6地址的字符串表达,即“xxx.xxx.xxx.xxx”
 addr:转换str指向的字符串,将网络字节序的二进制值,存储在addr指向的内存
    通常addr指向XX.sin_addr.s_addr.
4)sin_family必须与要绑定的套接字的domain一致。
  5)sin_zero[8]:不用设置(memset为0)
注意:有两种情况要初始化 sockaddr_in,一种是客户端或服务器端的bind()绑定套接字时中设置自己的sockaddr_in.另一种客户端的connect()中要连接目的服务器的sockaddr_in。
 客户端一般不需要bind(),应为不需要清楚地知道客户端的地址,只需要知道服务器的地址。在connect阶段会绑定默认的地址。

.listen()服务器特有)

<sys/socket.h>

int listen(int sockfd,int backlog);

返回值:成功返回0,出错返回-1

listen将套接口从主动态变成被动态即进入LISTEN状态,表示服务器愿意接收请求。

listen的第一个参数为sock()返回的套接字描述符,listen的第二个参数规定了内核应该为相应套接口排队的最大连接个数,Linux的上限为SOMAXCONN.


4.accept()(服务器特有)

#include<sys/socket.h>

int accept(int sockfd, struct sockaddr *cliaddr,socklen_t *addrlen);

成功:非负新的套接字描述符(记作new_sockfd),代表与所返回客户的TCP连接(sockfd用来监听套接口,new_sockfd为已连接套接口用来read/write或send /recv

出错:-1

当没有客户端连接请求时服务器将在这里阻塞,直到有客户端connect()。

 

sockfd:即socket函数的返回值,套接字

cliaddr:当accept函数返回时,内核将从已完成连接队列中,取出一个连接;并将该连接的客户端信息(),保存在cliaddr指向的结构体中

addrlen:调用accept前,*addrlen的值为cliaddr实际所指的套接口地址结构的长度;accept返回时,内核填充*addrlen,该值为确切地客户套接口地址结构长度

当不需要利用客户端的地址,可以将cliaddraddrlen设置为NULL


5.connect()(客户端特有)

int connect(int sockfd, const struct sockaddr*servaddr, socklen_t addrlen);

sockfd:socket()申请到的套接字描述符。

servaddr:服务器的地址信息(这说明了要在客户端程序里初始化一次目的服务器sockaddr_in,但不需要在客户端中再bind()一次)

addrlen:服务器套接口地址结构的长度,也就是sizeof(sockaddr)或sizeof(sockaddr_in

返回值:成功返回0,出错返回-1


6.收发数据

当客户端connect()且服务器accept()后,两者建立连接,然后利用数据收发函数通信Write()、read()、send()、recv()、recvfrom/sendto()(与send()一样,但可用于 无连接通信)进行交换数据。

×客户 端: write/read(client_sockfd,….)表示向服务器“发/收”数据。

×服务器端:write/read(new_server_sockfd,….)表示向客户端“发/收”数据。这里的new_server_sockfd是服务器accept后返回的新套接字描述符。

7.close()

  如果服务器accept()成功,要close()两次套接字,分别为new_server_sockfd与server_sockfd。

#include<iostream>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
/*
TCPclient创建一个客户端对象,通过接受连接的目的服务器的端口号,目的服务器IP地址两个参数来构造一个
基于字节流的TCP客户端对象
*/
class TCPclient
{
 public:
	TCPclient(uint16_t port,const char* IP)
	{
		c_port=port;
		c_IP=new char[strlen(IP)+1];
		memcpy(c_IP,IP,strlen(IP)+1);
	}	
	~TCPclient()
	{
		delete []c_IP;
	}

	bool TCPwork()
	{
		struct sockaddr_in  server_addr;
	
		int client_socket=socket(AF_INET,SOCK_STREAM,0);
		if(client_socket==-1)
		{
			std::cout<<"creat socket fail!"<<std::endl;
			return 0;
    	}

		memset(&server_addr1,0,sizeof(sockaddr_in));//初始化目的服务器的struct sockaddr_in结构
		server_addr1.sin_family=AF_INET;
		server_addr1.sin_port=htons(c_port);
		int i=inet_pton(AF_INET,c_IP,&server_addr.sin_addr);
		if(i==-1)
		{
			std::cout<<"make serversock fail!"<<std::endl;
			return 0;
    	}


		int j=connect(client_socket,(sockaddr *)&server_addr1,(socklen_t)sizeof(sockaddr));
		if(j==-1)
		{
			std::cout<<"connect socket fail!"<<std::endl;
			return 0;
		}
		client_funtion(client_socket);
		close(client_socket);
		return 1;
	}
 private:
	virtual void client_funtion(int sockfd)//实现某种数据交换功能的虚函数
	{
		char buf[20];
		read(sockfd,buf,sizeof(buf));
		std::cout<<buf<<std::endl;
	}
 private:
	uint16_t c_port;
	char *c_IP;
};

main()
{
	CTPclient client1(2000,"192.168.178.20");
	client1.TCPwork();
}

#include<iostream>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
/*
TCPserver是一个基于TCP 字节流的服务器对象
*/

class TCPserver
{
 public:
	TCPserver(int listenquene=SOMAXCONN, uint16_t port=2000,char* IP=NULL)//接收监听队列最大值,服务器端口,服务器IP地址三个参数来构造对象
	{
		C_listenquene=listenquene;
		C_port=port;
		if(IP==NULL)
		{
			C_IP=IP;
		}
		else
		{
			C_IP=new char[strlen(IP)+1];
			memcpy(C_IP,IP,strlen(IP)+1);
		}
	}
	~TCPserver()
	{
		if(C_IP!=NULL)
			delete [] C_IP;
	}
	bool TCPserver_work()
	{
		struct sockaddr_in  s_addr1, s_addr2;
	
		int server_socket=socket(AF_INET,SOCK_STREAM,0);
		if(server_socket==-1)
		{
		std::cout<<"creat socket fail!"<<std::endl;
		return 0;
    	}

	    memset(&s_addr1,0,sizeof(s_addr1));
	    s_addr1.sin_family=AF_INET;
	    s_addr1.sin_port=htons(C_port);
		if(C_IP==NULL)//当C-ip为空值时,服务器IP地址接收为设置为INADDR_ANY。可以接受连接到服务器所在主机所有网卡接口的客户端连接请求。
		{
	    	s_addr1.sin_addr.s_addr=htonl(INADDR_ANY);
		}	
		else、//设置为固定某个网卡接口,
		{
			int i=inet_pton(AF_INET,C_IP,(void*)(&s_addr1.sin_addr.s_addr));
			if(i!=1)
			{
				std::cout<<"inet_pton  fail!"<<std::endl;
				return 0;
			}
		}
		
		int j=bind(server_socket,(sockaddr*)&s_addr1,sizeof(sockaddr));
		if(j==-1)
		{
		std::cout<<"bind socket fail!"<<std::endl;
		return 0;
		}	

		int i=listen(server_socket,C_listenquene);
		if(i==-1)
		{
			std::cout<<"listen socket fail!"<<std::endl;
			return 0;
    	}
		socklen_t lenght_addr= sizeof(sockaddr);//注意accept最后一个参数
		int new_socket=accept(server_socket,(sockaddr*)&s_addr2,&lenght_addr);
		if(new_socket==-1)
		{
		std::cout<<"accep socket fail!"<<std::endl;
		return 0;
    	}

		server_funtion(new_socket);
·		//std::cout<<"write:hellow world!"<<std::endl;
		close(new_socket);
		close(server_socket);
		return 1;
	}
 private:
	virtual void server_funtion(int sockfd)
	{
	
	}
 private:
	int C_listenquene;
	uint16_t C_port;
	char* C_IP;
};

class myTCPserver :public TCPserver
{
 public:
	myTCPserver(int listenquene=SOMAXCONN,uint16_t port=2000,char* IP=NULL):TCPserver(listenquene,port,IP)
	{
	}
	~myTCPserver()
	{
	}
 private:
	void server_funtion(int sockfd)
	{
		write(sockfd,"========hellow Mir.li!",23);
		std::cout<<"write:hellow Mir.li!"<<std::endl;
	}
	
};

main()
{
	myTCPserver server1;
    server1.TCPserver_work();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值