服务器、客户端简单交互程序

这是一个简单的TCP服务器/客户端的程序示例。客户端发送两个long型变量到服务器端,服务器端读取这两个long型变量并返回这两个变量的和给客户端。

这是服务器端的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#define MAXLINE 1024
#define LISTENQ 5

void str_echo(int connfd);
void sig_chld(int sign);
void err_sys(char *str);

int main(void)
{
	int listenfd, connfd;
	pid_t childpid;
	socklen_t clilen;
	struct sockaddr_in servaddr, cliaddr;

	if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		err_sys("socket error");

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(4321); //4321端口是自己填写的临时端口,只要和客户端上填写的服务器端口是该临时端口就可以了
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
		err_sys("bind error");
	if(listen(listenfd, LISTENQ) < 0)
		err_sys("listen error");

	signal(SIGCHLD, sig_chld);

	for( ; ; )
	{
		clilen = sizeof(cliaddr);
		if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0)
		{
			//if(errno == EINTR)	//如果在Linux下的vim编写的话可以把这三行注释到的代码给加上去
			//	continue;
			//else
				err_sys("accept error");
		}

		if((childpid = fork()) == 0)
		{
			printf("I am in child %d\n", getpid());
			close(listenfd);
			str_echo(connfd);
			close(connfd);
			exit(0);
		}
		close(connfd);
	}

	return 0;
}

/*
* the function to handle INT of SIGCHLD
*/
void sig_chld(int sign)
{
	pid_t pid;
	int stat;

	while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
		printf("child %d terminated\n", pid);

	return;
}

/*
* send the sum of two long numbers to client
*/
void str_echo(int sockfd)
{
	long arg1, arg2;
	ssize_t n;
	char line[MAXLINE];

	for( ; ; )
	{
		if((n = read(sockfd, line, MAXLINE)) == 0)
			return;
		if(sscanf(line, "%ld%ld", &arg1, &arg2) == 2)
			snprintf(line, sizeof(line), "%ld\n", arg1 + arg2);
		else
			snprintf(line, sizeof(line), "input error");
		n = strlen(line);
		if(write(sockfd, line, n) != n)
			err_sys("write error");
	}
}

void err_sys(char *str)
{
    perror(str);
    exit(1);
}

以下是客户端的示例代码

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

#define MAXLINE 1024

void str_cli(int sockfd);
void err_sys(char *str);

int main(int argc, char *argv[])
{
	int sockfd;
	struct sockaddr_in servaddr;

	if(argc != 2)
	{
		printf("usage: ./a.out <ip>\n");
		exit(1);
	}
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		err_sys("socket error");

	printf("sockfd is ok\n");
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(4321);
	if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0)
		err_sys("inet_pton error");

	if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
		err_sys("connect error");

	printf("connect is ok\n");
	str_cli(sockfd);

	close(sockfd);

	return 0;
}

/*
* send two long num to server host
*/
void str_cli(int sockfd)
{
	char sendline[MAXLINE], recvline[MAXLINE];

	while(fgets(sendline, MAXLINE, stdin) != NULL)
	{
		if(write(sockfd, sendline, strlen(sendline)) < 0)
            err_sys("write error");
		if(read(sockfd, recvline, MAXLINE) == 0)
			err_sys("read error");
		fputs(recvline, stdout);
		bzero(recvline, strlen(recvline));
	}
}

void err_sys(char *str)
{
    perror(str);
    exit(1);
}

基本套接字函数讲解

socket函数

#include <sys/socket.h>
int socket(int family, int type, int protocol);  //成功返回非负描述符,出错返回-1

socket函数指定期望的通信协议类型(使用IPv4的TCP、使用IPv6的UDP、Unix域字节流协议)和套接字字类型(字节流、数据报或原始套接字)

----socket函数的family常值 ---------------

family             说明

AF_INET       IPv4协议

AF_INET6     IPv4协议

AF_LOCAL   Unix协议域

AF_ROUTE   路由套接字

AF_KEY         秘钥套接字

----------------------------------------------------

----socket函数的type常值 ------------------

SOCK_STREAM           字节流套接字

SOCK_DGRAM            数据报套接字

SOCK_SEQPACKET  有序分组套接字

SOCK_RAW                 原始套接字

----------------------------------------------------

----socket函数的protocal常值 ------------

IPPROTO_CP        TCP传输协议

IPPROTO_UDP     UDP传输协议

IPPROTO_SCTP   SCTP传输协议

----------------------------------------------------

connect函数

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);  //成功返回0,出错为-1

TCP客户用connect函数来建立一个与TCP服务器连接,sockfd是由socket函数返回的套接字描述符,第二个、第三个参数分别是指向一个套接字地址结构的指针和该结构的大小,套接字结构必须含有服务器的IP地址和端口号。如果connect失败后,就必须close当前的套接字描述符并重新调用socket。

bind函数

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);  //成功返回0,出错-1

bind函数把一个本地协议地址赋予一个套接字,它只是把一个协议地址赋予一个套接字,至于协议地址的含义则取决于协议本身。第二个参数指向协议地址结构的指针,第三个参数是协议地址的长度,对于TCP,调用bind函数可以指定一个端口号,或指定一个IP地址,或两者都指定,也可以两者都不指定。

listen函数

