经过几天高强度的学习,对套接字的编程有了初步的认识,今天对这几天所学的知识总结一下;首先简单阐述一下tcp通信;

  TCP提供的是可靠的,顺序的,以及不会重复的数据传输,处理流控制,由于TCP是可靠的,连接的,顺序的,所以TCP一般用于都应用于对传输的完整性,正确性要求严的场合;编写基于tcp的服务器-客户端模型的程序简单流程如下:

  服务器端:

  (1)调用socket()创建一个套接口

  (2)调用bind()函数是服务器进程与一个端口绑定

  (3)调用listen()设置客户接入队列的大小

  (4)调用accept()接受一个连接,如果介入的队列不为空,则返回一个已连接的套接口描述符,

  (5)调用sned()和recv()函数用来在已连接的套接口间进行发送和接收数据

  客户端:

  (1)调用socket()创建套接字

  (2)调用connect()函数向服务器发送连接请求;

  (3)调用send()函数和recv()函数

  下面是服务器端的代码;

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h> //server
int main()
{
int fd = ;
int nfd = ;
int ret = ;
unsigned char data[] = {}; fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd < ) {
perror("socket");
return ;
}
/*服务端信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons();
ser.sin_addr.s_addr = htonl(0xc0a8010a);//192.168.1.10
struct sockaddr_in clt;
int len = ; /*绑定*/
ret = bind(fd, (struct sockaddr *)&ser, );
if(ret < ) {
perror("bind");
return ;
} /*监听*/
ret = listen(fd, );
if(ret == -) {
perror("listen");
return ;
} /*接收连接,并且返回一个新的套接字描述符nfd*/
nfd = accept(fd, (struct sockaddr *)&clt, &len);
if(ret < ) {
perror("accept");
return ;
} /*接收*/
ret = recv(nfd, data, , );
if(ret < ) {
perror("recv");
return ;
}
printf("clt said: %s\n", data);
close(fd); return ;
}

服务器端

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h> //client
int main()
{
int sock_fd = ;
int ret = ;
unsigned char *buf = "hello, hao ara you"; /*创建一个套接口*/
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < ) {
perror("socket");
return ;
} /*服务端信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons();
ser.sin_addr.s_addr = inet_addr("192.168.1.10"); //建立链接
ret = connect(sock_fd, (struct sockaddr *)&ser, );
if(ret < ) {
perror("connect");
return ;
} /*发送*/
ret = send(sock_fd, buf, strlen(buf), );
if(ret < ) {
perror("send");
return ;
} close(sock_fd);
return ;
}

客户端

上面程序是基于tcp的简单通信,下面我们利用tcp实现一个服务器多个客户机;要实现一对多,就要使用线程编程,服务器端在不断监听中,如果有连接请求的话,就用通过 accept函数接受并创建一个线程来处理。线程的创建函数为int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);

  下面是这个程序的源码

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h> #define PORT 9527 void *function(void *d);//线程要执行的函数 int main()
{
/*创建套接口*/
pthread_t pid= ;
int nfd = ;
int fd = ;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd < ) {
perror("sock");
return ;
}
/*服务器信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(PORT);
ser.sin_addr.s_addr = inet_addr("192.168.1.10");
struct sockaddr_in clt;
int len = ; /*绑定*/
int ret = bind(fd, (struct sockaddr *)&ser, );
if(ret == -) {
perror("bind");
return ;
} /*监听*/
ret = listen(fd, );
if(ret < ) {
perror("listen");
return ;
} while() {
/*接受链接*/
nfd = accept(fd, (struct sockaddr *)&clt, &len);
if(nfd < ) {
perror("accept");
return ;
} /*创建一个线程*/
ret = pthread_create(&pid, NULL, function, (void *)nfd);
if(ret != ) {
perror("pthread_create");
return ;
} pthread_join(pid, NULL); close(nfd);
} close(fd);
return ;
} void *function(void *d)
{
unsigned char buf[] = {};
int nfd = (int )d;
int ret = ; memset(buf, , );
ret = recv(nfd, buf, , );
if(ret < ) {
perror("recv");
return NULL;
}
printf("client said: %s\n", buf); ret = send(nfd, "recv ok", , );
if(ret < ) {
perror("send");
return NULL;
} }

