最简单的server/client程序

编写了一个最简单的server/client聊天工具练练手:

server.c 的作用是接受client的请求,并与client进行简单的数据通信,整体为一个阻塞式的网络聊天工具。

server.c:

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

const short port = 8080;
char *ip = "127.0.0.1";

void *pthread_run(void *arg)
{
	int fd = (int)arg;
	 
 	char buf[1024];
 	memset(buf, '\0', sizeof(buf));
 	
	while (1)
	{
	 	ssize_t sz = read(fd, buf, sizeof(buf) - 1);
	 	if (sz < 0)
	 	{
	 		perror("read");
	 		break;
	 	}
	 	else if (0 == sz)
	 	{
	 		printf("read done...\n");
	 		break;
	 	}
	 	else
	 	{
	 		buf[sz - 1] = '\0';
			printf("client# %s\n", buf);
			write(fd, buf, strlen(buf));
		}
	}
	return 0;
}

int main()
{
	//1.创建套接字
	int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
	if (listen_sock < 0)
	{
		perror("socket");
		return 1;
	}
	
	//2.绑定
	struct sockaddr_in local;
	local.sin_family = AF_INET;
	local.sin_port = htons(port);
	local.sin_addr.s_addr = inet_addr(ip);

	if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
	{
		perror("bind");
		return 2;
	}
	
	//3.监听
	if (listen(listen_sock, 5) < 0)
	{
		perror("listen");
		return 3;
	}
	

	while (1)
	{	
		//4.获取收到的套接字
		struct sockaddr_in peer;	//远端
		socklen_t len = sizeof(peer);	
		int fd = accept(listen_sock, (struct sockaddr *)&peer, &len);
		
		printf("new connect: %s:%d\n", inet_ntoa(peer.sin_addr), peer.sin_port);

		pthread_t id = pthread_create(&id, NULL, pthread_run, (void *)fd); //创建线程来服务客户
		pthread_detach(id);		
	}


	return 0;
}

client.c:

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

const short port = 8080;
char *ip = "127.0.0.1";

int main()
{
	//1.创建套接字
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0)
	{
		perror("socket");
		return 1;
	}
	
	//2.建立链接
	struct sockaddr_in remote;
	remote.sin_family = AF_INET;
	remote.sin_port = htons(port);
	remote.sin_addr.s_addr = inet_addr(ip);
	
	if (connect(fd, (struct sockaddr *)&remote, sizeof(remote)) < 0)
	{
		perror("connect");
		return 2;
	}

	char buf[1024];
	memset(buf, '\0', sizeof(buf));
	while (1)
	{
		printf("Please Enter# ");
		fflush(stdout);
		ssize_t _s = read(0, buf, sizeof(buf) - 1);
		if (_s > 0)
		{
			buf[_s] = '\0';
		}
		write(fd, buf, strlen(buf));
		ssize_t sz = read(fd, buf, sizeof(buf) - 1);
		if (sz < 0)
		{
			perror("read");
			return 3;
		}
		else if (sz == 0)
		{
			printf("read done...");
			break;
		}
		else
		{
			buf[sz] = '\0';
			printf("server echo# %s\n", buf);
		}
	}


	return 0;
}

socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。对 于IPv4,family参数指定为AF_INET。对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表示面向数据报的传输协 议。protocol参数的介绍从略,指定为0即可。

服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端 口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络地址和端口号。bind()成功返回0,失败返回-1。bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号。

典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept 的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略。

三方握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请 求,就阻塞等待直到有客户端连接上来。

client.c的作用是链接server,并向server发起通信请求。

由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端口号由内核自动分配。注意, 客户端不是不允许调用bind(),只是没有必要调用bind()固定一个端口号,服务器也不是必须调用bind(),但如果服务器不调用bind(),内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。

演示

先编译运行服务器:

         

查看监听状态:

         

运行客户端:

         

给server发送消息,立即接收到了server的回显消息:

             

client退出后:


         

图示:

 

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fireplusplus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值