【Linux网络编程实例】实例二:利用TCP协议进行客户端与服务器通信

本例子是在unp官方代码的基础上改动的,完成的功能是客户端向服务器发送消息,并且在服务器上显示出来,服务器给予客户端确认信息。虽然说功能很简单,但是在这个过程中遇到了一些问题,还是值得一提的。而且,这个基础代码对于以后深入学习TCP/IP这一块还是很重要的。

下面进入正题。首先,在启动服务器和客户端进行通信之后,关闭终端常用的方法就是直接关闭窗口,然后,再次使用这个程序的时候,奇怪的事情就发生了。

错误:bind error: Address already in use

绑定失败地址已经被占用了。就是说,如果你不把进程杀了,休想再次使用这个地址了。怎么解决?重启电脑不就行了。确实也对,重启电脑通常是解决电脑问题的最简单但却很有效的方法。但是对于程序员而言,往往是打开很多软件和文档工作的,重启电脑是很烦的事情。为了查看到底是怎么一回事,被哪个进程占用了,敲一下netstat -tanlp看看哪个进程没退掉后台了,然后把进程杀了。其实也挺麻烦的,每次运行完都得去杀进程。还有一种比较靠谱的方法是,在blind或者listen之前添加两行代码,然后要关闭服务器进程时,使用CTRL+C而不是直接关闭窗口就行了。

int on = 1;
int ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

下面对整个过程进行分析。先看整个例子的示意图。



在客户端等待输入,用的是Fgets函数,读取的数据保存在buf指向的字符数组中,每次最多读取bufsize-1个字符(第bufsize个字符赋'\0'),如果文件中的该行,不足bufsize个字符,则读完该行就结束。如若该行(包括最后一个换行符)的字符数超过bufsize-1,则fgets只返回一个不完整的行,但是,缓冲区总是以NULL字符结尾,对fgets的下一次调用会继续读该行。读取输入信息之后,把客户端输入的信息写入套接字sockfd。

//等待输入
if(Fgets(sendline,MAXLINE,stdin) != NULL)
//写到套接字上
Writen(sockfd,sendline,strlen(sendline));

服务器方面,一直在监听客户端,从套接字中读取信息之后,将其在服务器端显示出来,用到的是Fputs,fputs是一种函数,具有的功能是向指定的文件写入一个字符串(不自动写入字符串结束标记符‘\0’)。成功写入一个字符串后,文件的位置指针会自动后移,函数返回为一个非负整数;否则返回EOF(符号常量,其值为-1)。接受到客户端信息之后,应该给予客户端确信信息。"I have receive the message!"

//读取套接字
if((receive=read(sockfd,recvline,MAXLINE))>0)
//在服务器上显示
Fputs(recvline,stdout);
//给与确认信息
sendline="I have receive the message!";
Writen(sockfd,sendline,MAXLINE);

客户端方面,也应该读取服务器发来确认信息,完成整个过程的通信。

//接收服务器的确认信息
Readline(sockfd,recvline,MAXLINE);
Fputs(recvline,stdout);
Fputs("\n",stdout);

下面给出完整代码。

/***********************************************************/
// 程序目的:TCP客户/服务器通信(服务器端)
// 日期:    2014-11-15
// 作者:    spencer_chong
// 邮箱:    zhuangxb91@qq.com
/***********************************************************/
#include	"unp.h"
void message(int sockfd);
int
main(int argc, char **argv)
{
	int					listenfd, connfd;
	pid_t				childpid;
	socklen_t			clilen;
	struct sockaddr_in	cliaddr, servaddr;

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    //bind error: Address already in use
	int on = 1;
	int ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family      = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port        = htons(SERV_PORT);

	Fputs("Welcome!",stdout);
	Fputs("Waiting for connectting ......\n",stdout);

	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

	Listen(listenfd, LISTENQ);

	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
		Fputs("Successfully!\n",stdout);

		if ( (childpid = Fork()) == 0) {	/* child process */
			Close(listenfd);	/* close listening socket */
		message(connfd);

			exit(0);
		}
		Close(connfd);			/* parent closes connected socket */
	}
}
void message(int sockfd)
{
	ssize_t receive = 0;
	char recvline[MAXLINE];
	char *sendline;
	//读取套接字
	if((receive=read(sockfd,recvline,MAXLINE))>0)
		//在服务器上显示
		Fputs(recvline,stdout);
	//给与确认信息
	sendline="I have receive the message!";
	Writen(sockfd,sendline,MAXLINE);

}

/***********************************************************/
// 程序目的: TCP客户/服务器通信(客户端)
// 日期:    2014-11-15
// 作者:    spencer_chong
// 邮箱:    zhuangxb91@qq.com
/***********************************************************/
#include	"unp.h"
void message(int sockfd);
int
main(int argc, char **argv)
{
	int					sockfd;
	struct sockaddr_in	servaddr;

	if (argc != 2)
		err_quit("usage: tcpcli <IPaddress>");

	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

	Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
    for(;;)
    {
	message(sockfd);
	exit(0);
    }
}

void message(int sockfd)
{
	char sendline[MAXLINE],recvline[MAXLINE];
	//等待输入
	if(Fgets(sendline,MAXLINE,stdin) != NULL)
		//写到套接字上
		Writen(sockfd,sendline,strlen(sendline));
	//接收服务器的确认信息
	Readline(sockfd,recvline,MAXLINE);
	Fputs(recvline,stdout);
	Fputs("\n",stdout);
}


本文到此结束,欢迎留言纠错及讨论!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值