server

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h> #define PORT 9527 int main(int argc, char **argv)
{
if(argc != ) {
printf("using %s <ip address> <message>\n", argv[]);
return ;
}
/*创建套接口*/
int fd = ;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd < ) {
perror("socket");
return ;
} /*服务器信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(PORT);
ser.sin_addr.s_addr = inet_addr(argv[]); /*创建链接*/
int ret = connect(fd, (struct sockaddr *)&ser, );
if(ret < ) {
perror("connect");
return ;
} /*访问*/
ret = send(fd, argv[], strlen(argv[]), );
if(ret < ) {
perror("send");
return ;
} char buf[] = {};
ret = recv(fd, buf, , );
if(ret < ) {
perror("recv");
return ;
}
printf("server: %s\n", buf);
close(fd); return ;
}

client

  上面代码需要注意的是,监听程序最大允许接受10个连接请求,如果这十个一直连接不断开的话,后续的连接请求就无法得到处理,所以我们需要在每次请求完毕之后就关闭nfd;下次请求再重新连接;

  第三个程序我们实现基于tcp的聊天程序:

 #include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h> int main()
{
/*创建套接口*/
int fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(fd < ){
perror("socket");
exit(EXIT_FAILURE);
} /*服务端信息*/
struct sockaddr_in srv;
srv.sin_family = AF_INET;
srv.sin_port=htons();
srv.sin_addr.s_addr = htonl(INADDR_ANY); /*绑定*/
int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));
if(ret < ){
perror("bind");
exit(EXIT_FAILURE);
}
ret = listen(fd,);
if(ret < ){
perror("bind");
exit(EXIT_FAILURE);
}
struct sockaddr_in snd;
int snd_len = ; /*接受*/
int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len);
if(nfd < ){
perror("accpet");
exit(EXIT_FAILURE);
} char data[] = {};
char revdata[] = {};
/*聊天*/
while(){
memset(revdata, , );
memset(data, , );
ret = recv(nfd,revdata,,);
if(ret < ){
perror("recv");
exit(EXIT_FAILURE);
} printf("client say: %s\n",revdata);
if(strcmp(revdata, "end") == ) {
break;
} ret = read(,data,);
if(ret < ){
perror("read");
exit(EXIT_FAILURE);
}
ret = send(nfd,data,,);
if(ret < ){
perror("recv");
exit(EXIT_FAILURE);
} }
close(nfd);
close(fd);
return ;
}

server

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h> int main()
{
/*创建套接口*/
int sock_fd = ;
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < ) {
perror("socket");
return ;
}
/*服务端信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons();
ser.sin_addr.s_addr = inet_addr("192.168.1.10"); /*建立链接*/
int ret = connect(sock_fd, (struct sockaddr *)&ser, );
if(ret == -) {
perror("connect");
return ;
} /*聊天*/
unsigned char data[] = {};
unsigned char rec[] = {};
while() {
memset(data, , );
memset(rec, , );
int r_size = read(, data, );
if(r_size < ) {
perror("read");
return ;
} ret = send(sock_fd, data, strlen(data), );
if(ret < ) {
perror("send");
return ;
} ret = recv(sock_fd, rec, , );
if(ret < ) {
perror("recv");
return ;
}
printf("server said: %s\n", rec);
}
close(sock_fd);
return ;
}

client

上面这个代码存在的缺陷是,发送方跟接收只能发送一句接收一句,不能一次性发送多句,要解决这个问题就要需用到IO多路服用,可以通过这个函数来实现:
  int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