#include <sys/socket.h>
int listen(int sockfd, int backlog);  //成功返回0,出错-1

socket创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的一个客户套接字。listen函数把一个未连接的套接字转换为一个被动套接字,指示内核应接受指向该套接字的连接请求,调用listen函数将导致套接字从CLOSEE状态转换到LISTEN状态。第二个参数规定了内核应为相应套接字排队的最大连接个数。

(1)、未完成连接队列:每一个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程。这些套接字处于SYN_RCVD状态。

(2)、已完成连接队列:每个完成TCP三路握手过程的客户对应其中一项,这些套接字处于ESTABLISHED状态。

------------------------------------------------------------------------------------------------------------------------------------------------------

TCP监听套接字维护的两个队列

accept函数

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);  //成功返回非负描述符,出错返回-1

如果accept成功,那么其返回值是由内核自动生成的一个全新套接字,代表与返回客户的TCP连接,函数的第一个参数为监听套接字,返回值为已连接套接字。

------------------------------------------------------------------------------------------------------------------------------------------------------

服务器、客户端程序流程图

------------------------------------------------------------------------------------------------------------------------------------------------------

TCP状态转换图

------------------------------------------------------------------------------------------------------------------------------------------------------

参考资料:

1、《UNIX网络编程 卷1 套接字联网API》 第四章-基本套接字编程

2、《计算机网络 - 谢希仁》

时间: 2024-08-01 18:01:29

服务器、客户端简单交互程序的相关文章

服务器客户端回射程序-自己设计包的结构

这次是个点对点,不过我自己设计包,包中包括发送的字符串的长度,和实际的字符串,使用结构体来表示. 客户端跟服务器在接收报文时,首先接收字符串的长度这一数值,然后将这一数值作为参数传入readn接收固定长度的字节数字符串. 看代码,首先是服务器端: 1 /*使用发送固定字节数报文的点对点聊天程序*/ 2 #include<stdio.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/sock

运用socket实现简单的服务器客户端交互

Socket解释: 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. Socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制,取后一种意思.通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信.在Internet上的主机一般运行了多个服务软件,同时提供几种服务.每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务.Socket正如其英文原意

c++下基于windows socket的单线程服务器客户端程序

今天自己用编写了一个简单的c++服务器客户端程序,注释较详细,在此做个笔记. windows下socket编程的主要流程可概括如下:初始化ws2_32.dll动态库-->创建套接字-->绑定地址信息-->服务器进行监听/客户端连接服务器-->数据交换-->关闭套接字对象. 服务器端: 1 #include <Winsock2.h> 2 #include <Ws2tcpip.h> 3 #include <iostream> 4 5 #prag

Linux网络编程:客户端/服务器的简单实现

一. Socket的基本知识 1. socket功能 Socket层次 Socket实质上提供了进程通信的端点,进程通信之前,双方必须首先各自创建一个端点,否则是没有办法建立联系并相互通信的. 每一个Socket都一个半相关描述: {协议, 本地地址, 本地端口} 完整的Socket的描述: {协议, 本地地址, 本地端口, 远程地址, 远程端口} 2. Socket工作流程 面向连接(TCP)的Socket工作流程 UDP的socket工作流程 l 服务器端 首先,服务器应用程序用系统调用so

android客户端和php服务简单交互

android客户端和php+mysql+apache搭建之间的简单交互,实现log信息存储. 实现原理就是android客户端发送请求,传给服务器log信息,服务器收到这些,连接数据库进行存储,并将存储后的状态返回给客户端. 服务器端: 先在mysql里面建一个testlog的数据库,里面有一个log_info表,记录了LogCategory,System,Executor,Action等信息. 在php的虚拟目录下新建一个php项目testlog,创建conn.php和log_deal.ph

Netty学习——基于netty实现简单的客户端聊天小程序

Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEven

Python学习笔记一:简单的交互程序

入门第一课:简单的用户交互程序 1 name = input("Name: ") #Python3.X中的input取代了Python2.X中的input_raw 2 age = int(input("Age: )) #此处若不加int语句,输入时计算机会识别为字符串,为了确保计算机识别为数字,用户也可以输入数字意外的字符.int = integer意为整数 3 job = input("Job: ") 4 salary = input("Sal

【windows socket+HTTP服务器客户端】

Windows Socket+HTTP服务器客户端 Winsock是 Windows下套接字标准.                 1.HTTP协议:          HTTP是基于客户端/服务器的请求,响应协议.        请求:由客户端向服务器发起,指定了要从服务器获取的资源.请求包含了协议首部,指明了客户端处理能力信息,如可以处理的文件类型,支持的语言,编码方式等.        响应:服务器收到客户端的请求后,解析这个请求,构造响应,并发送给客户端.响应同样包含了协议首部,指明了服

【windows socket+UDP服务器客户端】

Windows Socket+UDP服务器客户端 Winsock是 Windows下套接字标准.                    1.UDP socket编程:          UDP(用户数据报协议)是一个无连接,不可靠的数据传输,其特点是简单,快捷.相比与TCP,UDP不需要建立连接(不需connect.accept函数),数据发送接收之后,不需要终止连接.基于UDP的程序,避免了TCP运行的开销,在效率与速度上具有更好的表现.          UDP是无连接的,可能会有数据的丢失