LinuxTCP/IP网络编程

通用socket地址
#include<bits/socket.h>
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];//因为这是端口号和ip地址共用,不知道哪里是端口号,哪里是ip地址,所以一般不用这个结构体
};

一般使用专用结构体
struct sockaddr_in
{
sa_family sin_family;//地址族:AF_INET
u_int16_t sin_port;//端口号,要用网络字节序表示
struct in_addr sin_addr;//IPv4地址结构体

};

struct in_addr
{
u_int32_t s_addr;//IPv4地址,要用网络字节序表示
};

主机字节序和网络字节序之间的转换:
#include<netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);

IP地址转换函数:
#include <arpa/inet.h>
in_addr_t inet_addr(const char *strptr);
//用于将点分十进制字符串表示的IPV4地址转换为用网络字节序整数表示的IPv4地址。失败返回INADDR_NONE;
int inet_aton(const char *cp,struct in_addr* inp);//功能和inet_addr一样,但是将转换的结果存储在参数inp指向的地址结构中。成功返回1,失败返回0;
char* inet_ntoa(struct in_addr in);//将用网络字节序整数表示的IPV4地址转换为用点分十进制表示的IPV4地址。(不可重入);

创建socket

socket在linux上是一个文件描述符

#include<sys/types.h>
#include<sys/socket.h>

int socket(int domain, int type, int protocol);
domain参数表示系统使用哪个底层协议族。对于TCP/IP协议族而言,参数应该设置为PF_INET(IPV4)或PF_INET6(IPV6);而对于linux本地域协议族而言,应该为PF_UNIX
type参数表示服务类型:主要有SOCK_STREAM(流服务)和SOCK_UGRAM(数据报)服务。对于TCP/IP协议而言流服务就是TCP协议,数据报服务就是UDP协议。
protocol参数表示在前两个参数构成的协议下,在选择具体的协议。一般都设置为0;

命名socket

在创建socket时我们给定了它一个地址族,但是并没有指定使用那个具体的socket地址。就需要将一个socket矛socket地址绑定在一起,就是为socket命名。
在服务器端,通常需要命名,只有命名后客户端才能知道该如何连接它。客户端不需要命名socket,采用匿名的方式,计时用操作系统自动分配的socket地址。

#include<sys/types.h>
#include<sys/socket.h>

int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen );
bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指出了socket地址的长度。
bind成功返回0,失败返回-1;

监听socket

socket被命名之后,还不能马上接受客户连接,需要创建监听队列以存放待处理的客户连接
#include<sys/socket.h>
init listen (int sockfd, int backlog);
sockfd参数指定被监听的socket。backlog参数提示内核监听队列的最大长度。
成功返回0。失败返回-1;

接受连接

从监听队列中接受一个连接

#include<sys/types.h>
#include<sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd为监听的sockfd,addr为用来获取被接受连接的远端socket地址,socket地址长度为addrlen.
函数成功返回一个新连接的socket,该socket唯一的标识了被接受的这个连接。失败返回-1。

发起连接

服务器通过listen调用来被动接受连接,客户端需要通过系统调用来建立连接。
#include<sys/types.h>
#include<sys/socket.h>
int connect (int sockfd, const struct scokaddr *serev_addr , socklen_t addrlen);
sockfd参数由socket系统调用返回一个socket,serv_addr参数是服务器监听的socket地址,addrlen参数指定这个地址长度。
函数成功返回0,一旦连接成功,这个socket就唯一标识这个连接。失败返回-1。

关闭连接

关闭一个连接实际上就是关闭了一个对应的socket,可以通过关闭普通的文件描述符系统调用完成;
#include<unistd.h>
int close (int fd);
可以关闭一个连接,也可以关闭一个通信

读写数据

#include<sys/types.h>
#include<sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

recv读取sockfd上的数据,buf和len参数分别指定度缓冲区的位置和大小,flags参数的含义为数据收发提供额外的控制,一般设置为0;

实现代码:
服务器端:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>

#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/types.h>

int main()
{
   int listenfd=socket(PF_INET,SOCK_STREAM,0);//创建一个socket文件描述符
	assert(listenfd!=-1);	  

   struct sockaddr_in ser;
   memset(&ser,0,sizeof(ser));

   ser.sin_family=AF_INET;//设置协议为tcp协议族
   ser.sin_port=htons(6000);//将主机子节序转换为网络子节序
   ser.sin_addr.s_addr=inet_addr("127.0.0.1");//将点分十进制字符串转换为网络字节序整数
   
   int res=bind(listenfd, (struct sockaddr*)&ser,sizeof(ser));//命名socket
   assert(res!=-1);

   listen(listenfd,5);//监听socket
   
   while(1)
   {
	   struct sockaddr_in cli;
	   int len=sizeof(cli);
	   int c =accept(listenfd,(struct sockaddr*)&cli,&len);//接收连接,没有客户请求时阻塞
	   if(c==-1)
	   {
		   printf("link is error\n");
	   	   break;
	   }

	   while(1)
	   {
		   char data[128]={0};
		   int n=recv(c,data,127,0);//发送消息
		   if(n==-1)
		   {
			   printf("recv data error\n");
			   break;
		   }
		   
		   if(n==0)//断开连接时
		   {
			   printf("%s:%d unlink\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
			   break;
		   }

		   printf("%s: %d ::",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
		   printf("%s \n",data);
		   
		   send(c,"Ok",2,0);
	   }
		   close(c);//关闭连接
   }


   exit(0);

}

客户端:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>

int main()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建一个socket文件描述符
	assert(sockfd!=-1);
	
	struct sockaddr_in ser_addr;
	memset(&ser_addr,0,sizeof(ser_addr));

	ser_addr.sin_family=AF_INET;
	ser_addr.sin_port=htons(6000);
	ser_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
	int res=connect(sockfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));//创建一个连接

	while(1)
	{
		printf("please input\n");
		char buff[128]={0};
		fgets(buff,128,stdin);//从标准输入设备获取

		if(strncmp(buff, "bye",3)==0)
    	{
	    	break;
		}

		send(sockfd,buff,strlen(buff)-1,0);//发送消息

		char data[128]={0};
		int n=recv(sockfd,data,sizeof(data),0);//接受消息
	}
	close(res);

	exit(0);
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值