下面贴出代码:

 #include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h> int main()
{
/*创建套接口*/
int fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(fd < ){
perror("socket");
exit(EXIT_FAILURE);
} /*服务端信息*/
struct sockaddr_in srv;
srv.sin_family = AF_INET;
srv.sin_port=htons();
srv.sin_addr.s_addr = htonl(INADDR_ANY); /*绑定*/
int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));
if(ret < ){
perror("bind");
exit(EXIT_FAILURE);
}
ret = listen(fd,);
if(ret < ){
perror("bind");
exit(EXIT_FAILURE);
}
struct sockaddr_in snd;
int snd_len = ; /*接受*/
int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len);
if(nfd < ){
perror("accpet");
exit(EXIT_FAILURE);
} fd_set rfds;
char data[] = {};
char revdata[] = {};
/*聊天*/
while(){ FD_ZERO(&rfds);
FD_SET(,&rfds);
FD_SET(nfd,&rfds);
ret = select(nfd+,&rfds,NULL,NULL,NULL);
if(FD_ISSET(nfd, &rfds)){
ret = recv(nfd,revdata,,);
if(ret < ){
perror("recv");
exit(EXIT_FAILURE);
} printf("client say: %s\n",revdata);
if(strcmp(revdata, "end") == ) {
break;
}
}
if(FD_ISSET(, &rfds)) {
ret = read(,data,);
if(ret < ){
perror("read");
exit(EXIT_FAILURE);
}
ret = send(nfd,data,,);
if(ret < ){
perror("recv");
exit(EXIT_FAILURE);
}
}
memset(revdata, , );
memset(data, , );
}
close(nfd);
close(fd);
return ;
}

server

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h> int main()
{
/*创建套接口*/
int sock_fd = ;
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < ) {
perror("socket");
return ;
}
/*服务端信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons();
ser.sin_addr.s_addr = inet_addr("192.168.1.10"); /*建立链接*/
int ret = connect(sock_fd, (struct sockaddr *)&ser, );
if(ret == -) {
perror("connect");
return ;
} /*聊天*/
fd_set rfds;
unsigned char data[] = {};
unsigned char rec[] = {};
while() {
memset(data, , );
memset(rec, , );
FD_ZERO(&rfds); //清空
FD_SET(,&rfds);//(标准输入)
FD_SET(sock_fd,&rfds);//添加监听描述符(套接字)
/*多路复用IO*/
ret = select(sock_fd+,&rfds,NULL,NULL,NULL); if(FD_ISSET(, &rfds)){//监听键盘是否有输入,执行接收
int r_size = read(, data, );
if(r_size < ) {
perror("read");
return ;
}
ret = send(sock_fd, data, strlen(data), );
if(ret < ) {
perror("send");
return ;
}
} if(FD_ISSET(sock_fd, &rfds)) {//监听sock_fd是否有输入,执行接收
ret = recv(sock_fd, rec, , );
if(ret < ) {
perror("recv");
return ;
}
printf("server said: %s\n", rec);
}
}
close(sock_fd);
return ;
}

client

    

  

