Linux下实现双方聊天的网络编程

本文详细介绍了如何在TCP协议下创建服务器端Socket,包括Socket函数的使用、IP与端口绑定、监听连接、接受客户端请求以及数据收发的过程。重点展示了bind(), listen(), accept()和数据传输API的实战应用。
摘要由CSDN通过智能技术生成

编程步骤参考图

TCP服务端
一、创建Socket套接字
1.头文件
#include <sys/types.h>
#include <sys/socket.h>
2.功能:Socket()为通信创建一个端点,并返回一个描述符。
3.函数原型
int socket(int domain, int type, int protocol);

参数
Domain 设置为AF_INET IPv4 因特网协议
Type 设置为SOCK_STREAM 使用TCP协议,提供有序的、可靠的、双向的、基于连接的字节流。
Protocal 设置为0,0选择type类型对应的默认协议
5.返回值
错误返回-1。

二、BindIP号端口号与相应描述字赋值函数
1.功能:用于绑定IP地址和端口号到socketfd
函数原型
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
2.参数
Sockfd是一个socket描述符
Addr
是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地质创建socket时的地址协议族的不同而不同。
Ipv4对应的是:
Struct sockaddr_in{
Sa_family sin_family; //协议族
in_port_t sin_port;//端口号
Struct in_addr sin_addr;//IP地址结构体
Unsigned char sin_zero[8];//填充,无实际意义,只是为跟sockaddr结构在内存中对齐,这样两者才能相互转换//
};

Addrlen 指定addr所指向的地址结构的大小(以字节为单位)。
3.返回值
成功返回0,失败返回-1

4.地址转换API
int inet_aton(const char* straddr,struct in_addr *addrp);
把字符串形式的“192.168.1.123”转为网络能识别的格式

char* inet_ntoa(struct in_addr inaddr);
把网络格式的ip地址转为字符串形式

5.字节序转换api
头文件
#include <netinet/in.h>
API
uint16_t htons(uint16_t host16bitvalue); //返回网络字节序的值
uint32_t htonl(uint32_t host32bitvalue); //返回网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue); //返回主机字节序的值
uint32_t ntohl(uint32_t net32bitvalue); //返回主机字节序的值

h代表host,n代表net,s代表short(两个字节),l代表long(4个字节),通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取。

6.字节序转换与地址转换代码
struct sockaddr_in s_addr;
Struct
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));//主机字节序和网络字节序之间的转换
inet_aton(argv[1],&s_addr.sin_addr);//把IP地址转为网络能识别的格式

三、监听设置函数Listen
1.功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排队的最大连接数
2.函数原型
int listen(int sockfd, int backlog);
参数
Sockfd
socket描述符
Backlog
指定在请求队列中允许的最大请求数 ,这里设置为10

四、接收链接Accept
1.功能:accept函数由TCP服务器调用 ,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。
2.函数原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数
Sockfd
socket描述符
Addr 用来返回已连接的客户端的协议地址
Addrlen 客户端地址长度
3.返回值
成功返回已连接的套接字描述符,失败返回-1

五、数据收发
1.头文件
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
2.函数说明:read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中。
3.返回值:返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据。若参数count 为0, 则read()不会有作用并返回0。

ssize_t write(int fd, const void *buf, size_t count);
函数说明:write()会把参数buf所指的内存写入count个字节到参数放到所指的文件内。write成功返回,只是buf中的数据被复制到了kernel中的TCP发送缓冲区
返回值:如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。

服务端代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>

int main(int argc,char **argv)
{
	int s_fd;
	int c_fd;
	int n_read;
	char readBuf[128] = {0};
//	char *msg = "I get your connect";
	char msg[128] = {0};
	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;
	if(argc != 3){
                printf("param error\n");
                exit(-1);
    }
	memset(&s_addr,0,sizeof(struct sockaddr_in));
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	//1.sockey
	s_fd = socket(AF_INET,SOCK_STREAM,0);
	if(s_fd == -1){
		perror("socket");
		exit(-1);
	}
		
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(atoi(argv[2]));	
	inet_aton(argv[1],&s_addr.sin_addr);
	
	//2.bind
	bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

	//3.listen
	listen(s_fd ,10);

	//4.accept
	int client = sizeof(struct sockaddr_in);

	while(1){
		c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&client);
		if(c_fd == -1){
			perror("accept");
		}	

		printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));

		if(fork() == 0){

			if(fork() == 0){
				while(1){
				//6.write
					memset(msg,0,sizeof(msg));
					printf("input data: ");
					gets(msg);
                    write(c_fd,msg,strlen(msg));
			}
		}
			while(1){
				//5.read
				memset(readBuf,0,sizeof(readBuf));
				n_read = read(c_fd,readBuf,128);
				if(n_read == -1 ){
					perror("read");
				}else{
					printf("get message:%d,from client:%s\n",n_read,readBuf);
				}	
			}
		}
	}

	//7.close
	//close(s_fd);
	
	return 0;
}

TCP客户端
六、创建socket套接字(略)

七、connect客户端连接主机
函数原型
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
1.功能:用于绑定之后的client(客户端)与服务器建立连接
2.参数
Sockfd
Socket描述符
Addr是服务器端的IP地址和端口号的地址结构指针
Addrlen
地址长度常被设置为sizeof(struct sockaddr)
3.返回值
成功返回0,失败返回-1.
八、数据收发(略)

客户端代码

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

int main(int argc,char **argv)
{
	int c_fd;
	int n_read;
	char readBuf[128] = {0};

//	char *msg = "message from client";
	char msg[128] = {0};
	struct sockaddr_in c_addr;
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	if(argc != 3){
                printf("param error\n");
                exit(-1);
        }
	//1.sockey
	c_fd = socket(AF_INET,SOCK_STREAM,0);
	if(c_fd == -1){
		perror("socket");
		exit(-1);
	}
	
	c_addr.sin_family = AF_INET;
	c_addr.sin_port = htons(atoi(argv[2]));	
	inet_aton(argv[1],&c_addr.sin_addr);
	
	//4.connect
	int c_net = connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr));
	if(c_net == 1){
		perror("connect");
		exit(-1);
	}
	
	while(1){
		if(fork() == 0){
			while(1){
				//5.send
				memset(msg,0,sizeof(msg));
				printf("input data: ");
				gets(msg);
				write(c_fd,msg,strlen(msg));
			}
		}
		while(1){
			//6.read
			memset(readBuf,0,sizeof(readBuf));
			n_read = read(c_fd,readBuf,128);
			if(n_read == -1){
				perror("read");
			}else{
				printf("get message form server:%d,%s\n",n_read,readBuf);
			}
		}
	}
	//7.close
	close(c_fd);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值