服务器端套接字分两种,一种是监听套接字,一个是已连接套接字。客户端只有已连接套接字。
监听套接字主要用来接受三次握手数据,一旦三次握手完成了,那么就将其放到已连接队列中,accpet()就可以从中返回一个连接,返回的这个连接被称之为已连接套接字,是用来和客户端进行通信的,并不能接受连接,为主动套接字。
因此一个服务器端有3个客户机连接时,服务器端就有3个不同的已连接套接字,1个监听套接字。
下面是点对点聊天程序的实现:
/*服务器端程序*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#define MYPORT 8887
#define QUEUE 20
#define BUFFER_SIZE 1024
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int do_service(int conn)
{
char recvbuf[BUFFER_SIZE]={0};
memset(recvbuf,0,sizeof(recvbuf));
int length = recv(conn,recvbuf,sizeof(recvbuf),0);
if(length == 0){//客户端关闭的情况
//signal(SIGUSR1,handler);
printf("client close by accient\n");
return 0;
}
if(length < 0){ //如果失败
ERR_EXIT("recv");
return 0;
}
if(strcmp(recvbuf,"exit\n")==0)
return 0;
else{
fputs(recvbuf,stdout);
return 1;
}
}
int main()
{
//定义客户端套接字
int sock_ser = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//最后一位可为0,自动
if(sock_ser <0)//如果失败
ERR_EXIT("socket");
//定义sockaddr_in
struct sockaddr_in addr_ser;
addr_ser.sin_family = AF_INET;
addr_ser.sin_port = htons(MYPORT);
addr_ser.sin_addr.s_addr = htonl(INADDR_ANY);
//开启地址重复利用
int on = 1;
int rel_set = setsockopt(sock_ser,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
if(rel_set < 0)//如果失败
ERR_EXIT("reuseaddr");
//bind 绑定
int bd = bind(sock_ser,(struct sockaddr*)&addr_ser,sizeof(addr_ser));
if(bd < 0)//如果失败
ERR_EXIT("bind");
//listen 监听
int ln = listen(sock_ser,SOMAXCONN);//listen函数将套接字从主动套接字变成被动套接字;主动套接字主要用来发起连接,而被动套接字用来接受连接
//第二个参数规定能够并发连接的最大数目 =未完成连接数目+ 已完成连接数目:
if(ln < 0) //如果错误
ERR_EXIT("listen");
/* 开始定义客户端套接字 */
struct sockaddr_in addr_cli;
socklen_t len = sizeof(addr_cli);
//使用进程处理多并发
pid_t pid;
while(1){
//accept 接收
int conn = accept(sock_ser,(struct sockaddr*)&addr_cli,&len);
printf("ip=%s,port=%d\n",inet_ntoa(addr_cli.sin_addr),ntohs(addr_cli.sin_port));//新增打印通信ip和端口
if(conn < 0) //如果失败
ERR_EXIT("accept");
//创建父进程--发送数据
pid = fork();
if(pid == -1)
ERR_EXIT("fork");
if(pid == 0)//创建成功
{
char sendbuf[BUFFER_SIZE]={0};
while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL){
send(conn,sendbuf,sizeof(sendbuf),0);
memset(sendbuf,0,sizeof(sendbuf));
}
printf("child close \n");
exit(EXIT_SUCCESS);
close(conn);
}
else
{
while(do_service(conn));
printf("parent close\n");
exit(EXIT_SUCCESS);
close(conn);
}
}
return 0;
}
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <signal.h>
#define MYPORT 8887
#define BUFFER_SIZE 1024
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void handler(int sig)
{
printf("recv a signal =%d\n",sig);
exit(EXIT_SUCCESS);
}
int main()
{
//定义客户端socket
int sock_cli = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sock_cli < 0 )//如果失败
ERR_EXIT("socket");
//定义sockaddr
struct sockaddr_in addr_cli;
addr_cli.sin_family = AF_INET;
addr_cli.sin_port = htons(MYPORT);
addr_cli.sin_addr.s_addr = inet_addr("127.0.0.1");
int conn = connect(sock_cli,(struct sockaddr*)&addr_cli,sizeof(addr_cli));
if(conn < 0)
ERR_EXIT("connect");
char recvbuf[BUFFER_SIZE]={0};
char sendbuf[BUFFER_SIZE]={0};
pid_t pid;
pid = fork();
if(pid == -1)
ERR_EXIT("fork");
if(pid == 0)//发送数据
{
while(1){
memset(recvbuf,0,sizeof(recvbuf));
int length = recv(sock_cli,recvbuf,sizeof(recvbuf),0);
if(length <0)
ERR_EXIT("recv");
else if(length == 0){
printf("peer close\n");
break;
}
fputs(recvbuf,stdout);
}
close(conn);
kill(getppid(),SIGUSR1);
}
else
{
while(fgets(sendbuf,sizeof(sendbuf),stdin)!=0){
send(sock_cli,sendbuf,sizeof(sendbuf),0);//发送
memset(sendbuf,0,sizeof(sendbuf));
}
}
close(conn);
return 0;
}