网络编程之套接字(tcp)的更多相关文章

  1. TCP/IP网络编程之套接字类型与协议设置

    套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...

  2. 【TCP/IP网络编程】:01理解网络编程和套接字

    1.网络编程和套接字 网络编程与C语言中的printf函数和scanf函数以及文件的输入输出类似,本质上也是一种基于I/O的编程方法.之所以这么说,是因为网络编程大多是基于套接字(socket,网络数 ...

  3. TCP/IP网络编程之网络编程和套接字

    网络编程和套接字 网络编程又称为套接字编程,就是编写一段程序,使得两台连网的计算机彼此之间可以交换数据.那么,这两台计算机用什么传输数据呢?首先,需要物理连接,将一台台独立的计算机通过物理线路连接在一 ...

  4. UNIX网络编程——原始套接字(dos攻击)

    原始套接字(SOCK_RAW).应用原始套接字,我们可以编写出由TCP和UDP套接字不能够实现的功能. 注意原始套接字只能够由有 root权限的人创建. 可以参考前面的博客<<UNIX网络 ...

  5. UNIX网络编程——原始套接字的魔力【续】

    如何从链路层直接发送数据帧 上一篇里面提到的是从链路层"收发"数据,该篇是从链路层发送数据帧. 上一节我们主要研究了如何从链路层直接接收数据帧,可以通过bind函数来将原始套接字绑 ...

  6. Linux网络编程——原始套接字实例:MAC 头部报文分析

    通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...

  7. 19 网络编程--Socket 套接字方法

    1.Socket(也称套接字)介绍 socket这个东东干的事情,就是帮你把tcp/ip协议层的各种数据封装啦.数据发送.接收等通过代码已经给你封装好了 ,你只需要调用几行代码,就可以给别的机器发消息 ...

  8. 网络编程--Socket(套接字)

    网络编程 网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯.网络编程中 有两个主要的问题,一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后 如何可靠高效的进行数据传输.在 ...

  9. 网络编程之套接字socket

    目录 socket套接字 引子 为何学习socket一定要先学习互联网协议 socket是什么 套接字类型 基于文件类型的套接字家族 基于网络类型的套接字家族 套接字工作流程 基于TCP的套接字 简单 ...

  10. 8.7 day28 网络编程 socket套接字 半连接池 通信循环 粘包问题 struct模块

    前置知识:不同计算机程序之间的数据传输 应用程序中的数据都是从程序所在计算机内存中读取的. 内存中的数据是从硬盘读取或者网络传输过来的 不同计算机程序数据传输需要经过七层协议物理连接介质才能到达目标程 ...

随机推荐

  1. 64位ubuntu14.04配置adb后提示没有那个文件或目录

    1.配置完adb环境变量后在终端输入adb: ameyume@ameyume-HP-450-Notebook-PC:~$ adb /home/ameyume/adt-bundle-linux-x86_ ...

  2. 标准会话对象——StandardSession

    Tomcat使用了一个StandardSession对象用来表示标准的会话结构,用来封装需要存储的状态信息.标准会话对象StandardSession实现了Session.Serializable.H ...

  3. TCP/IP概述

    1) 链路层,有时也称作数据链路层或网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡.它们一起处理与电缆(或其他任何传输媒介)的物理接口细节. 2) 网络层,有时也称作互联网层, ...

  4. ExtJS:文件上传实例

    ExtJS:文件上传实例 var ext_dateFormat = 'Y-m-d H:i:s'; var dateFormat = 'yyyy-MM-dd HH:mm:ss'; var date = ...

  5. SpriteBuilder中频繁的切换场景层的解决办法

    注意,不像SettingsLayer,CCScrollView实例并没有从场景中删除和重新加载像代码所示的那样. 你只是简单的改变其可视(visible)状态on和off. 改变可视状态比加载CCB或 ...

  6. 【59】Quartz+Spring框架详解

    什么是Quartz Quartz是一个作业调度系统(a job scheduling system),Quartz不但可以集成到其他的软件系统中,而且也可以独立运行的:在本文中"job sc ...

  7. Android开发技巧——自定义控件之使用style

    Android开发技巧--自定义控件之使用style 回顾 在上一篇<Android开发技巧--自定义控件之自定义属性>中,我讲到了如何定义属性以及在自定义控件中获取这些属性的值,也提到了 ...

  8. The 13th tip of DB Query Analyzer, powerful processing EXCEL file

    The 13thtip of DB Query Analyzer, powerful processing EXCEL file MA Genfeng (Guangdong UnitollServic ...

  9. MOOS学习笔记2——HelloWorld回调

    MOOS学习笔记2--HelloWorld回调 例程 #include "MOOS/libMOOS/Comms/MOOSAsyncCommClient.h" bool OnConn ...

  10. 跨JavaScript对象作用域调用setInterval方法

    跨JavaScript对象作用域调用setInterval方法: var id = window.setInterval(function() {foofunc.call(this);}, 200);