网络编程总结(C/S)

(一)TCP编程流程

服务器端编程流程
/*
第一步:创建socket int socket(int domain.int type,int protocol)
成功时返回一个socket 失败时返回-1并设置error
domain:告诉系统使用哪个底层协议族 PF_INET(ipv4) PF_INET6(ipv6)
type:指定服务类型 SOCK_STREAM(流服务) SOCK_UGRAM(数据报服务)
protocol:在前两个协议的结合下,在选择一个具体的协议,默认值为0,表示使用默认协议

第二步:命名socket int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen)
将socket与地址协议族中的某个socket地址绑定  成功时返回0,失败返回-1并设置error
常见的error   1.EACCES 被绑定的地址是受保护的地址,仅可以被超级用户访问;普通用户绑定0-1023的端口号时会返回EACCES错误
              2.EADDRINUSE 被绑定的地址正在被使用 比如将socket绑定到正处于time_wait状态的地址上

第三步:监听socket int listen(int sockfd,int backlog)
sockfd被指定要监听的socket backlog表示内核监听队列的最大长度 
                  Linux内核版本2.2之前是处于SYN_RECV和ESTABLISHED的socket的上限
                  2.2版本之后表示处于ESTABLISHED的socket的上限,
                  处于半连接状态的socket上限由/proc/sys/net/ipv4/tacp_max_syn_backlog内核参数设置
                  超过此上限,服务器将不再接受新的客户端连接并返回ECONNREFUSED错误信息
第四步:接受连接  int accept(int socket,struct sockaddr *addr,socklen_t *addrlen)
sockfd指的是经过listen系统调用的监听socket  addr参数用来指定被接受连接的socket地址 addrlen为其长度
accept成功时返回一个新的连接socket,该socket唯一标识了这个被接受的连接 失败时返回-1并设置error

第五步:recv ssize_t recv(int sockfd,void* buf,siez_t len ,int flags)
返回实际读取到的数据的长度
第六步:send  ssize_t send(int sockfd,const void* buf,size_t len,int flags)
返回实际写入的数据的长度
第七步:close int close(int fd)
fd的等待关闭连接的socket close系统调用并不是立即关闭 而是将fd的引用计数为0时才真正关闭连接
多进程程序中,一次fork系统调用默认父进程打开得socket的引用计数加1,所以要将父子进程中的socket都关闭才是真正断开连接

如果想立即终止连接 可以使用下列的函数 
int shutdown(int fd,int howto)
howto决定了关闭的行为
SHUT_RD 关闭socket读的一端 应用程序不能针对socket进行读操作 并且该socket接受缓冲区中的数据都被丢弃
SHUT_WR 关闭socket写的一端 在关闭之前将socket发送缓冲区的数据全部发送出去 应用程序不能针对socket进行写操作 这种情况下 经常处于半关闭状态
SHUT_RDWR 同时关闭socket上的读与写
*/

客户端编程流程
/*
第一步:创建socket int socket(int domain.int type,int protocol)
成功时返回一个socket 失败时返回-1并设置error
domain:告诉系统使用哪个底层协议族 PF_INET(ipv4) PF_INET6(ipv6)
type:指定服务类型 SOCK_STREAM(流服务) SOCK_UGRAM(数据报服务)
protocol:在前两个协议的结合下,在选择一个具体的协议,默认值为0,表示使用默认协议

第二步:命名socket int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen)
将socket与地址协议族中的某个socket地址绑定  成功时返回0,失败返回-1并设置error
常见的error   1.EACCES 被绑定的地址是受保护的地址,仅可以被超级用户访问;普通用户绑定0-1023的端口号时会返回EACCES错误
              2.EADDRINUSE 被绑定的地址正在被使用 比如将socket绑定到正处于time_wait状态的地址上

第三步:连接 int connect(int sockfd,const struct sockaddr* ser_addr,socklen_t addrlen)
sockfd是系统socket系统调用产生的socketfd ser_addr是服务器监听的socket地址 addrlen为此地址的长度
成功时返回0 一旦连接成功 sockfd就唯一标识了这个连接 
失败时返回-1并设置error
ECONNREFUSED 目标端口不存在 连接被拒绝
ETIMEDOUT 连接超时
与服务器端相同
第四步:send
第五步:recv
第六步:close
*/以下为代码示例

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





void main()
{
	int listen_fd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
	assert(listen_fd != -1);

	struct sockaddr_in ser,cli;//在绑定函数中需要的结构体,用来记录客户端的iip地址和端口号
	ser.sin_family = PF_INET;//地址族:TCP/IP
	ser.sin_port = htons(6000);//将客户端端口号转化为网络字节序
	ser.sin_addr.s_addr = inet_addr("127.0.0.1");//将客户端的ip地址转化为网络字节序;注意这里输入的ip地址是链接本机;

	int ret = bind(listen_fd,(struct sockaddr*)&ser,sizeof(ser));//命名套接字
	assert(ret != -1);

	listen(listen_fd,5);//监听套接字
	printf("listen finish\n");

	
	while(1)
	{
		int len = sizeof(cli);
                //连接套接字 通过c实现服务器与客户端之间的通信
		int c = accept(listen_fd,(struct sockaddr*)&cli,&len);
		assert(c != -1);
		char buff[128] = {0};
                //接受客户端消息
		int n = recv(fds[i],buff,128,0);
		if(n <= 0)
		{
			close(fds[i]);
		}
		printf("buff::%s\n",buff);
                //向客户端发消息
		send(fds[i],"0k",2,0);	
	}
}
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>



void main()
{
	int sock_fd = socket(AF_INET,SOCK_STREAM,0);
	assert(sock_fd != -1);

	struct sockaddr_in ser;
	ser.sin_family = PF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("127.0.0.1");

	int ret =  connect(sock_fd,(struct sockaddr*)&ser,sizeof(ser));
	assert(ret != -1);

	while(1)
	{
		char buff[128] = {0};
		printf("please input:");

		fgets(buff,128,stdin);

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

		send(sock_fd,buff,strlen(buff) - 1,0);//不将 \n发送给服务器
	
		memset(buff,0,128);
		recv(sock_fd,buff,127,0);
		printf("buff:: %s\n",buff);
	}
	close(sock_fd);

}

注意在网络中传输的数据的字节序都是大端模式,在PC端存储的数据都是小端模式,所以在网络通讯中必须将小端模式转为大端模式,所以在上述代码编程中用到了转换字节序的函数

1.转换端口号

#include <netinet/in.h>
unsigned short int htons(unsigned short int hostshort);

2.转换ip地址

#include <arpa/inet.h>
int_addr_t inet_addr(const char *strptr)

(二)UDP编程流程

服务器端编程流程

创建socket(socket)------->命名socket(bind)------>recvfrom()----->sendto()------>close()

客户端编程流程

创建socket(socket)------->命名socket(bind)------>连接服务器connect()---->recvfrom()----->sendto()------>close()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值