基于TCP/IP的socket编程

基于tcp/ip_socket编程代码

linux 中 socket 实现TCP/IP的通信机制的原理:
首先确定一个网络进程需通过“IP地址+端口号”,也称为 socket。

内核中提供的API:

  1. 指定接受或发送的特定数据格式
    int socket(int domain, int type, int protocol);
    参数1domain:指定IP地址的格式
    参数2type:指定传输层的协议
    参数3protocol:一般为0,指定当前格式为默认格式
    成功返回该socket对应的文件描述符,失败返回-1

  2. 绑定socket对应的网络进程的IP+端口号
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    参数1sockfd:对应的socket文件描述符
    参数2addr:网络进程的服务器地址描述符
    参数3addrlen:描述符大小

  3. 初始化网络进程的监听机制
    int listen(int sockfd, int backlog);
    参数1sockfd:对应的socket文件描述符
    参数2backlog:排队建立3次握手队列和刚刚建立3次握手队列的最大链接数和,
    可通过"cat /proc/sys/net/ipv4/tcp_max_syn_backlog"查看系统默认配置

  4. 阻塞等待客户端连接,进入监听模式
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    参数1sockfd:对应的socket文件描述符
    参数2addr:用于接收客户端地址描述符
    参数3addrlen:客户端地址的长度,防止溢出

  5. 客户端于服务器建立连接
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    参数1sockfd:对应的socket文件描述符
    参数2addr:网络进程的服务器地址描述符
    参数3addrlen:描述符大小

补充一个问题

为什么服务器需要调用Bind,而客户端不用呢?
答:服务器具有固定的端口号,IP地址可以设为本机下所支持的IP号即INADDR_ANY,客户端通过connect连接服务器时,服务器为客户端会自动分配一个未使用的端口号,并且客户端的IP也默认为客户端机上的本地IP,这里就要注意了,如果客户端调用bind绑定,则服务器则需要给客户端分配客户端绑定的端口号,但是该端口号又有可能在服务器端已被使用,导致连接失败。
所以,一般客户端不需要多此一举去bind,让服务器分配比较适合。

好了,下面开始看一段代码,了解简单实现的机制
实现功能:
客户端发送字符串数据给服务器,服务器将其转为大写,返回给客户端

可用命令netstat -apn|grep 8000查看连接情况

服务器代码:

//server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>

//服务器端口号
#define SERV_PORT 8000
//listen的最大队列数
#define MAX_LOG 2048
//读写缓冲区大小
#define MAX_LINE 128

void sys_err(char *ptr)
{
	perror(ptr);
	exit(1);
}

int main(void){
	int ser_fd,cli_fd,len;
	socklen_t cliaddr_len;
	char buf[MAX_LINE];
	char ipstr[128];
	int i = 0;
	struct sockaddr_in servaddr,cliaddr;
	//1.socket
	ser_fd = socket(AF_INET,SOCK_STREAM,0);
	//2.bind
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	if(bind(ser_fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
		sys_err("bind");
	}

	//3.listen
	if(listen(ser_fd, MAX_LOG)<0)
		sys_err("listen");

	//4.while accept
	while(1){
		cliaddr_len = sizeof(cliaddr);
		cli_fd = accept(ser_fd, (struct sockaddr *)&cliaddr, 
					&cliaddr_len);
		//5.do
		len = read(cli_fd, buf, MAX_LINE);
		for(i = 0;i<len; i++){
			buf[i] = toupper(buf[i]);//小写转大写API函数
		}
		write(cli_fd, buf, len);
		//6.close
		close(cli_fd);	
	}
	//7.end
	close(ser_fd);
	return 0;
}

客户端代码:

//client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>

#define SERV_POST 8000
#define MAXLINE 80

int main(int argc, char *argv[]){
	struct sockaddr_in servaddr;
	int confd,n;
	char buf[MAXLINE];
	char *str;
	if(argc < 2){
		printf("input ./client str\n");
		exit(1);
	}
	str = argv[1];
	//1.socket
	confd = socket(AF_INET, SOCK_STREAM, 0);
	//初始化服务器IP和端口号。即封装IP数据报
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_POST);
	inet_pton(AF_INET, "192.168.43.139", &servaddr.sin_addr.s_addr);
	//2.connect
	connect(confd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	//3.do
	write(confd, str, strlen(str));
	n = read(confd, buf, MAXLINE);
	write(1,buf,n);
	printf("\n");
	//4.end
	close(confd);
	return 0;
}

Makefile:

all:server client

server:server.c
	gcc $< -o $@
client:client.c
	gcc $< -o $@

.Phony:clean
clean:
	rm -f server
	rm -f client

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值